1
|
|
|
<?php |
|
|
|
|
2
|
|
|
defined('PH7') or exit('Restricted access'); |
3
|
|
|
if (!\PH7\Admin::auth()) exit('Restricted access'); // Accessible only for admins |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* elFinder - file manager for web. |
7
|
|
|
* Core class. |
8
|
|
|
* |
9
|
|
|
* @package elfinder |
10
|
|
|
* @author Dmitry (dio) Levashov |
11
|
|
|
* @author Troex Nevelin |
12
|
|
|
* @author Alexey Sukhotin |
13
|
|
|
**/ |
14
|
|
|
class elFinder |
15
|
|
|
{ |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* API version number |
19
|
|
|
* |
20
|
|
|
* @var string |
21
|
|
|
**/ |
22
|
|
|
protected $version = '2.1'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Storages (root dirs) |
26
|
|
|
* |
27
|
|
|
* @var array |
28
|
|
|
**/ |
29
|
|
|
protected $volumes = array(); |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Network mount drivers |
33
|
|
|
* |
34
|
|
|
* @var array |
35
|
|
|
*/ |
36
|
|
|
public static $netDrivers = array(); |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* elFinder global locale |
40
|
|
|
* |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
public static $locale = ''; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* elFinderVolumeDriver default mime.type file path |
47
|
|
|
* |
48
|
|
|
* @var string |
49
|
|
|
*/ |
50
|
|
|
public static $defaultMimefile = ''; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* elFinder session wrapper object |
54
|
|
|
* |
55
|
|
|
* @var elFinderSessionInterface |
56
|
|
|
*/ |
57
|
|
|
protected $session; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* elFinder global sessionCacheKey |
61
|
|
|
* |
62
|
|
|
* @deprecated |
63
|
|
|
* @var string |
64
|
|
|
*/ |
65
|
|
|
public static $sessionCacheKey = ''; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Is session closed |
69
|
|
|
* |
70
|
|
|
* @deprecated |
71
|
|
|
* @var bool |
72
|
|
|
*/ |
73
|
|
|
private static $sessionClosed = false; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* elFinder base64encodeSessionData |
77
|
|
|
* elFinder save session data as `UTF-8` |
78
|
|
|
* If the session storage mechanism of the system does not allow `UTF-8` |
79
|
|
|
* And it must be `true` option 'base64encodeSessionData' of elFinder |
80
|
|
|
* |
81
|
|
|
* @var bool |
82
|
|
|
*/ |
83
|
|
|
protected static $base64encodeSessionData = false; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* elFinder common tempraly path |
87
|
|
|
* |
88
|
|
|
* @var string |
89
|
|
|
**/ |
90
|
|
|
protected static $commonTempPath = ''; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Additional volume root options for network mounting volume |
94
|
|
|
* |
95
|
|
|
* @var array |
96
|
|
|
*/ |
97
|
|
|
protected $optionsNetVolumes = array(); |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Session key of net mount volumes |
101
|
|
|
* |
102
|
|
|
* @deprecated |
103
|
|
|
* @var string |
104
|
|
|
*/ |
105
|
|
|
protected $netVolumesSessionKey = ''; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Mounted volumes count |
109
|
|
|
* Required to create unique volume id |
110
|
|
|
* |
111
|
|
|
* @var int |
112
|
|
|
**/ |
113
|
|
|
public static $volumesCnt = 1; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Default root (storage) |
117
|
|
|
* |
118
|
|
|
* @var elFinderStorageDriver |
119
|
|
|
**/ |
120
|
|
|
protected $default = null; |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Commands and required arguments list |
124
|
|
|
* |
125
|
|
|
* @var array |
126
|
|
|
**/ |
127
|
|
|
protected $commands = array( |
128
|
|
|
'open' => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false, 'compare' => false), |
129
|
|
|
'ls' => array('target' => true, 'mimes' => false, 'intersect' => false), |
130
|
|
|
'tree' => array('target' => true), |
131
|
|
|
'parents' => array('target' => true), |
132
|
|
|
'tmb' => array('targets' => true), |
133
|
|
|
'file' => array('target' => true, 'download' => false), |
134
|
|
|
'zipdl' => array('targets' => true, 'download' => false), |
135
|
|
|
'size' => array('targets' => true), |
136
|
|
|
'mkdir' => array('target' => true, 'name' => false, 'dirs' => false), |
137
|
|
|
'mkfile' => array('target' => true, 'name' => true, 'mimes' => false), |
138
|
|
|
'rm' => array('targets' => true), |
139
|
|
|
'rename' => array('target' => true, 'name' => true, 'mimes' => false), |
140
|
|
|
'duplicate' => array('targets' => true, 'suffix' => false), |
141
|
|
|
'paste' => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false, 'renames' => false, 'hashes' => false, 'suffix' => false), |
142
|
|
|
'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false, 'chunk' => false, 'cid' => false, 'node' => false, 'renames' => false, 'hashes' => false, 'suffix' => false, 'mtime' => false), |
143
|
|
|
'get' => array('target' => true, 'conv' => false), |
144
|
|
|
'put' => array('target' => true, 'content' => '', 'mimes' => false), |
145
|
|
|
'archive' => array('targets' => true, 'type' => true, 'mimes' => false, 'name' => false), |
146
|
|
|
'extract' => array('target' => true, 'mimes' => false, 'makedir' => false), |
147
|
|
|
'search' => array('q' => true, 'mimes' => false, 'target' => false), |
148
|
|
|
'info' => array('targets' => true, 'compare' => false), |
149
|
|
|
'dim' => array('target' => true), |
150
|
|
|
'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false, 'quality' => false), |
151
|
|
|
'netmount' => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => false, 'pass' => false, 'alias' => false, 'options' => false), |
152
|
|
|
'url' => array('target' => true, 'options' => false), |
153
|
|
|
'callback' => array('node' => true, 'json' => false, 'bind' => false, 'done' => false), |
154
|
|
|
'chmod' => array('targets' => true, 'mode' => true) |
155
|
|
|
); |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Plugins instance |
159
|
|
|
* |
160
|
|
|
* @var array |
161
|
|
|
**/ |
162
|
|
|
protected $plugins = array(); |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Commands listeners |
166
|
|
|
* |
167
|
|
|
* @var array |
168
|
|
|
**/ |
169
|
|
|
protected $listeners = array(); |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* script work time for debug |
173
|
|
|
* |
174
|
|
|
* @var string |
175
|
|
|
**/ |
176
|
|
|
protected $time = 0; |
177
|
|
|
/** |
178
|
|
|
* Is elFinder init correctly? |
179
|
|
|
* |
180
|
|
|
* @var bool |
181
|
|
|
**/ |
182
|
|
|
protected $loaded = false; |
183
|
|
|
/** |
184
|
|
|
* Send debug to client? |
185
|
|
|
* |
186
|
|
|
* @var string |
187
|
|
|
**/ |
188
|
|
|
protected $debug = false; |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Call `session_write_close()` before exec command? |
192
|
|
|
* |
193
|
|
|
* @var bool |
194
|
|
|
*/ |
195
|
|
|
protected $sessionCloseEarlier = true; |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* SESSION use commands @see __construct() |
199
|
|
|
* |
200
|
|
|
* @var array |
201
|
|
|
*/ |
202
|
|
|
protected $sessionUseCmds = array(); |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* session expires timeout |
206
|
|
|
* |
207
|
|
|
* @var int |
208
|
|
|
**/ |
209
|
|
|
protected $timeout = 0; |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Temp dir path for Upload |
213
|
|
|
* |
214
|
|
|
* @var string |
215
|
|
|
*/ |
216
|
|
|
protected $uploadTempPath = ''; |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Max allowed archive files size (0 - no limit) |
220
|
|
|
* |
221
|
|
|
* @var integer |
222
|
|
|
*/ |
223
|
|
|
protected $maxArcFilesSize = 0; |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* undocumented class variable |
227
|
|
|
* |
228
|
|
|
* @var string |
229
|
|
|
**/ |
230
|
|
|
protected $uploadDebug = ''; |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Errors from PHP |
234
|
|
|
* |
235
|
|
|
* @var array |
236
|
|
|
**/ |
237
|
|
|
public static $phpErrors = array(); |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Errors from not mounted volumes |
241
|
|
|
* |
242
|
|
|
* @var array |
243
|
|
|
**/ |
244
|
|
|
public $mountErrors = array(); |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* URL for callback output window for CORS |
248
|
|
|
* redirect to this URL when callback output |
249
|
|
|
* |
250
|
|
|
* @var string URL |
251
|
|
|
*/ |
252
|
|
|
protected $callbackWindowURL = ''; |
253
|
|
|
|
254
|
|
|
// Errors messages |
255
|
|
|
const ERROR_UNKNOWN = 'errUnknown'; |
256
|
|
|
const ERROR_UNKNOWN_CMD = 'errUnknownCmd'; |
257
|
|
|
const ERROR_CONF = 'errConf'; |
258
|
|
|
const ERROR_CONF_NO_JSON = 'errJSON'; |
259
|
|
|
const ERROR_CONF_NO_VOL = 'errNoVolumes'; |
260
|
|
|
const ERROR_INV_PARAMS = 'errCmdParams'; |
261
|
|
|
const ERROR_OPEN = 'errOpen'; |
262
|
|
|
const ERROR_DIR_NOT_FOUND = 'errFolderNotFound'; |
263
|
|
|
const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.' |
264
|
|
|
const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.' |
265
|
|
|
const ERROR_NOT_DIR = 'errNotFolder'; |
266
|
|
|
const ERROR_NOT_FILE = 'errNotFile'; |
267
|
|
|
const ERROR_PERM_DENIED = 'errPerm'; |
268
|
|
|
const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.' |
269
|
|
|
const ERROR_EXISTS = 'errExists'; // 'File named "$1" already exists.' |
270
|
|
|
const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.' |
271
|
|
|
const ERROR_MKDIR = 'errMkdir'; |
272
|
|
|
const ERROR_MKFILE = 'errMkfile'; |
273
|
|
|
const ERROR_RENAME = 'errRename'; |
274
|
|
|
const ERROR_COPY = 'errCopy'; |
275
|
|
|
const ERROR_MOVE = 'errMove'; |
276
|
|
|
const ERROR_COPY_FROM = 'errCopyFrom'; |
277
|
|
|
const ERROR_COPY_TO = 'errCopyTo'; |
278
|
|
|
const ERROR_COPY_ITSELF = 'errCopyInItself'; |
279
|
|
|
const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".' |
280
|
|
|
const ERROR_RM = 'errRm'; // 'Unable to remove "$1".' |
281
|
|
|
const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)' |
282
|
|
|
const ERROR_MKOUTLINK = 'errMkOutLink'; // 'Unable to create a link to outside the volume root.' |
283
|
|
|
const ERROR_UPLOAD = 'errUpload'; // 'Upload error.' |
284
|
|
|
const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".' |
285
|
|
|
const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.' |
286
|
|
|
const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.' |
287
|
|
|
const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.' |
288
|
|
|
const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.' |
289
|
|
|
const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.' |
290
|
|
|
const ERROR_UPLOAD_TEMP = 'errUploadTemp'; // 'Unable to make temporary file for upload.' |
291
|
|
|
const ERROR_ACCESS_DENIED = 'errAccess'; |
292
|
|
|
const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type. |
293
|
|
|
const ERROR_SAVE = 'errSave'; |
294
|
|
|
const ERROR_EXTRACT = 'errExtract'; |
295
|
|
|
const ERROR_ARCHIVE = 'errArchive'; |
296
|
|
|
const ERROR_NOT_ARCHIVE = 'errNoArchive'; |
297
|
|
|
const ERROR_ARCHIVE_TYPE = 'errArcType'; |
298
|
|
|
const ERROR_ARC_SYMLINKS = 'errArcSymlinks'; |
299
|
|
|
const ERROR_ARC_MAXSIZE = 'errArcMaxSize'; |
300
|
|
|
const ERROR_RESIZE = 'errResize'; |
301
|
|
|
const ERROR_UNSUPPORT_TYPE = 'errUsupportType'; |
302
|
|
|
const ERROR_CONV_UTF8 = 'errConvUTF8'; |
303
|
|
|
const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content'; |
304
|
|
|
const ERROR_NETMOUNT = 'errNetMount'; |
305
|
|
|
const ERROR_NETUNMOUNT = 'errNetUnMount'; |
306
|
|
|
const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver'; |
307
|
|
|
const ERROR_NETMOUNT_FAILED = 'errNetMountFailed'; |
308
|
|
|
|
309
|
|
|
const ERROR_SESSION_EXPIRES = 'errSessionExpires'; |
310
|
|
|
|
311
|
|
|
const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir'; |
312
|
|
|
const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile'; |
313
|
|
|
const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile'; |
314
|
|
|
const ERROR_FTP_MKDIR = 'errFtpMkdir'; |
315
|
|
|
const ERROR_ARCHIVE_EXEC = 'errArchiveExec'; |
316
|
|
|
const ERROR_EXTRACT_EXEC = 'errExtractExec'; |
317
|
|
|
const ERROR_SEARCH_TIMEOUT = 'errSearchTimeout'; // 'Timed out while searching "$1". Search result is partial.' |
318
|
|
|
const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.' |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Constructor |
322
|
|
|
* |
323
|
|
|
* @param array elFinder and roots configurations |
324
|
|
|
* @author Dmitry (dio) Levashov |
325
|
|
|
*/ |
326
|
|
|
public function __construct($opts) |
|
|
|
|
327
|
|
|
{ |
328
|
|
|
// set error handler of WARNING, NOTICE |
329
|
|
|
set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE); |
330
|
|
|
|
331
|
|
|
// setup debug mode |
332
|
|
|
$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false); |
|
|
|
|
333
|
|
|
if ($this->debug) { |
334
|
|
|
error_reporting(-1); |
335
|
|
|
ini_set('diaplay_errors', '1'); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
if (!interface_exists('elFinderSessionInterface')) { |
339
|
|
|
include_once dirname(__FILE__) . '/elFinderSessionInterface.php'; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
// session handler |
343
|
|
|
if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) { |
344
|
|
|
$this->session = $opts['session']; |
345
|
|
|
} else { |
346
|
|
|
$sessionOpts = array( |
347
|
|
|
'base64encode' => !empty($opts['base64encodeSessionData']), |
348
|
|
|
'keys' => array( |
349
|
|
|
'default' => !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches', |
350
|
|
|
'netvolume' => !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes' |
351
|
|
|
) |
352
|
|
|
); |
353
|
|
|
if (! class_exists('elFinderSession')) { |
354
|
|
|
include_once dirname(__FILE__) . '/elFinderSession.php'; |
355
|
|
|
} |
356
|
|
|
$this->session = new elFinderSession($sessionOpts); |
357
|
|
|
} |
358
|
|
|
// try session start | restart |
359
|
|
|
$this->session->start(); |
360
|
|
|
|
361
|
|
|
$sessionUseCmds = array(); |
362
|
|
|
if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) { |
363
|
|
|
$sessionUseCmds = $opts['sessionUseCmds']; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart" |
367
|
|
|
if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) { |
368
|
|
|
self::$volumesCnt = $volumesCntStart; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
$this->time = $this->utime(); |
|
|
|
|
372
|
|
|
$this->sessionCloseEarlier = isset($opts['sessionCloseEarlier'])? (bool)$opts['sessionCloseEarlier'] : true; |
373
|
|
|
$this->sessionUseCmds = array_flip($sessionUseCmds); |
374
|
|
|
$this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0); |
375
|
|
|
$this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : ''); |
376
|
|
|
$this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : ''); |
377
|
|
|
elFinder::$commonTempPath = (isset($opts['commonTempPath']) ? $opts['commonTempPath'] : './.tmp'); |
378
|
|
|
if (!is_writable(elFinder::$commonTempPath)) { |
379
|
|
|
elFinder::$commonTempPath = ''; |
380
|
|
|
} |
381
|
|
|
$this->maxArcFilesSize = isset($opts['maxArcFilesSize'])? intval($opts['maxArcFilesSize']) : 0; |
382
|
|
|
$this->optionsNetVolumes = (isset($opts['optionsNetVolumes']) && is_array($opts['optionsNetVolumes']))? $opts['optionsNetVolumes'] : array(); |
383
|
|
|
|
384
|
|
|
// deprecated settings |
385
|
|
|
$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey'])? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes'; |
|
|
|
|
386
|
|
|
self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches'; |
|
|
|
|
387
|
|
|
|
388
|
|
|
// check session cache |
389
|
|
|
$_optsMD5 = md5(json_encode($opts['roots'])); |
390
|
|
|
if ($this->session->get('_optsMD5') !== $_optsMD5) { |
391
|
|
|
$this->session->set('_optsMD5', $_optsMD5); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
// setlocale and global locale regists to elFinder::locale |
395
|
|
|
self::$locale = !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8'; |
396
|
|
|
if (false === setlocale(LC_ALL, self::$locale)) { |
397
|
|
|
self::$locale = setlocale(LC_ALL, ''); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
// set defaultMimefile |
401
|
|
|
elFinder::$defaultMimefile = (isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : ''); |
402
|
|
|
|
403
|
|
|
// bind events listeners |
404
|
|
|
if (!empty($opts['bind']) && is_array($opts['bind'])) { |
405
|
|
|
$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET; |
406
|
|
|
$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : ''; |
407
|
|
|
foreach ($opts['bind'] as $cmd => $handlers) { |
408
|
|
|
$doRegist = (strpos($cmd, '*') !== false); |
409
|
|
|
if (! $doRegist) { |
410
|
|
|
$_getcmd = create_function('$cmd', 'list($ret) = explode(\'.\', $cmd);return trim($ret);'); |
|
|
|
|
411
|
|
|
$doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd)))); |
412
|
|
|
} |
413
|
|
|
if ($doRegist) { |
414
|
|
|
// for backward compatibility |
415
|
|
|
if (! is_array($handlers)) { |
416
|
|
|
$handlers = array($handlers); |
417
|
|
|
} else { |
418
|
|
|
if (count($handlers) === 2 && is_object($handlers[0])) { |
419
|
|
|
$handlers = array($handlers); |
420
|
|
|
} |
421
|
|
|
} |
422
|
|
|
foreach($handlers as $handler) { |
423
|
|
|
if ($handler) { |
424
|
|
|
if (is_string($handler) && strpos($handler, '.')) { |
425
|
|
|
list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, ''); |
426
|
|
|
if (strcasecmp($_domain, 'plugin') === 0) { |
427
|
|
|
if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array()) |
428
|
|
|
and method_exists($plugin, $_method)) { |
429
|
|
|
$this->bind($cmd, array($plugin, $_method)); |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
} else { |
433
|
|
|
$this->bind($cmd, $handler); |
434
|
|
|
} |
435
|
|
|
} |
436
|
|
|
} |
437
|
|
|
} |
438
|
|
|
} |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
if (!isset($opts['roots']) || !is_array($opts['roots'])) { |
442
|
|
|
$opts['roots'] = array(); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
// check for net volumes stored in session |
446
|
|
|
$netVolumes = $this->getNetVolumes(); |
447
|
|
|
foreach ($netVolumes as $key => $root) { |
448
|
|
|
if (! isset($root['id'])) { |
449
|
|
|
// given fixed unique id |
450
|
|
|
if (! $root['id'] = $this->getNetVolumeUniqueId($netVolumes)) { |
451
|
|
|
$this->mountErrors[] = 'Netmount Driver "'.$root['driver'].'" : Could\'t given volume id.'; |
452
|
|
|
continue; |
453
|
|
|
} |
454
|
|
|
} |
455
|
|
|
$opts['roots'][$key] = $root; |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
// "mount" volumes |
459
|
|
|
foreach ($opts['roots'] as $i => $o) { |
460
|
|
|
$class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : ''); |
461
|
|
|
|
462
|
|
|
if (class_exists($class)) { |
463
|
|
|
$volume = new $class(); |
464
|
|
|
|
465
|
|
|
try { |
466
|
|
|
if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) { |
467
|
|
|
$o['maxArcFilesSize'] = $this->maxArcFilesSize; |
468
|
|
|
} |
469
|
|
|
// pass session handler |
470
|
|
|
$volume->setSession($this->session); |
471
|
|
|
if ($volume->mount($o)) { |
472
|
|
|
// unique volume id (ends on "_") - used as prefix to files hash |
473
|
|
|
$id = $volume->id(); |
474
|
|
|
|
475
|
|
|
$this->volumes[$id] = $volume; |
476
|
|
|
if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) { |
477
|
|
|
$this->default = $this->volumes[$id]; |
478
|
|
|
} |
479
|
|
|
} else { |
480
|
|
|
$this->removeNetVolume($i, $volume); |
481
|
|
|
$this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error()); |
482
|
|
|
} |
483
|
|
|
} catch (Exception $e) { |
484
|
|
|
$this->removeNetVolume($i, $volume); |
485
|
|
|
$this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage(); |
486
|
|
|
} |
487
|
|
|
} else { |
488
|
|
|
$this->mountErrors[] = 'Driver "'.$class.'" does not exist'; |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
// if at least one readable volume - ii desu >_< |
493
|
|
|
$this->loaded = !empty($this->default); |
494
|
|
|
|
495
|
|
|
// restore error handler for now |
496
|
|
|
restore_error_handler(); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
/** |
500
|
|
|
* Return elFinder session wrapper instance |
501
|
|
|
* |
502
|
|
|
* @return object elFinderSessionInterface |
503
|
|
|
**/ |
504
|
|
|
public function getSession() { |
505
|
|
|
return $this->session; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Return true if fm init correctly |
510
|
|
|
* |
511
|
|
|
* @return bool |
512
|
|
|
* @author Dmitry (dio) Levashov |
513
|
|
|
**/ |
514
|
|
|
public function loaded() { |
515
|
|
|
return $this->loaded; |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Return version (api) number |
520
|
|
|
* |
521
|
|
|
* @return string |
522
|
|
|
* @author Dmitry (dio) Levashov |
523
|
|
|
**/ |
524
|
|
|
public function version() { |
525
|
|
|
return $this->version; |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Add handler to elFinder command |
530
|
|
|
* |
531
|
|
|
* @param string command name |
532
|
|
|
* @param string|array callback name or array(object, method) |
533
|
|
|
* @return elFinder |
534
|
|
|
* @author Dmitry (dio) Levashov |
535
|
|
|
**/ |
536
|
|
|
public function bind($cmd, $handler) { |
537
|
|
|
$allCmds = array_keys($this->commands); |
538
|
|
|
$cmds = array(); |
539
|
|
|
foreach(explode(' ', $cmd) as $_cmd) { |
540
|
|
|
if ($_cmd !== '') { |
541
|
|
|
if ($all = strpos($_cmd, '*') !== false) { |
|
|
|
|
542
|
|
|
list(, $sub) = array_pad(explode('.', $_cmd), 2, ''); |
543
|
|
|
if ($sub) { |
544
|
|
|
$sub = str_replace('\'', '\\\'', $sub); |
545
|
|
|
$addSub = create_function('$cmd', 'return $cmd . \'.\' . trim(\'' . $sub . '\');'); |
|
|
|
|
546
|
|
|
$cmds = array_merge($cmds, array_map($addSub, $allCmds)); |
547
|
|
|
} else { |
548
|
|
|
$cmds = array_merge($cmds, $allCmds); |
549
|
|
|
} |
550
|
|
|
} else { |
551
|
|
|
$cmds[] = $_cmd; |
552
|
|
|
} |
553
|
|
|
} |
554
|
|
|
} |
555
|
|
|
$cmds = array_unique($cmds); |
556
|
|
|
|
557
|
|
|
foreach ($cmds as $cmd) { |
558
|
|
|
if (!isset($this->listeners[$cmd])) { |
559
|
|
|
$this->listeners[$cmd] = array(); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
if (is_callable($handler)) { |
563
|
|
|
$this->listeners[$cmd][] = $handler; |
564
|
|
|
} |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
return $this; |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* Remove event (command exec) handler |
572
|
|
|
* |
573
|
|
|
* @param string command name |
574
|
|
|
* @param string|array callback name or array(object, method) |
575
|
|
|
* @return elFinder |
576
|
|
|
* @author Dmitry (dio) Levashov |
577
|
|
|
**/ |
578
|
|
|
public function unbind($cmd, $handler) { |
579
|
|
|
if (!empty($this->listeners[$cmd])) { |
580
|
|
|
foreach ($this->listeners[$cmd] as $i => $h) { |
581
|
|
|
if ($h === $handler) { |
582
|
|
|
unset($this->listeners[$cmd][$i]); |
583
|
|
|
return $this; |
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
} |
587
|
|
|
return $this; |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
/** |
591
|
|
|
* Return true if command exists |
592
|
|
|
* |
593
|
|
|
* @param string command name |
594
|
|
|
* @return bool |
595
|
|
|
* @author Dmitry (dio) Levashov |
596
|
|
|
**/ |
597
|
|
|
public function commandExists($cmd) { |
598
|
|
|
return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
/** |
602
|
|
|
* Return root - file's owner (public func of volume()) |
603
|
|
|
* |
604
|
|
|
* @param string file hash |
605
|
|
|
* @return elFinderStorageDriver |
606
|
|
|
* @author Naoki Sawada |
607
|
|
|
*/ |
608
|
|
|
public function getVolume($hash) { |
609
|
|
|
return $this->volume($hash); |
610
|
|
|
} |
611
|
|
|
|
612
|
|
|
/** |
613
|
|
|
* Return command required arguments info |
614
|
|
|
* |
615
|
|
|
* @param string command name |
616
|
|
|
* @return array |
617
|
|
|
* @author Dmitry (dio) Levashov |
618
|
|
|
**/ |
619
|
|
|
public function commandArgsList($cmd) { |
620
|
|
|
return $this->commandExists($cmd) ? $this->commands[$cmd] : array(); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
private function session_expires() { |
624
|
|
|
|
625
|
|
|
if (! $last = $this->session->get(':LAST_ACTIVITY')) { |
626
|
|
|
$this->session->set(':LAST_ACTIVITY', time()); |
627
|
|
|
return false; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
if ( ($this->timeout > 0) && (time() - $last > $this->timeout) ) { |
631
|
|
|
return true; |
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
$this->session->set(':LAST_ACTIVITY', time()); |
635
|
|
|
return false; |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* Exec command and return result |
640
|
|
|
* |
641
|
|
|
* @param string $cmd command name |
642
|
|
|
* @param array $args command arguments |
643
|
|
|
* @return array |
644
|
|
|
* @author Dmitry (dio) Levashov |
645
|
|
|
**/ |
646
|
|
|
public function exec($cmd, $args) { |
647
|
|
|
// set error handler of WARNING, NOTICE |
648
|
|
|
set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE); |
649
|
|
|
|
650
|
|
|
if (!$this->loaded) { |
651
|
|
|
return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL)); |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
if ($this->session_expires()) { |
655
|
|
|
return array('error' => $this->error(self::ERROR_SESSION_EXPIRES)); |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
if (!$this->commandExists($cmd)) { |
659
|
|
|
return array('error' => $this->error(self::ERROR_UNKNOWN_CMD)); |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
if (!empty($args['mimes']) && is_array($args['mimes'])) { |
663
|
|
|
foreach ($this->volumes as $id => $v) { |
664
|
|
|
$this->volumes[$id]->setMimesFilter($args['mimes']); |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
// detect destination dirHash and volume |
669
|
|
|
$dstVolume = false; |
670
|
|
|
$dst = ! empty($args['target'])? $args['target'] : (! empty($args['dst'])? $args['dst'] : ''); |
671
|
|
|
if ($dst) { |
672
|
|
|
$dstVolume = $this->volume($dst); |
673
|
|
|
} else if (isset($args['targets']) && is_array($args['targets']) && isset($args['targets'][0])) { |
674
|
|
|
$dst = $args['targets'][0]; |
675
|
|
|
$dstVolume = $this->volume($dst); |
676
|
|
|
if (($_stat = $dstVolume->file($dst)) && ! empty($_stat['phash'])) { |
677
|
|
|
$dst = $_stat['phash']; |
678
|
|
|
} else { |
679
|
|
|
$dst = ''; |
680
|
|
|
} |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
// call pre handlers for this command |
684
|
|
|
$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd])? false : $this->sessionCloseEarlier; |
685
|
|
|
if (!empty($this->listeners[$cmd.'.pre'])) { |
686
|
|
|
foreach ($this->listeners[$cmd.'.pre'] as $handler) { |
687
|
|
|
call_user_func_array($handler, array($cmd, &$args, $this, $dstVolume)); |
688
|
|
|
} |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
// unlock session data for multiple access |
692
|
|
|
if ($this->sessionCloseEarlier && $args['sessionCloseEarlier']) { |
693
|
|
|
$this->session->close(); |
694
|
|
|
// deprecated property |
695
|
|
|
elFinder::$sessionClosed = true; |
|
|
|
|
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
if (substr(PHP_OS,0,3) === 'WIN') { |
699
|
|
|
// set time out |
700
|
|
|
elFinder::extendTimeLimit(300); |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
try { |
704
|
|
|
$result = $this->$cmd($args); |
705
|
|
|
} catch (Exception $e) { |
706
|
|
|
$result = array( |
707
|
|
|
'error' => htmlspecialchars($e->getMessage()), |
708
|
|
|
'sync' => true |
709
|
|
|
); |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
// check change dstDir |
713
|
|
|
$changeDst = false; |
714
|
|
|
if ($dst && $dstVolume && (! empty($result['added']) || ! empty($result['removed']))) { |
715
|
|
|
$changeDst = true; |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
foreach ($this->volumes as $volume) { |
719
|
|
|
if (isset($result['removed'])) { |
720
|
|
|
$result['removed'] = array_merge($result['removed'], $volume->removed()); |
721
|
|
|
if (! $changeDst && $dst && $dstVolume && $volume === $dstVolume) { |
722
|
|
|
$changeDst = true; |
723
|
|
|
} |
724
|
|
|
} |
725
|
|
|
if (isset($result['added'])) { |
726
|
|
|
$result['added'] = array_merge($result['added'], $volume->added()); |
727
|
|
|
if (! $changeDst && $dst && $dstVolume && $volume === $dstVolume) { |
728
|
|
|
$changeDst = true; |
729
|
|
|
} |
730
|
|
|
} |
731
|
|
|
$volume->resetResultStat(); |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
// dstDir is changed |
735
|
|
|
if ($changeDst) { |
736
|
|
|
if ($dstDir = $dstVolume->dir($dst)) { |
737
|
|
|
if (! isset($result['changed'])) { |
738
|
|
|
$result['changed'] = array(); |
739
|
|
|
} |
740
|
|
|
$result['changed'][] = $dstDir; |
741
|
|
|
} |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
// call handlers for this command |
745
|
|
|
if (!empty($this->listeners[$cmd])) { |
746
|
|
|
foreach ($this->listeners[$cmd] as $handler) { |
747
|
|
|
if (call_user_func_array($handler,array($cmd, &$result, $args, $this, $dstVolume))) { |
748
|
|
|
// handler return true to force sync client after command completed |
749
|
|
|
$result['sync'] = true; |
750
|
|
|
} |
751
|
|
|
} |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
// replace removed files info with removed files hashes |
755
|
|
|
if (!empty($result['removed'])) { |
756
|
|
|
$removed = array(); |
757
|
|
|
foreach ($result['removed'] as $file) { |
758
|
|
|
$removed[] = $file['hash']; |
759
|
|
|
} |
760
|
|
|
$result['removed'] = array_unique($removed); |
761
|
|
|
} |
762
|
|
|
// remove hidden files and filter files by mimetypes |
763
|
|
|
if (!empty($result['added'])) { |
764
|
|
|
$result['added'] = $this->filter($result['added']); |
765
|
|
|
} |
766
|
|
|
// remove hidden files and filter files by mimetypes |
767
|
|
|
if (!empty($result['changed'])) { |
768
|
|
|
$result['changed'] = $this->filter($result['changed']); |
769
|
|
|
} |
770
|
|
|
|
771
|
|
|
if ($this->debug || !empty($args['debug'])) { |
772
|
|
|
$result['debug'] = array( |
773
|
|
|
'connector' => 'php', |
774
|
|
|
'phpver' => PHP_VERSION, |
775
|
|
|
'time' => $this->utime() - $this->time, |
776
|
|
|
'memory' => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'), |
777
|
|
|
'upload' => $this->uploadDebug, |
778
|
|
|
'volumes' => array(), |
779
|
|
|
'mountErrors' => $this->mountErrors, |
780
|
|
|
'phpErrors' => elFinder::$phpErrors |
781
|
|
|
); |
782
|
|
|
elFinder::$phpErrors = array(); |
783
|
|
|
|
784
|
|
|
foreach ($this->volumes as $id => $volume) { |
785
|
|
|
$result['debug']['volumes'][] = $volume->debug(); |
786
|
|
|
} |
787
|
|
|
} |
788
|
|
|
|
789
|
|
|
foreach ($this->volumes as $volume) { |
790
|
|
|
$volume->umount(); |
791
|
|
|
} |
792
|
|
|
|
793
|
|
|
if (!empty($result['callback'])) { |
794
|
|
|
$result['callback']['json'] = json_encode($result); |
795
|
|
|
$this->callback($result['callback']); |
796
|
|
|
} else { |
797
|
|
|
return $result; |
798
|
|
|
} |
799
|
|
|
//TODO: Add return statement here |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* Return file real path |
804
|
|
|
* |
805
|
|
|
* @param string $hash file hash |
806
|
|
|
* @return string |
807
|
|
|
* @author Dmitry (dio) Levashov |
808
|
|
|
**/ |
809
|
|
|
public function realpath($hash) { |
810
|
|
|
if (($volume = $this->volume($hash)) == false) { |
811
|
|
|
return false; |
812
|
|
|
} |
813
|
|
|
return $volume->realpath($hash); |
814
|
|
|
} |
815
|
|
|
|
816
|
|
|
/** |
817
|
|
|
* Return network volumes config. |
818
|
|
|
* |
819
|
|
|
* @return array |
820
|
|
|
* @author Dmitry (dio) Levashov |
821
|
|
|
*/ |
822
|
|
|
protected function getNetVolumes() { |
823
|
|
|
if ($data = $this->session->get('netvolume', array())) { |
824
|
|
|
return $data; |
825
|
|
|
} |
826
|
|
|
return array(); |
827
|
|
|
} |
828
|
|
|
|
829
|
|
|
/** |
830
|
|
|
* Save network volumes config. |
831
|
|
|
* |
832
|
|
|
* @param array $volumes volumes config |
833
|
|
|
* @return void |
834
|
|
|
* @author Dmitry (dio) Levashov |
835
|
|
|
*/ |
836
|
|
|
protected function saveNetVolumes($volumes) { |
837
|
|
|
$this->session->set('netvolume', $volumes); |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
/** |
841
|
|
|
* Remove netmount volume |
842
|
|
|
* |
843
|
|
|
* @param string $key netvolume key |
844
|
|
|
* @param object $volume volume driver instance |
845
|
|
|
* @return bool |
846
|
|
|
*/ |
847
|
|
|
protected function removeNetVolume($key, $volume) { |
848
|
|
|
$netVolumes = $this->getNetVolumes(); |
849
|
|
|
$res = true; |
850
|
|
|
if (is_object($volume) && method_exists($volume, 'netunmount')) { |
851
|
|
|
$res = $volume->netunmount($netVolumes, $key); |
852
|
|
|
} |
853
|
|
|
if ($res) { |
854
|
|
|
if (is_string($key) && isset($netVolumes[$key])) { |
855
|
|
|
unset($netVolumes[$key]); |
856
|
|
|
$this->saveNetVolumes($netVolumes); |
857
|
|
|
return true; |
858
|
|
|
} |
859
|
|
|
} |
860
|
|
|
return false; |
861
|
|
|
} |
862
|
|
|
|
863
|
|
|
/** |
864
|
|
|
* Get plugin instance & set to $this->plugins |
865
|
|
|
* |
866
|
|
|
* @param string $name Plugin name (dirctory name) |
867
|
|
|
* @param array $opts Plugin options (optional) |
868
|
|
|
* @return object | bool Plugin object instance Or false |
869
|
|
|
* @author Naoki Sawada |
870
|
|
|
*/ |
871
|
|
|
protected function getPluginInstance($name, $opts = array()) { |
872
|
|
|
$key = strtolower($name); |
873
|
|
|
if (! isset($this->plugins[$key])) { |
874
|
|
|
$class = 'elFinderPlugin' . $name; |
875
|
|
|
// to try auto load |
876
|
|
|
if (! class_exists($class)) { |
877
|
|
|
$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php'; |
878
|
|
|
if (is_file($p_file)) { |
879
|
|
|
include_once $p_file; |
880
|
|
|
} |
881
|
|
|
} |
882
|
|
|
if (class_exists($class, false)) { |
883
|
|
|
$this->plugins[$key] = new $class($opts); |
884
|
|
|
} else { |
885
|
|
|
$this->plugins[$key] = false; |
886
|
|
|
} |
887
|
|
|
} |
888
|
|
|
return $this->plugins[$key]; |
889
|
|
|
} |
890
|
|
|
|
891
|
|
|
/***************************************************************************/ |
892
|
|
|
/* commands */ |
893
|
|
|
/***************************************************************************/ |
894
|
|
|
|
895
|
|
|
/** |
896
|
|
|
* Normalize error messages |
897
|
|
|
* |
898
|
|
|
* @return array |
899
|
|
|
* @author Dmitry (dio) Levashov |
900
|
|
|
**/ |
901
|
|
|
public function error() { |
902
|
|
|
$errors = array(); |
903
|
|
|
|
904
|
|
|
foreach (func_get_args() as $msg) { |
905
|
|
|
if (is_array($msg)) { |
906
|
|
|
$errors = array_merge($errors, $msg); |
907
|
|
|
} else { |
908
|
|
|
$errors[] = $msg; |
909
|
|
|
} |
910
|
|
|
} |
911
|
|
|
|
912
|
|
|
return count($errors) ? $errors : array(self::ERROR_UNKNOWN); |
913
|
|
|
} |
914
|
|
|
|
915
|
|
|
protected function netmount($args) { |
916
|
|
|
$options = array(); |
917
|
|
|
$protocol = $args['protocol']; |
918
|
|
|
|
919
|
|
|
if ($protocol === 'netunmount') { |
920
|
|
|
if (! empty($args['user']) && $volume = $this->volume($args['user'])) { |
921
|
|
|
if ($this->removeNetVolume($args['host'], $volume)) { |
922
|
|
|
return array('removed' => array(array('hash' => $volume->root()))); |
923
|
|
|
} |
924
|
|
|
} |
925
|
|
|
return array('sync' => true, 'error' => $this->error(self::ERROR_NETUNMOUNT)); |
926
|
|
|
} |
927
|
|
|
|
928
|
|
|
$driver = isset(self::$netDrivers[$protocol]) ? self::$netDrivers[$protocol] : ''; |
929
|
|
|
$class = 'elFinderVolume'.$driver; |
930
|
|
|
|
931
|
|
|
if (!class_exists($class)) { |
932
|
|
|
return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER)); |
933
|
|
|
} |
934
|
|
|
|
935
|
|
|
if (!$args['path']) { |
936
|
|
|
$args['path'] = '/'; |
937
|
|
|
} |
938
|
|
|
|
939
|
|
|
foreach ($args as $k => $v) { |
940
|
|
|
if ($k != 'options' && $k != 'protocol' && $v) { |
941
|
|
|
$options[$k] = $v; |
942
|
|
|
} |
943
|
|
|
} |
944
|
|
|
|
945
|
|
|
if (is_array($args['options'])) { |
946
|
|
|
foreach ($args['options'] as $key => $value) { |
947
|
|
|
$options[$key] = $value; |
948
|
|
|
} |
949
|
|
|
} |
950
|
|
|
|
951
|
|
|
$volume = new $class(); |
952
|
|
|
|
953
|
|
|
// pass session handler |
954
|
|
|
$volume->setSession($this->session); |
955
|
|
|
|
956
|
|
|
if (method_exists($volume, 'netmountPrepare')) { |
957
|
|
|
$options = $volume->netmountPrepare($options); |
958
|
|
|
if (isset($options['exit'])) { |
959
|
|
|
if ($options['exit'] === 'callback') { |
960
|
|
|
$this->callback($options['out']); |
961
|
|
|
} |
962
|
|
|
return $options; |
963
|
|
|
} |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
$netVolumes = $this->getNetVolumes(); |
967
|
|
|
|
968
|
|
|
if (! isset($options['id'])) { |
969
|
|
|
// given fixed unique id |
970
|
|
|
if (! $options['id'] = $this->getNetVolumeUniqueId($netVolumes)) { |
971
|
|
|
return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], 'Could\'t given volume id.')); |
972
|
|
|
} |
973
|
|
|
} |
974
|
|
|
|
975
|
|
|
// load additional volume root options |
976
|
|
|
if (! empty($this->optionsNetVolumes['*'])) { |
977
|
|
|
$options = array_merge($options, $this->optionsNetVolumes['*']); |
978
|
|
|
} |
979
|
|
|
if (! empty($this->optionsNetVolumes[$protocol])) { |
980
|
|
|
$options = array_merge($options, $this->optionsNetVolumes[$protocol]); |
981
|
|
|
} |
982
|
|
|
|
983
|
|
|
if (! $key = $volume->netMountKey) { |
984
|
|
|
$key = md5($protocol . '-' . serialize($options)); |
985
|
|
|
} |
986
|
|
|
$options['netkey'] = $key; |
987
|
|
|
|
988
|
|
|
if ($volume->mount($options)) { |
989
|
|
|
$options['driver'] = $driver; |
990
|
|
|
$netVolumes[$key] = $options; |
991
|
|
|
$this->saveNetVolumes($netVolumes); |
992
|
|
|
$rootstat = $volume->file($volume->root()); |
993
|
|
|
return array('added' => array($rootstat)); |
994
|
|
|
} else { |
995
|
|
|
$this->removeNetVolume(null, $volume); |
996
|
|
|
return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error()))); |
997
|
|
|
} |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
/** |
1001
|
|
|
* "Open" directory |
1002
|
|
|
* Return array with following elements |
1003
|
|
|
* - cwd - opened dir info |
1004
|
|
|
* - files - opened dir content [and dirs tree if $args[tree]] |
1005
|
|
|
* - api - api version (if $args[init]) |
1006
|
|
|
* - uplMaxSize - if $args[init] |
1007
|
|
|
* - error - on failed |
1008
|
|
|
* |
1009
|
|
|
* @param array command arguments |
1010
|
|
|
* @return array |
1011
|
|
|
* @author Dmitry (dio) Levashov |
1012
|
|
|
**/ |
1013
|
|
|
protected function open($args) { |
1014
|
|
|
$target = $args['target']; |
1015
|
|
|
$init = !empty($args['init']); |
1016
|
|
|
$tree = !empty($args['tree']); |
1017
|
|
|
$volume = $this->volume($target); |
1018
|
|
|
$cwd = $volume ? $volume->dir($target) : false; |
1019
|
|
|
$hash = $init ? 'default folder' : '#'.$target; |
1020
|
|
|
$sleep = 0; |
|
|
|
|
1021
|
|
|
$compare = ''; |
1022
|
|
|
|
1023
|
|
|
// on init request we can get invalid dir hash - |
1024
|
|
|
// dir which can not be opened now, but remembered by client, |
1025
|
|
|
// so open default dir |
1026
|
|
|
if ((!$cwd || !$cwd['read']) && $init) { |
1027
|
|
|
$volume = $this->default; |
1028
|
|
|
$cwd = $volume->dir($volume->defaultPath()); |
1029
|
|
|
} |
1030
|
|
|
|
1031
|
|
|
if (!$cwd) { |
1032
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND)); |
1033
|
|
|
} |
1034
|
|
|
if (!$cwd['read']) { |
1035
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED)); |
1036
|
|
|
} |
1037
|
|
|
|
1038
|
|
|
$files = array(); |
1039
|
|
|
|
1040
|
|
|
// get other volume root |
1041
|
|
|
if ($tree) { |
1042
|
|
|
foreach ($this->volumes as $id => $v) { |
1043
|
|
|
$files[] = $v->file($v->root()); |
1044
|
|
|
} |
1045
|
|
|
} |
1046
|
|
|
|
1047
|
|
|
// get current working directory files list |
1048
|
|
|
if (($ls = $volume->scandir($cwd['hash'])) === false) { |
1049
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); |
1050
|
|
|
} |
1051
|
|
|
// long polling mode |
1052
|
|
|
if ($args['compare']) { |
1053
|
|
|
$sleep = max(1, (int)$volume->getOption('lsPlSleep')); |
1054
|
|
|
$standby = (int)$volume->getOption('plStandby'); |
1055
|
|
|
if ($standby > 0 && $sleep > $standby) { |
1056
|
|
|
$standby = $sleep; |
1057
|
|
|
} |
1058
|
|
|
$limit = max(0, floor($standby / $sleep)) + 1; |
1059
|
|
|
do { |
1060
|
|
|
elFinder::extendTimeLimit(30 + $sleep); |
1061
|
|
|
$_mtime = 0; |
1062
|
|
|
foreach($ls as $_f) { |
1063
|
|
|
$_mtime = max($_mtime, $_f['ts']); |
1064
|
|
|
} |
1065
|
|
|
$compare = strval(count($ls)).':'.strval($_mtime); |
1066
|
|
|
if ($compare !== $args['compare']) { |
1067
|
|
|
break; |
1068
|
|
|
} |
1069
|
|
|
if (--$limit) { |
1070
|
|
|
sleep($sleep); |
1071
|
|
|
$volume->clearstatcache(); |
1072
|
|
|
if (($ls = $volume->scandir($cwd['hash'])) === false) { |
1073
|
|
|
break; |
1074
|
|
|
} |
1075
|
|
|
} |
1076
|
|
|
} while($limit); |
1077
|
|
|
if ($ls === false) { |
1078
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); |
1079
|
|
|
} |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
if ($ls) { |
1083
|
|
|
if ($files) { |
|
|
|
|
1084
|
|
|
$files = array_merge($files, $ls); |
1085
|
|
|
} else { |
1086
|
|
|
$files = $ls; |
1087
|
|
|
} |
1088
|
|
|
} |
1089
|
|
|
|
1090
|
|
|
$result = array( |
1091
|
|
|
'cwd' => $cwd, |
1092
|
|
|
'options' => $volume->options($cwd['hash']), |
1093
|
|
|
'files' => $files |
1094
|
|
|
); |
1095
|
|
|
|
1096
|
|
|
if ($compare) { |
1097
|
|
|
$result['cwd']['compare'] = $compare; |
1098
|
|
|
} |
1099
|
|
|
|
1100
|
|
|
if (!empty($args['init'])) { |
1101
|
|
|
$result['api'] = $this->version; |
1102
|
|
|
$result['uplMaxSize'] = ini_get('upload_max_filesize'); |
1103
|
|
|
$result['uplMaxFile'] = ini_get('max_file_uploads'); |
1104
|
|
|
$result['netDrivers'] = array_keys(self::$netDrivers); |
1105
|
|
|
if ($volume) { |
1106
|
|
|
$result['cwd']['root'] = $volume->root(); |
1107
|
|
|
} |
1108
|
|
|
} |
1109
|
|
|
|
1110
|
|
|
return $result; |
1111
|
|
|
} |
1112
|
|
|
|
1113
|
|
|
/** |
1114
|
|
|
* Return dir files names list |
1115
|
|
|
* |
1116
|
|
|
* @param array command arguments |
1117
|
|
|
* @return array |
1118
|
|
|
* @author Dmitry (dio) Levashov |
1119
|
|
|
**/ |
1120
|
|
|
protected function ls($args) { |
1121
|
|
|
$target = $args['target']; |
1122
|
|
|
$intersect = isset($args['intersect'])? $args['intersect'] : array(); |
1123
|
|
|
|
1124
|
|
|
if (($volume = $this->volume($target)) == false |
1125
|
|
|
|| ($list = $volume->ls($target, $intersect)) === false) { |
1126
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); |
1127
|
|
|
} |
1128
|
|
|
return array('list' => $list); |
1129
|
|
|
} |
1130
|
|
|
|
1131
|
|
|
/** |
1132
|
|
|
* Return subdirs for required directory |
1133
|
|
|
* |
1134
|
|
|
* @param array command arguments |
1135
|
|
|
* @return array |
1136
|
|
|
* @author Dmitry (dio) Levashov |
1137
|
|
|
**/ |
1138
|
|
|
protected function tree($args) { |
1139
|
|
|
$target = $args['target']; |
1140
|
|
|
|
1141
|
|
|
if (($volume = $this->volume($target)) == false |
1142
|
|
|
|| ($tree = $volume->tree($target)) == false) { |
1143
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
return array('tree' => $tree); |
1147
|
|
|
} |
1148
|
|
|
|
1149
|
|
|
/** |
1150
|
|
|
* Return parents dir for required directory |
1151
|
|
|
* |
1152
|
|
|
* @param array command arguments |
1153
|
|
|
* @return array |
1154
|
|
|
* @author Dmitry (dio) Levashov |
1155
|
|
|
**/ |
1156
|
|
|
protected function parents($args) { |
1157
|
|
|
$target = $args['target']; |
1158
|
|
|
|
1159
|
|
|
if (($volume = $this->volume($target)) == false |
1160
|
|
|
|| ($tree = $volume->parents($target)) == false) { |
1161
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); |
1162
|
|
|
} |
1163
|
|
|
|
1164
|
|
|
return array('tree' => $tree); |
1165
|
|
|
} |
1166
|
|
|
|
1167
|
|
|
/** |
1168
|
|
|
* Return new created thumbnails list |
1169
|
|
|
* |
1170
|
|
|
* @param array command arguments |
1171
|
|
|
* @return array |
1172
|
|
|
* @author Dmitry (dio) Levashov |
1173
|
|
|
**/ |
1174
|
|
|
protected function tmb($args) { |
1175
|
|
|
|
1176
|
|
|
$result = array('images' => array()); |
1177
|
|
|
$targets = $args['targets']; |
1178
|
|
|
|
1179
|
|
|
foreach ($targets as $target) { |
1180
|
|
|
if (($volume = $this->volume($target)) != false |
1181
|
|
|
&& (($tmb = $volume->tmb($target)) != false)) { |
1182
|
|
|
$result['images'][$target] = $tmb; |
1183
|
|
|
} |
1184
|
|
|
} |
1185
|
|
|
return $result; |
1186
|
|
|
} |
1187
|
|
|
|
1188
|
|
|
/** |
1189
|
|
|
* Download files/folders as an archive file |
1190
|
|
|
* |
1191
|
|
|
* 1st: Return srrsy contains download archive file info |
1192
|
|
|
* 2nd: Return array contains opened file pointer, root itself and required headers |
1193
|
|
|
* |
1194
|
|
|
* @param array command arguments |
1195
|
|
|
* @return array |
1196
|
|
|
* @author Naoki Sawada |
1197
|
|
|
**/ |
1198
|
|
|
protected function zipdl($args) { |
|
|
|
|
1199
|
|
|
$targets = $args['targets']; |
1200
|
|
|
$download = !empty($args['download']); |
1201
|
|
|
$h404 = 'HTTP/1.x 404 Not Found'; |
1202
|
|
|
|
1203
|
|
|
if (!$download) { |
1204
|
|
|
//1st: Return srrsy contains download archive file info |
1205
|
|
|
$error = array(self::ERROR_ARCHIVE); |
1206
|
|
|
if (($volume = $this->volume($targets[0])) !== false) { |
1207
|
|
|
if ($dlres = $volume->zipdl($targets)) { |
1208
|
|
|
$path = $dlres['path']; |
1209
|
|
|
register_shutdown_function(create_function('$f', 'connection_status() && is_file($f) && unlink($f);'), $path); |
|
|
|
|
1210
|
|
|
if (count($targets) === 1) { |
1211
|
|
|
$name = basename($volume->path($targets[0])); |
1212
|
|
|
} else { |
1213
|
|
|
$name = $dlres['prefix'].'_Files'; |
1214
|
|
|
} |
1215
|
|
|
$name .= '.'.$dlres['ext']; |
1216
|
|
|
$result = array( |
1217
|
|
|
'zipdl' => array( |
1218
|
|
|
'file' => basename($path), |
1219
|
|
|
'name' => $name, |
1220
|
|
|
'mime' => $dlres['mime'] |
1221
|
|
|
) |
1222
|
|
|
); |
1223
|
|
|
return $result; |
1224
|
|
|
} |
1225
|
|
|
$error = array_merge($error, $volume->error()); |
1226
|
|
|
} |
1227
|
|
|
return array('error' => $error); |
1228
|
|
|
} else { |
1229
|
|
|
// 2nd: Return array contains opened file pointer, root itself and required headers |
1230
|
|
|
if (count($targets) !== 4 || ($volume = $this->volume($targets[0])) == false) { |
1231
|
|
|
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); |
1232
|
|
|
} |
1233
|
|
|
$file = $targets[1]; |
1234
|
|
|
$path = $volume->getTempPath().DIRECTORY_SEPARATOR.$file; |
1235
|
|
|
register_shutdown_function(create_function('$f', 'is_file($f) && unlink($f);'), $path); |
|
|
|
|
1236
|
|
|
if (!is_readable($path)) { |
1237
|
|
|
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); |
1238
|
|
|
} |
1239
|
|
|
$name = $targets[2]; |
1240
|
|
|
$mime = $targets[3]; |
1241
|
|
|
|
1242
|
|
|
$filenameEncoded = rawurlencode($name); |
1243
|
|
|
if (strpos($filenameEncoded, '%') === false) { // ASCII only |
1244
|
|
|
$filename = 'filename="'.$name.'"'; |
1245
|
|
|
} else { |
1246
|
|
|
$ua = $_SERVER['HTTP_USER_AGENT']; |
1247
|
|
|
if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) |
1248
|
|
|
$filename = 'filename="'.$filenameEncoded.'"'; |
1249
|
|
|
} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 |
1250
|
|
|
$filename = 'filename="'.str_replace('"', '', $name).'"'; |
1251
|
|
|
} else { // RFC 6266 (RFC 2231/RFC 5987) |
|
|
|
|
1252
|
|
|
$filename = 'filename*=UTF-8\'\''.$filenameEncoded; |
1253
|
|
|
} |
1254
|
|
|
} |
1255
|
|
|
|
1256
|
|
|
$fp = fopen($path, 'rb'); |
1257
|
|
|
$file = fstat($fp); |
1258
|
|
|
$result = array( |
1259
|
|
|
'pointer' => $fp, |
1260
|
|
|
'header' => array( |
1261
|
|
|
'Content-Type: '.$mime, |
1262
|
|
|
'Content-Disposition: attachment; '.$filename, |
1263
|
|
|
'Content-Transfer-Encoding: binary', |
1264
|
|
|
'Content-Length: '.$file['size'], |
1265
|
|
|
'Accept-Ranges: none', |
1266
|
|
|
'Connection: close' |
1267
|
|
|
) |
1268
|
|
|
); |
1269
|
|
|
return $result; |
1270
|
|
|
} |
1271
|
|
|
} |
1272
|
|
|
|
1273
|
|
|
/** |
1274
|
|
|
* Required to output file in browser when volume URL is not set |
1275
|
|
|
* Return array contains opened file pointer, root itself and required headers |
1276
|
|
|
* |
1277
|
|
|
* @param array command arguments |
1278
|
|
|
* @return array |
1279
|
|
|
* @author Dmitry (dio) Levashov |
1280
|
|
|
**/ |
1281
|
|
|
protected function file($args) { |
|
|
|
|
1282
|
|
|
$target = $args['target']; |
1283
|
|
|
$download = !empty($args['download']); |
1284
|
|
|
$h403 = 'HTTP/1.x 403 Access Denied'; |
1285
|
|
|
$h404 = 'HTTP/1.x 404 Not Found'; |
1286
|
|
|
|
1287
|
|
|
if (($volume = $this->volume($target)) == false) { |
1288
|
|
|
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); |
1289
|
|
|
} |
1290
|
|
|
|
1291
|
|
|
if (($file = $volume->file($target)) == false) { |
1292
|
|
|
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); |
1293
|
|
|
} |
1294
|
|
|
|
1295
|
|
|
if (!$file['read']) { |
1296
|
|
|
return array('error' => 'Access denied', 'header' => $h403, 'raw' => true); |
1297
|
|
|
} |
1298
|
|
|
|
1299
|
|
|
if (($fp = $volume->open($target)) == false) { |
1300
|
|
|
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); |
1301
|
|
|
} |
1302
|
|
|
|
1303
|
|
|
// allow change MIME type by 'file.pre' callback functions |
1304
|
|
|
$mime = isset($args['mime'])? $args['mime'] : $file['mime']; |
1305
|
|
|
if ($download) { |
1306
|
|
|
$disp = 'attachment'; |
1307
|
|
|
} else { |
1308
|
|
|
$dispInlineRegex = $volume->getOption('dispInlineRegex'); |
1309
|
|
|
$inlineRegex = false; |
1310
|
|
|
if ($dispInlineRegex) { |
1311
|
|
|
$inlineRegex = '#' . str_replace('#', '\\#', $dispInlineRegex) . '#'; |
1312
|
|
|
try { |
1313
|
|
|
preg_match($inlineRegex, ''); |
1314
|
|
|
} catch(Exception $e) { |
1315
|
|
|
$inlineRegex = false; |
1316
|
|
|
} |
1317
|
|
|
} |
1318
|
|
|
if (!$inlineRegex) { |
|
|
|
|
1319
|
|
|
$inlineRegex = '#^(?:(?:image|text)|application/x-shockwave-flash$)#'; |
1320
|
|
|
} |
1321
|
|
|
$disp = preg_match($inlineRegex, $mime)? 'inline' : 'attachment'; |
1322
|
|
|
} |
1323
|
|
|
|
1324
|
|
|
$filenameEncoded = rawurlencode($file['name']); |
1325
|
|
|
if (strpos($filenameEncoded, '%') === false) { // ASCII only |
1326
|
|
|
$filename = 'filename="'.$file['name'].'"'; |
1327
|
|
|
} else { |
1328
|
|
|
$ua = $_SERVER['HTTP_USER_AGENT']; |
1329
|
|
|
if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987) |
1330
|
|
|
$filename = 'filename="'.$filenameEncoded.'"'; |
1331
|
|
|
} elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false && preg_match('#Version/[3-5]#', $ua)) { // Safari < 6 |
1332
|
|
|
$filename = 'filename="'.str_replace('"', '', $file['name']).'"'; |
1333
|
|
|
} else { // RFC 6266 (RFC 2231/RFC 5987) |
|
|
|
|
1334
|
|
|
$filename = 'filename*=UTF-8\'\''.$filenameEncoded; |
1335
|
|
|
} |
1336
|
|
|
} |
1337
|
|
|
|
1338
|
|
|
$result = array( |
1339
|
|
|
'volume' => $volume, |
1340
|
|
|
'pointer' => $fp, |
1341
|
|
|
'info' => $file, |
1342
|
|
|
'header' => array( |
1343
|
|
|
'Content-Type: '.$mime, |
1344
|
|
|
'Content-Disposition: '.$disp.'; '.$filename, |
1345
|
|
|
'Content-Transfer-Encoding: binary', |
1346
|
|
|
'Content-Length: '.$file['size'], |
1347
|
|
|
'Connection: close' |
1348
|
|
|
) |
1349
|
|
|
); |
1350
|
|
|
if (isset($file['url']) && $file['url'] && $file['url'] != 1) { |
1351
|
|
|
$result['header'][] = 'Content-Location: '.$file['url']; |
1352
|
|
|
} |
1353
|
|
|
return $result; |
1354
|
|
|
} |
1355
|
|
|
|
1356
|
|
|
/** |
1357
|
|
|
* Count total files size |
1358
|
|
|
* |
1359
|
|
|
* @param array command arguments |
1360
|
|
|
* @return array |
1361
|
|
|
* @author Dmitry (dio) Levashov |
1362
|
|
|
**/ |
1363
|
|
|
protected function size($args) { |
1364
|
|
|
$size = 0; |
1365
|
|
|
|
1366
|
|
|
foreach ($args['targets'] as $target) { |
1367
|
|
|
if (($volume = $this->volume($target)) == false |
1368
|
|
|
|| ($file = $volume->file($target)) == false |
1369
|
|
|
|| !$file['read']) { |
1370
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, '#'.$target)); |
1371
|
|
|
} |
1372
|
|
|
|
1373
|
|
|
$size += $volume->size($target); |
1374
|
|
|
} |
1375
|
|
|
return array('size' => $size); |
1376
|
|
|
} |
1377
|
|
|
|
1378
|
|
|
/** |
1379
|
|
|
* Create directory |
1380
|
|
|
* |
1381
|
|
|
* @param array command arguments |
1382
|
|
|
* @return array |
1383
|
|
|
* @author Dmitry (dio) Levashov |
1384
|
|
|
**/ |
1385
|
|
|
protected function mkdir($args) { |
1386
|
|
|
$target = $args['target']; |
1387
|
|
|
$name = $args['name']; |
1388
|
|
|
$dirs = $args['dirs']; |
1389
|
|
|
if ($name === '' && !$dirs) { |
1390
|
|
|
return array('error' => $this->error(self::ERROR_INV_PARAMS, 'mkdir')); |
1391
|
|
|
} |
1392
|
|
|
|
1393
|
|
|
if (($volume = $this->volume($target)) == false) { |
1394
|
|
|
return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)); |
1395
|
|
|
} |
1396
|
|
|
if ($dirs) { |
1397
|
|
|
sort($dirs); |
1398
|
|
|
$reset = null; |
1399
|
|
|
$mkdirs = array(); |
1400
|
|
|
foreach($dirs as $dir) { |
1401
|
|
|
$tgt =& $mkdirs; |
1402
|
|
|
$_names = explode('/', trim($dir, '/')); |
1403
|
|
|
foreach($_names as $_key => $_name) { |
1404
|
|
|
if (! isset($tgt[$_name])) { |
1405
|
|
|
$tgt[$_name] = array(); |
1406
|
|
|
} |
1407
|
|
|
$tgt =& $tgt[$_name]; |
1408
|
|
|
} |
1409
|
|
|
$tgt =& $reset; |
1410
|
|
|
} |
1411
|
|
|
return ($res = $this->ensureDirsRecursively($volume, $target, $mkdirs)) === false |
|
|
|
|
1412
|
|
|
? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error())) |
1413
|
|
|
: array('added' => $res['stats'], 'hashes' => $res['hashes']); |
1414
|
|
|
} else { |
1415
|
|
|
return ($dir = $volume->mkdir($target, $name)) == false |
1416
|
|
|
? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error())) |
1417
|
|
|
: array('added' => array($dir)); |
1418
|
|
|
} |
1419
|
|
|
} |
1420
|
|
|
|
1421
|
|
|
/** |
1422
|
|
|
* Create empty file |
1423
|
|
|
* |
1424
|
|
|
* @param array command arguments |
1425
|
|
|
* @return array |
1426
|
|
|
* @author Dmitry (dio) Levashov |
1427
|
|
|
**/ |
1428
|
|
|
protected function mkfile($args) { |
1429
|
|
|
$target = $args['target']; |
1430
|
|
|
$name = $args['name']; |
1431
|
|
|
|
1432
|
|
|
if (($volume = $this->volume($target)) == false) { |
1433
|
|
|
return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)); |
1434
|
|
|
} |
1435
|
|
|
|
1436
|
|
|
return ($file = $volume->mkfile($target, $args['name'])) == false |
1437
|
|
|
? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error())) |
1438
|
|
|
: array('added' => array($file)); |
1439
|
|
|
} |
1440
|
|
|
|
1441
|
|
|
/** |
1442
|
|
|
* Rename file |
1443
|
|
|
* |
1444
|
|
|
* @param array $args |
1445
|
|
|
* @return array |
1446
|
|
|
* @author Dmitry (dio) Levashov |
1447
|
|
|
**/ |
1448
|
|
|
protected function rename($args) { |
1449
|
|
|
$target = $args['target']; |
1450
|
|
|
$name = $args['name']; |
1451
|
|
|
|
1452
|
|
|
if (($volume = $this->volume($target)) == false |
1453
|
|
|
|| ($rm = $volume->file($target)) == false) { |
1454
|
|
|
return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND)); |
1455
|
|
|
} |
1456
|
|
|
$rm['realpath'] = $volume->realpath($target); |
1457
|
|
|
|
1458
|
|
|
return ($file = $volume->rename($target, $name)) == false |
1459
|
|
|
? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error())) |
1460
|
|
|
: array('added' => array($file), 'removed' => array($rm)); |
1461
|
|
|
} |
1462
|
|
|
|
1463
|
|
|
/** |
1464
|
|
|
* Duplicate file - create copy with "copy %d" suffix |
1465
|
|
|
* |
1466
|
|
|
* @param array $args command arguments |
1467
|
|
|
* @return array |
1468
|
|
|
* @author Dmitry (dio) Levashov |
1469
|
|
|
**/ |
1470
|
|
|
protected function duplicate($args) { |
1471
|
|
|
$targets = is_array($args['targets']) ? $args['targets'] : array(); |
1472
|
|
|
$result = array('added' => array()); |
1473
|
|
|
$suffix = empty($args['suffix']) ? 'copy' : $args['suffix']; |
1474
|
|
|
|
1475
|
|
|
foreach ($targets as $target) { |
1476
|
|
|
if (($volume = $this->volume($target)) == false |
1477
|
|
|
|| ($src = $volume->file($target)) == false) { |
1478
|
|
|
$result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND); |
1479
|
|
|
break; |
1480
|
|
|
} |
1481
|
|
|
|
1482
|
|
|
if (($file = $volume->duplicate($target, $suffix)) == false) { |
1483
|
|
|
$result['warning'] = $this->error($volume->error()); |
1484
|
|
|
break; |
1485
|
|
|
} |
1486
|
|
|
|
1487
|
|
|
$result['added'][] = $file; |
1488
|
|
|
} |
1489
|
|
|
|
1490
|
|
|
return $result; |
1491
|
|
|
} |
1492
|
|
|
|
1493
|
|
|
/** |
1494
|
|
|
* Remove dirs/files |
1495
|
|
|
* |
1496
|
|
|
* @param array command arguments |
1497
|
|
|
* @return array |
1498
|
|
|
* @author Dmitry (dio) Levashov |
1499
|
|
|
**/ |
1500
|
|
|
protected function rm($args) { |
1501
|
|
|
$targets = is_array($args['targets']) ? $args['targets'] : array(); |
1502
|
|
|
$result = array('removed' => array()); |
1503
|
|
|
|
1504
|
|
|
foreach ($targets as $target) { |
1505
|
|
|
if (($volume = $this->volume($target)) == false) { |
1506
|
|
|
$result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND); |
1507
|
|
|
return $result; |
1508
|
|
|
} |
1509
|
|
|
if (!$volume->rm($target)) { |
1510
|
|
|
$result['warning'] = $this->error($volume->error()); |
1511
|
|
|
return $result; |
1512
|
|
|
} |
1513
|
|
|
} |
1514
|
|
|
|
1515
|
|
|
return $result; |
1516
|
|
|
} |
1517
|
|
|
|
1518
|
|
|
/** |
1519
|
|
|
* Get remote contents |
1520
|
|
|
* |
1521
|
|
|
* @param string $url target url |
1522
|
|
|
* @param int $timeout timeout (sec) |
1523
|
|
|
* @param int $redirect_max redirect max count |
1524
|
|
|
* @param string $ua |
1525
|
|
|
* @param resource $fp |
1526
|
|
|
* @return string or bool(false) |
1527
|
|
|
* @retval string contents |
1528
|
|
|
* @retval false error |
1529
|
|
|
* @author Naoki Sawada |
1530
|
|
|
**/ |
1531
|
|
|
protected function get_remote_contents( &$url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0', $fp = null ) { |
1532
|
|
|
$method = (function_exists('curl_exec') && !ini_get('safe_mode') && !ini_get('open_basedir'))? 'curl_get_contents' : 'fsock_get_contents'; |
1533
|
|
|
return $this->$method( $url, $timeout, $redirect_max, $ua, $fp ); |
1534
|
|
|
} |
1535
|
|
|
|
1536
|
|
|
/** |
1537
|
|
|
* Get remote contents with cURL |
1538
|
|
|
* |
1539
|
|
|
* @param string $url target url |
1540
|
|
|
* @param int $timeout timeout (sec) |
1541
|
|
|
* @param int $redirect_max redirect max count |
1542
|
|
|
* @param string $ua |
1543
|
|
|
* @param resource $outfp |
1544
|
|
|
* @return string or bool(false) |
1545
|
|
|
* @retval string contents |
1546
|
|
|
* @retval false error |
1547
|
|
|
* @author Naoki Sawada |
1548
|
|
|
**/ |
1549
|
|
|
protected function curl_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ){ |
1550
|
|
|
$ch = curl_init(); |
1551
|
|
|
curl_setopt( $ch, CURLOPT_URL, $url ); |
1552
|
|
|
curl_setopt( $ch, CURLOPT_HEADER, false ); |
1553
|
|
|
if ($outfp) { |
1554
|
|
|
curl_setopt( $ch, CURLOPT_FILE, $outfp ); |
1555
|
|
|
} else { |
1556
|
|
|
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); |
1557
|
|
|
curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true ); |
1558
|
|
|
} |
1559
|
|
|
curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 1 ); |
1560
|
|
|
curl_setopt( $ch, CURLOPT_LOW_SPEED_TIME, $timeout ); |
1561
|
|
|
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); |
1562
|
|
|
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1); |
1563
|
|
|
curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max); |
1564
|
|
|
curl_setopt( $ch, CURLOPT_USERAGENT, $ua); |
1565
|
|
|
$result = curl_exec( $ch ); |
1566
|
|
|
$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); |
1567
|
|
|
curl_close( $ch ); |
1568
|
|
|
return $outfp? $outfp : $result; |
1569
|
|
|
} |
1570
|
|
|
|
1571
|
|
|
/** |
1572
|
|
|
* Get remote contents with fsockopen() |
1573
|
|
|
* |
1574
|
|
|
* @param string $url url |
1575
|
|
|
* @param int $timeout timeout (sec) |
1576
|
|
|
* @param int $redirect_max redirect max count |
1577
|
|
|
* @param string $ua |
1578
|
|
|
* @param resource $outfp |
1579
|
|
|
* @return string or bool(false) |
1580
|
|
|
* @retval string contents |
1581
|
|
|
* @retval false error |
1582
|
|
|
* @author Naoki Sawada |
1583
|
|
|
*/ |
1584
|
|
|
protected function fsock_get_contents( &$url, $timeout, $redirect_max, $ua, $outfp ) { |
1585
|
|
|
|
1586
|
|
|
$connect_timeout = 3; |
1587
|
|
|
$connect_try = 3; |
1588
|
|
|
$method = 'GET'; |
1589
|
|
|
$readsize = 4096; |
1590
|
|
|
$ssl = ''; |
1591
|
|
|
|
1592
|
|
|
$getSize = null; |
1593
|
|
|
$headers = ''; |
1594
|
|
|
|
1595
|
|
|
$arr = parse_url($url); |
1596
|
|
|
if (!$arr){ |
1597
|
|
|
// Bad request |
1598
|
|
|
return false; |
1599
|
|
|
} |
1600
|
|
|
if ($arr['scheme'] === 'https') { |
1601
|
|
|
$ssl = 'ssl://'; |
1602
|
|
|
} |
1603
|
|
|
|
1604
|
|
|
// query |
1605
|
|
|
$arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : ''; |
1606
|
|
|
// port |
1607
|
|
|
$arr['port'] = isset($arr['port']) ? $arr['port'] : ($ssl? 443 : 80); |
1608
|
|
|
|
1609
|
|
|
$url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port']; |
1610
|
|
|
$url_path = isset($arr['path']) ? $arr['path'] : '/'; |
1611
|
|
|
$uri = $url_path.$arr['query']; |
1612
|
|
|
|
1613
|
|
|
$query = $method.' '.$uri." HTTP/1.0\r\n"; |
1614
|
|
|
$query .= "Host: ".$arr['host']."\r\n"; |
1615
|
|
|
$query .= "Accept: */*\r\n"; |
1616
|
|
|
$query .= "Connection: close\r\n"; |
1617
|
|
|
if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n"; |
1618
|
|
|
if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n"; |
1619
|
|
|
|
1620
|
|
|
$query .= $headers; |
1621
|
|
|
|
1622
|
|
|
$query .= "\r\n"; |
1623
|
|
|
|
1624
|
|
|
$fp = $connect_try_count = 0; |
1625
|
|
|
while( !$fp && $connect_try_count < $connect_try ) { |
1626
|
|
|
|
1627
|
|
|
$errno = 0; |
1628
|
|
|
$errstr = ""; |
1629
|
|
|
$fp = fsockopen( |
1630
|
|
|
$ssl.$arr['host'], |
1631
|
|
|
$arr['port'], |
1632
|
|
|
$errno,$errstr,$connect_timeout); |
1633
|
|
|
if ($fp) break; |
1634
|
|
|
$connect_try_count++; |
1635
|
|
|
if (connection_aborted()) { |
1636
|
|
|
exit(); |
|
|
|
|
1637
|
|
|
} |
1638
|
|
|
sleep(1); // wait 1sec |
1639
|
|
|
} |
1640
|
|
|
|
1641
|
|
|
$fwrite = 0; |
|
|
|
|
1642
|
|
|
for ($written = 0; $written < strlen($query); $written += $fwrite) { |
1643
|
|
|
$fwrite = fwrite($fp, substr($query, $written)); |
1644
|
|
|
if (!$fwrite) { |
1645
|
|
|
break; |
1646
|
|
|
} |
1647
|
|
|
} |
1648
|
|
|
|
1649
|
|
|
$response = ''; |
|
|
|
|
1650
|
|
|
|
1651
|
|
|
if ($timeout) { |
1652
|
|
|
socket_set_timeout($fp, $timeout); |
1653
|
|
|
} |
1654
|
|
|
|
1655
|
|
|
$_response = ''; |
1656
|
|
|
$header = ''; |
1657
|
|
|
while($_response !== "\r\n"){ |
1658
|
|
|
$_response = fgets($fp, $readsize); |
1659
|
|
|
$header .= $_response; |
1660
|
|
|
}; |
1661
|
|
|
|
1662
|
|
|
$rccd = array_pad(explode(' ',$header,2), 2, ''); // array('HTTP/1.1','200') |
|
|
|
|
1663
|
|
|
$rc = (int)$rccd[1]; |
1664
|
|
|
|
1665
|
|
|
$ret = false; |
1666
|
|
|
// Redirect |
1667
|
|
|
switch ($rc) { |
1668
|
|
|
case 307: // Temporary Redirect |
1669
|
|
|
case 303: // See Other |
1670
|
|
|
case 302: // Moved Temporarily |
1671
|
|
|
case 301: // Moved Permanently |
1672
|
|
|
$matches = array(); |
1673
|
|
|
if (preg_match('/^Location: (.+?)(#.+)?$/im',$header,$matches) && --$redirect_max > 0) { |
1674
|
|
|
$_url = $url; |
1675
|
|
|
$url = trim($matches[1]); |
1676
|
|
|
$hash = isset($matches[2])? trim($matches[2]) : ''; |
|
|
|
|
1677
|
|
|
if (!preg_match('/^https?:\//',$url)) { // no scheme |
1678
|
|
|
if ($url{0} != '/') { // Relative path |
1679
|
|
|
// to Absolute path |
1680
|
|
|
$url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url; |
1681
|
|
|
} |
1682
|
|
|
// add sheme,host |
1683
|
|
|
$url = $url_base.$url; |
1684
|
|
|
} |
1685
|
|
|
if ($_url !== $url) { |
1686
|
|
|
fclose($fp); |
1687
|
|
|
return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua, $outfp ); |
1688
|
|
|
} |
1689
|
|
|
} |
1690
|
|
|
break; |
1691
|
|
|
case 200: |
1692
|
|
|
$ret = true; |
1693
|
|
|
} |
1694
|
|
|
if (! $ret) { |
1695
|
|
|
fclose($fp); |
1696
|
|
|
return false; |
1697
|
|
|
} |
1698
|
|
|
|
1699
|
|
|
$body = ''; |
1700
|
|
|
if (!$outfp) { |
1701
|
|
|
$outfp = fopen('php://temp', 'rwb'); |
1702
|
|
|
$body = true; |
1703
|
|
|
} |
1704
|
|
|
while(fwrite($outfp, fread($fp, $readsize))) { |
1705
|
|
|
if ($timeout) { |
1706
|
|
|
$_status = socket_get_status($fp); |
1707
|
|
|
if ($_status['timed_out']) { |
1708
|
|
|
fclose($outfp); |
1709
|
|
|
fclose($fp); |
1710
|
|
|
return false; // Request Time-out |
1711
|
|
|
} |
1712
|
|
|
} |
1713
|
|
|
} |
1714
|
|
|
if ($body) { |
1715
|
|
|
rewind($outfp); |
1716
|
|
|
$body = stream_get_contents($outfp); |
1717
|
|
|
fclose($outfp); |
1718
|
|
|
$outfp = null; |
1719
|
|
|
} |
1720
|
|
|
|
1721
|
|
|
fclose($fp); |
1722
|
|
|
|
1723
|
|
|
return $outfp? $outfp : $body; // Data |
|
|
|
|
1724
|
|
|
} |
1725
|
|
|
|
1726
|
|
|
/** |
1727
|
|
|
* Parse Data URI scheme |
1728
|
|
|
* |
1729
|
|
|
* @param string $str |
1730
|
|
|
* @param array $extTable |
1731
|
|
|
* @return array |
1732
|
|
|
* @author Naoki Sawada |
1733
|
|
|
*/ |
1734
|
|
|
protected function parse_data_scheme( $str, $extTable ) { |
1735
|
|
|
$data = $name = ''; |
1736
|
|
|
if ($fp = fopen('data://'.substr($str, 5), 'rb')) { |
1737
|
|
|
if ($data = stream_get_contents($fp)) { |
1738
|
|
|
$meta = stream_get_meta_data($fp); |
1739
|
|
|
$ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : ''; |
1740
|
|
|
$name = substr(md5($data), 0, 8) . $ext; |
1741
|
|
|
} |
1742
|
|
|
fclose($fp); |
1743
|
|
|
} |
1744
|
|
|
return array($data, $name); |
1745
|
|
|
} |
1746
|
|
|
|
1747
|
|
|
/** |
1748
|
|
|
* Detect file type extension by local path |
1749
|
|
|
* |
1750
|
|
|
* @param string $path Local path |
1751
|
|
|
* @return string file type extension with dot |
1752
|
|
|
* @author Naoki Sawada |
1753
|
|
|
*/ |
1754
|
|
|
protected function detectFileExtension($path) { |
1755
|
|
|
static $type, $finfo; |
1756
|
|
|
if (!$type) { |
1757
|
|
|
$keys = array_keys($this->volumes); |
1758
|
|
|
$volume = $this->volumes[$keys[0]]; |
1759
|
|
|
|
1760
|
|
|
if (class_exists('finfo', false)) { |
1761
|
|
|
$tmpFileInfo = explode(';', finfo_file(finfo_open(FILEINFO_MIME), __FILE__)); |
1762
|
|
|
} else { |
1763
|
|
|
$tmpFileInfo = false; |
1764
|
|
|
} |
1765
|
|
|
$regexp = '/text\/x\-(php|c\+\+)/'; |
1766
|
|
|
if ($tmpFileInfo && preg_match($regexp, array_shift($tmpFileInfo))) { |
1767
|
|
|
$type = 'finfo'; |
1768
|
|
|
$finfo = finfo_open(FILEINFO_MIME); |
1769
|
|
|
} elseif (function_exists('mime_content_type') |
1770
|
|
|
&& preg_match($regexp, array_shift(explode(';', mime_content_type(__FILE__))))) { |
|
|
|
|
1771
|
|
|
$type = 'mime_content_type'; |
1772
|
|
|
} elseif (function_exists('getimagesize')) { |
1773
|
|
|
$type = 'getimagesize'; |
1774
|
|
|
} else { |
1775
|
|
|
$type = 'none'; |
1776
|
|
|
} |
1777
|
|
|
} |
1778
|
|
|
|
1779
|
|
|
$mime = ''; |
1780
|
|
|
if ($type === 'finfo') { |
1781
|
|
|
$mime = finfo_file($finfo, $path); |
1782
|
|
|
} elseif ($type === 'mime_content_type') { |
1783
|
|
|
$mime = mime_content_type($path); |
1784
|
|
|
} elseif ($type === 'getimagesize') { |
1785
|
|
|
if ($img = getimagesize($path)) { |
1786
|
|
|
$mime = $img['mime']; |
1787
|
|
|
} |
1788
|
|
|
} |
1789
|
|
|
|
1790
|
|
|
if ($mime) { |
1791
|
|
|
$mime = explode(';', $mime); |
1792
|
|
|
$mime = trim($mime[0]); |
1793
|
|
|
|
1794
|
|
|
if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) { |
1795
|
|
|
// finfo return this mime for empty files |
1796
|
|
|
$mime = 'text/plain'; |
1797
|
|
|
} elseif ($mime == 'application/x-zip') { |
1798
|
|
|
// http://elrte.org/redmine/issues/163 |
1799
|
|
|
$mime = 'application/zip'; |
1800
|
|
|
} |
1801
|
|
|
} |
1802
|
|
|
|
1803
|
|
|
$ext = $mime? $volume->getExtentionByMime($mime) : ''; |
|
|
|
|
1804
|
|
|
return $ext? ('.' . $ext) : ''; |
1805
|
|
|
} |
1806
|
|
|
|
1807
|
|
|
/** |
1808
|
|
|
* Get temporary directory path |
1809
|
|
|
* |
1810
|
|
|
* @param string $volumeTempPath |
1811
|
|
|
* @return string |
1812
|
|
|
* @author Naoki Sawada |
1813
|
|
|
*/ |
1814
|
|
|
private function getTempDir($volumeTempPath = null) { |
1815
|
|
|
$testDirs = array(); |
1816
|
|
|
if ($this->uploadTempPath) { |
1817
|
|
|
$testDirs[] = rtrim(realpath($this->uploadTempPath), DIRECTORY_SEPARATOR); |
1818
|
|
|
} |
1819
|
|
|
if ($volumeTempPath) { |
|
|
|
|
1820
|
|
|
$testDirs[] = rtrim(realpath($volumeTempPath), DIRECTORY_SEPARATOR); |
1821
|
|
|
} |
1822
|
|
|
if (function_exists('sys_get_temp_dir')) { |
1823
|
|
|
$testDirs[] = sys_get_temp_dir(); |
1824
|
|
|
} |
1825
|
|
|
$tempDir = ''; |
1826
|
|
|
foreach($testDirs as $testDir) { |
1827
|
|
|
if (!$testDir || !is_dir($testDir)) continue; |
1828
|
|
|
if (is_writable($testDir)) { |
1829
|
|
|
$tempDir = $testDir; |
1830
|
|
|
$gc = time() - 3600; |
1831
|
|
|
foreach(glob($tempDir . DIRECTORY_SEPARATOR .'ELF*') as $cf) { |
1832
|
|
|
if (filemtime($cf) < $gc) { |
1833
|
|
|
unlink($cf); |
1834
|
|
|
} |
1835
|
|
|
} |
1836
|
|
|
break; |
1837
|
|
|
} |
1838
|
|
|
} |
1839
|
|
|
return $tempDir; |
1840
|
|
|
} |
1841
|
|
|
|
1842
|
|
|
/** |
1843
|
|
|
* chmod |
1844
|
|
|
* |
1845
|
|
|
* @param array command arguments |
1846
|
|
|
* @return array |
1847
|
|
|
* @author David Bartle |
1848
|
|
|
**/ |
1849
|
|
|
protected function chmod($args) { |
1850
|
|
|
$targets = $args['targets']; |
1851
|
|
|
$mode = intval((string)$args['mode'], 8); |
1852
|
|
|
|
1853
|
|
|
if (!is_array($targets)) { |
1854
|
|
|
$targets = array($targets); |
1855
|
|
|
} |
1856
|
|
|
|
1857
|
|
|
$result = array(); |
1858
|
|
|
|
1859
|
|
|
if (($volume = $this->volume($targets[0])) == false) { |
1860
|
|
|
$result['error'] = $this->error(self::ERROR_CONF_NO_VOL); |
1861
|
|
|
return $result; |
1862
|
|
|
} |
1863
|
|
|
|
1864
|
|
|
$files = array(); |
1865
|
|
|
$errors = array(); |
1866
|
|
|
foreach($targets as $target) { |
1867
|
|
|
$file = $volume->chmod($target, $mode); |
1868
|
|
|
if ($file) { |
1869
|
|
|
$files = array_merge($files, is_array($file)? $file : array($file)); |
1870
|
|
|
} else { |
1871
|
|
|
$errors = array_merge($errors, $volume->error()); |
1872
|
|
|
} |
1873
|
|
|
} |
1874
|
|
|
|
1875
|
|
|
if ($files) { |
|
|
|
|
1876
|
|
|
$result['changed'] = $files; |
1877
|
|
|
if ($errors) { |
|
|
|
|
1878
|
|
|
$result['warning'] = $this->error($errors); |
1879
|
|
|
} |
1880
|
|
|
} else { |
1881
|
|
|
$result['error'] = $this->error($errors); |
1882
|
|
|
} |
1883
|
|
|
|
1884
|
|
|
return $result; |
1885
|
|
|
} |
1886
|
|
|
|
1887
|
|
|
/** |
1888
|
|
|
* Check chunked upload files |
1889
|
|
|
* |
1890
|
|
|
* @param string $tmpname uploaded temporary file path |
1891
|
|
|
* @param string $chunk uploaded chunk file name |
1892
|
|
|
* @param string $cid uploaded chunked file id |
1893
|
|
|
* @param string $tempDir temporary dirctroy path |
1894
|
|
|
* @param null $volume |
1895
|
|
|
* @return array or (empty, empty) |
1896
|
|
|
* @author Naoki Sawada |
1897
|
|
|
*/ |
1898
|
|
|
private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) { |
|
|
|
|
1899
|
|
|
if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { |
1900
|
|
|
$fname = $m[1]; |
1901
|
|
|
$encname = md5($cid . '_' . $fname); |
1902
|
|
|
$base = $tempDir . DIRECTORY_SEPARATOR . 'ELF' . $encname; |
1903
|
|
|
$clast = intval($m[3]); |
1904
|
|
|
if (is_null($tmpname)) { |
1905
|
|
|
ignore_user_abort(true); |
1906
|
|
|
sleep(10); // wait 10 sec |
1907
|
|
|
// chunked file upload fail |
1908
|
|
|
foreach(glob($base . '*') as $cf) { |
1909
|
|
|
unlink($cf); |
1910
|
|
|
} |
1911
|
|
|
ignore_user_abort(false); |
1912
|
|
|
return; |
1913
|
|
|
} |
1914
|
|
|
|
1915
|
|
|
$range = isset($_POST['range'])? trim($_POST['range']) : ''; |
1916
|
|
|
if ($range && preg_match('/^(\d+),(\d+),(\d+)$/', $range, $ranges)) { |
1917
|
|
|
$start = $ranges[1]; |
1918
|
|
|
$len = $ranges[2]; |
1919
|
|
|
$size = $ranges[3]; |
1920
|
|
|
$tmp = $base . '.part'; |
1921
|
|
|
$csize = filesize($tmpname); |
1922
|
|
|
|
1923
|
|
|
$tmpExists = is_file($tmp); |
1924
|
|
|
if (!$tmpExists) { |
1925
|
|
|
// check upload max size |
1926
|
|
|
$uploadMaxSize = $volume->getUploadMaxSize(); |
|
|
|
|
1927
|
|
|
if ($uploadMaxSize > 0 && $size > $uploadMaxSize) { |
1928
|
|
|
return array(self::ERROR_UPLOAD_FILE_SIZE, false); |
1929
|
|
|
} |
1930
|
|
|
// make temp file |
1931
|
|
|
$ok = false; |
1932
|
|
|
if ($fp = fopen($tmp, 'wb')) { |
1933
|
|
|
flock($fp, LOCK_EX); |
1934
|
|
|
$ok = ftruncate($fp, $size); |
1935
|
|
|
flock($fp, LOCK_UN); |
1936
|
|
|
fclose($fp); |
1937
|
|
|
touch($base); |
1938
|
|
|
} |
1939
|
|
|
if (!$ok) { |
1940
|
|
|
unlink($tmp); |
1941
|
|
|
return array(self::ERROR_UPLOAD_TEMP, false); |
1942
|
|
|
} |
1943
|
|
|
} else { |
1944
|
|
|
// wait until makeing temp file (for anothor session) |
1945
|
|
|
$cnt = 1200; // Time limit 120 sec |
1946
|
|
|
while(!is_file($base) && --$cnt) { |
1947
|
|
|
usleep(100000); // wait 100ms |
1948
|
|
|
} |
1949
|
|
|
if (!$cnt) { |
1950
|
|
|
return array(self::ERROR_UPLOAD_TEMP, false); |
1951
|
|
|
} |
1952
|
|
|
} |
1953
|
|
|
|
1954
|
|
|
// check size info |
1955
|
|
|
if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) { |
1956
|
|
|
return array(self::ERROR_UPLOAD_TEMP, false); |
1957
|
|
|
} |
1958
|
|
|
|
1959
|
|
|
// write chunk data |
1960
|
|
|
$writelen = 0; |
|
|
|
|
1961
|
|
|
$src = fopen($tmpname, 'rb'); |
1962
|
|
|
$fp = fopen($tmp, 'cb'); |
1963
|
|
|
fseek($fp, $start); |
1964
|
|
|
$writelen = stream_copy_to_stream($src, $fp, $len); |
1965
|
|
|
fclose($fp); |
1966
|
|
|
fclose($src); |
1967
|
|
|
if ($writelen != $len) { |
1968
|
|
|
return array(self::ERROR_UPLOAD_TEMP, false); |
1969
|
|
|
} |
1970
|
|
|
|
1971
|
|
|
// write counts |
1972
|
|
|
file_put_contents($base, "\0", FILE_APPEND | LOCK_EX); |
1973
|
|
|
|
1974
|
|
|
if (filesize($base) >= $clast + 1) { |
1975
|
|
|
// Completion |
1976
|
|
|
unlink($base); |
1977
|
|
|
return array($tmp, $fname); |
1978
|
|
|
} |
1979
|
|
|
} else { |
1980
|
|
|
// old way |
1981
|
|
|
$part = $base . $m[2]; |
1982
|
|
|
if (move_uploaded_file($tmpname, $part)) { |
1983
|
|
|
chmod($part, 0600); |
1984
|
|
|
if ($clast < count(glob($base . '*'))) { |
1985
|
|
|
$parts = array(); |
1986
|
|
|
for ($i = 0; $i <= $clast; $i++) { |
1987
|
|
|
$name = $base . '.' . $i . '_' . $clast; |
1988
|
|
|
if (is_readable($name)) { |
1989
|
|
|
$parts[] = $name; |
1990
|
|
|
} else { |
1991
|
|
|
$parts = null; |
1992
|
|
|
break; |
1993
|
|
|
} |
1994
|
|
|
} |
1995
|
|
|
if ($parts) { |
1996
|
|
|
if (!is_file($base)) { |
1997
|
|
|
touch($base); |
1998
|
|
|
if ($resfile = tempnam($tempDir, 'ELF')) { |
1999
|
|
|
$target = fopen($resfile, 'wb'); |
2000
|
|
|
foreach($parts as $f) { |
2001
|
|
|
$fp = fopen($f, 'rb'); |
2002
|
|
|
while (!feof($fp)) { |
2003
|
|
|
fwrite($target, fread($fp, 8192)); |
2004
|
|
|
} |
2005
|
|
|
fclose($fp); |
2006
|
|
|
unlink($f); |
2007
|
|
|
} |
2008
|
|
|
fclose($target); |
2009
|
|
|
unlink($base); |
2010
|
|
|
return array($resfile, $fname); |
2011
|
|
|
} |
2012
|
|
|
unlink($base); |
2013
|
|
|
} |
2014
|
|
|
} |
2015
|
|
|
} |
2016
|
|
|
} |
2017
|
|
|
} |
2018
|
|
|
} |
2019
|
|
|
return array('', ''); |
2020
|
|
|
} |
2021
|
|
|
|
2022
|
|
|
/** |
2023
|
|
|
* Save uploaded files |
2024
|
|
|
* |
2025
|
|
|
* @param array |
2026
|
|
|
* @return array |
2027
|
|
|
* @author Dmitry (dio) Levashov |
2028
|
|
|
**/ |
2029
|
|
|
protected function upload($args) { |
|
|
|
|
2030
|
|
|
$ngReg = '/[\/\\?*:|"<>]/'; |
2031
|
|
|
$target = $args['target']; |
2032
|
|
|
$volume = $this->volume($target); |
2033
|
|
|
$files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array(); |
2034
|
|
|
$header = empty($args['html']) ? array() : array('header' => 'Content-Type: text/html; charset=utf-8'); |
2035
|
|
|
$result = array_merge(array('added' => array()), $header); |
2036
|
|
|
$paths = $args['upload_path']? $args['upload_path'] : array(); |
2037
|
|
|
$chunk = $args['chunk']? $args['chunk'] : ''; |
2038
|
|
|
$cid = $args['cid']? (int)$args['cid'] : ''; |
2039
|
|
|
$mtimes = $args['mtime']? $args['mtime'] : array(); |
2040
|
|
|
|
2041
|
|
|
$renames = $hashes = array(); |
2042
|
|
|
$suffix = '~'; |
2043
|
|
|
if ($args['renames'] && is_array($args['renames'])) { |
2044
|
|
|
$renames = array_flip($args['renames']); |
2045
|
|
|
if (is_string($args['suffix']) && ! preg_match($ngReg, $args['suffix'])) { |
2046
|
|
|
$suffix = $args['suffix']; |
2047
|
|
|
} |
2048
|
|
|
} |
2049
|
|
|
if ($args['hashes'] && is_array($args['hashes'])) { |
2050
|
|
|
$hashes = array_flip($args['hashes']); |
2051
|
|
|
} |
2052
|
|
|
|
2053
|
|
|
if (!$volume) { |
2054
|
|
|
return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target)), $header); |
2055
|
|
|
} |
2056
|
|
|
|
2057
|
|
|
// regist Shutdown function |
2058
|
|
|
$GLOBALS['elFinderTempFiles'] = array(); |
2059
|
|
|
// if (version_compare(PHP_VERSION, '5.3.0', '>=')) { |
|
|
|
|
2060
|
|
|
// $shutdownfunc = function(){ // <- Parse error on PHP < 5.3 ;-( |
2061
|
|
|
// foreach(array_keys($GLOBALS['elFinderTempFiles']) as $f){ |
2062
|
|
|
// unlink($f); |
2063
|
|
|
// } |
2064
|
|
|
// }; |
2065
|
|
|
// } else { |
2066
|
|
|
$shutdownfunc = create_function('', ' |
|
|
|
|
2067
|
|
|
foreach(array_keys($GLOBALS[\'elFinderTempFiles\']) as $f){ |
2068
|
|
|
is_file($f) && unlink($f); |
2069
|
|
|
} |
2070
|
|
|
'); |
2071
|
|
|
// } |
2072
|
|
|
register_shutdown_function($shutdownfunc); |
2073
|
|
|
|
2074
|
|
|
// file extentions table by MIME |
2075
|
|
|
$extTable = array_flip(array_unique($volume->getMimeTable())); |
2076
|
|
|
|
2077
|
|
|
if (empty($files)) { |
2078
|
|
|
|
2079
|
|
|
//--- This part is unnecessary code from 2.1.7 START ---// |
2080
|
|
|
if (!$args['upload'] && $args['name'] && is_array($args['name'])) { |
2081
|
|
|
$error = ''; |
2082
|
|
|
$result['name'] = array(); |
2083
|
|
|
foreach($args['name'] as $_i => $_name) { |
2084
|
|
|
if (!$volume->isUploadableByName($_name)) { |
2085
|
|
|
$error = $this->error(self::ERROR_UPLOAD_FILE, $_name, self::ERROR_UPLOAD_FILE_MIME); |
2086
|
|
|
break; |
2087
|
|
|
} |
2088
|
|
|
$result['name'][$_i] = preg_replace($ngReg, '_', $_name); |
2089
|
|
|
} |
2090
|
|
|
if ($error) { |
2091
|
|
|
$result['error'] = $error; |
2092
|
|
|
return $result; |
2093
|
|
|
} |
2094
|
|
|
$result = array_merge_recursive($result, $this->ls($args)); |
2095
|
|
|
if (empty($result['list'])) { |
2096
|
|
|
$result['name'] = array(); |
2097
|
|
|
} else { |
2098
|
|
|
// It is using the old(<=2.1.6) JavaScript in the new(>2.1.6) back-end? |
2099
|
|
|
unset($result['list']['exists'], $result['list']['hashes']); |
2100
|
|
|
$result['name'] = array_merge(array_intersect($result['name'], $result['list'])); |
2101
|
|
|
} |
2102
|
|
|
return $result; |
2103
|
|
|
} |
2104
|
|
|
//--- This part is unnessesaly code from 2.1.7 END ---// |
2105
|
|
|
|
2106
|
|
|
if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) { |
2107
|
|
|
$names = array(); |
2108
|
|
|
foreach($args['upload'] as $i => $url) { |
2109
|
|
|
// check chunked file upload commit |
2110
|
|
|
if ($args['chunk']) { |
2111
|
|
|
if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') { |
2112
|
|
|
$this->checkChunkedFile(null, $chunk, $cid, $tempDir); |
2113
|
|
|
if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { |
2114
|
|
|
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], self::ERROR_UPLOAD_TRANSFER); |
2115
|
|
|
} |
2116
|
|
|
return $result; |
2117
|
|
|
} else { |
2118
|
|
|
$tmpfname = $tempDir . '/' . $args['chunk']; |
2119
|
|
|
$files['tmp_name'][$i] = $tmpfname; |
2120
|
|
|
$files['name'][$i] = $url; |
2121
|
|
|
$files['error'][$i] = 0; |
2122
|
|
|
$GLOBALS['elFinderTempFiles'][$tmpfname] = true; |
2123
|
|
|
break; |
2124
|
|
|
} |
2125
|
|
|
} |
2126
|
|
|
|
2127
|
|
|
$tmpfname = $tempDir . DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime(true)); |
2128
|
|
|
|
2129
|
|
|
$_name = ''; |
2130
|
|
|
// check is data: |
2131
|
|
|
if (substr($url, 0, 5) === 'data:') { |
2132
|
|
|
list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable); |
2133
|
|
|
} else { |
2134
|
|
|
$fp = fopen($tmpfname, 'wb'); |
2135
|
|
|
$data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp); |
2136
|
|
|
$_POST['overwrite'] = false; |
2137
|
|
|
$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url)); |
2138
|
|
|
// Check `Content-Disposition` response header |
2139
|
|
|
if ($data && ($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) { |
2140
|
|
|
if (preg_match('/filename\*?=(?:([a-zA-Z0-9_-]+?)\'\')?"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { |
2141
|
|
|
$_name = rawurldecode($m[2]); |
2142
|
|
|
if ($m[1] && strtoupper($m[1]) !== 'UTF-8' && function_exists('mb_convert_encoding')) { |
2143
|
|
|
$_name = mb_convert_encoding($_name, 'UTF-8', $m[1]); |
2144
|
|
|
} |
2145
|
|
|
} |
2146
|
|
|
} |
2147
|
|
|
} |
2148
|
|
|
if ($data) { |
2149
|
|
|
if (isset($args['name'][$i])) { |
2150
|
|
|
$_name = $args['name'][$i]; |
2151
|
|
|
} |
2152
|
|
|
if ($_name) { |
2153
|
|
|
$_ext = ''; |
2154
|
|
|
if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) { |
2155
|
|
|
$_ext = $_match[1]; |
2156
|
|
|
} |
2157
|
|
|
if ((is_resource($data) && fclose($data)) || file_put_contents($tmpfname, $data)) { |
2158
|
|
|
$GLOBALS['elFinderTempFiles'][$tmpfname] = true; |
2159
|
|
|
$_name = preg_replace($ngReg, '_', $_name); |
2160
|
|
|
list($_a, $_b) = array_pad(explode('.', $_name, 2), 2, ''); |
2161
|
|
|
if ($_b === '') { |
2162
|
|
|
if ($_ext) { |
2163
|
|
|
rename($tmpfname, $tmpfname . $_ext); |
2164
|
|
|
$tmpfname = $tmpfname . $_ext; |
2165
|
|
|
} |
2166
|
|
|
$_b = $this->detectFileExtension($tmpfname); |
2167
|
|
|
$_name = $_a.$_b; |
2168
|
|
|
} else { |
2169
|
|
|
$_b = '.'.$_b; |
2170
|
|
|
} |
2171
|
|
|
if (isset($names[$_name])) { |
2172
|
|
|
$_name = $_a.'_'.$names[$_name]++.$_b; |
2173
|
|
|
} else { |
2174
|
|
|
$names[$_name] = 1; |
2175
|
|
|
} |
2176
|
|
|
$files['tmp_name'][$i] = $tmpfname; |
2177
|
|
|
$files['name'][$i] = $_name; |
2178
|
|
|
$files['error'][$i] = 0; |
2179
|
|
|
} else { |
2180
|
|
|
unlink($tmpfname); |
2181
|
|
|
} |
2182
|
|
|
} |
2183
|
|
|
} |
2184
|
|
|
} |
2185
|
|
|
} |
2186
|
|
|
if (empty($files)) { |
2187
|
|
|
return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES)), $header); |
2188
|
|
|
} |
2189
|
|
|
} |
2190
|
|
|
|
2191
|
|
|
$addedDirs = array(); |
2192
|
|
|
foreach ($files['name'] as $i => $name) { |
2193
|
|
|
if (($error = $files['error'][$i]) > 0) { |
2194
|
|
|
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER); |
2195
|
|
|
$this->uploadDebug = 'Upload error code: '.$error; |
2196
|
|
|
break; |
2197
|
|
|
} |
2198
|
|
|
|
2199
|
|
|
$tmpname = $files['tmp_name'][$i]; |
2200
|
|
|
$path = ($paths && isset($paths[$i]))? $paths[$i] : ''; |
2201
|
|
|
$mtime = isset($mtimes[$i])? $mtimes[$i] : 0; |
2202
|
|
|
if ($name === 'blob') { |
2203
|
|
|
if ($chunk) { |
2204
|
|
|
if ($tempDir = $this->getTempDir($volume->getTempPath())) { |
2205
|
|
|
list($tmpname, $name) = $this->checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume); |
2206
|
|
|
if ($tmpname) { |
2207
|
|
|
if ($name === false) { |
2208
|
|
|
preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m); |
2209
|
|
|
$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $m[1], $tmpname); |
2210
|
|
|
$result['_chunkfailure'] = true; |
2211
|
|
|
$this->uploadDebug = 'Upload error: ' . $tmpname; |
2212
|
|
|
} else if ($name) { |
2213
|
|
|
$result['_chunkmerged'] = basename($tmpname); |
2214
|
|
|
$result['_name'] = $name; |
2215
|
|
|
$result['_mtime'] = $mtime; |
2216
|
|
|
} |
2217
|
|
|
} |
2218
|
|
|
} else { |
2219
|
|
|
$result['error'] = $this->error(self::ERROR_UPLOAD_FILE, $chunk, self::ERROR_UPLOAD_TRANSFER); |
2220
|
|
|
$this->uploadDebug = 'Upload error: unable open tmp file'; |
2221
|
|
|
} |
2222
|
|
|
return $result; |
2223
|
|
|
} else { |
2224
|
|
|
// for form clipboard with Google Chrome |
2225
|
|
|
$type = $files['type'][$i]; |
2226
|
|
|
$ext = isset($extTable[$type])? '.' . $extTable[$type] : ''; |
2227
|
|
|
$name = substr(md5(basename($tmpname)), 0, 8) . $ext; |
2228
|
|
|
} |
2229
|
|
|
} |
2230
|
|
|
|
2231
|
|
|
// do hook function 'upload.presave' |
2232
|
|
|
if (! empty($this->listeners['upload.presave'])) { |
2233
|
|
|
foreach($this->listeners['upload.presave'] as $handler) { |
2234
|
|
|
call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume)); |
2235
|
|
|
} |
2236
|
|
|
} |
2237
|
|
|
|
2238
|
|
|
if ($mtime) { |
2239
|
|
|
touch($tmpname, $mtime); |
2240
|
|
|
} |
2241
|
|
|
|
2242
|
|
|
if (($fp = fopen($tmpname, 'rb')) == false) { |
2243
|
|
|
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER); |
2244
|
|
|
$this->uploadDebug = 'Upload error: unable open tmp file'; |
2245
|
|
|
if (! is_uploaded_file($tmpname)) { |
2246
|
|
|
if ( unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpfname]); |
2247
|
|
|
continue; |
2248
|
|
|
} |
2249
|
|
|
break; |
2250
|
|
|
} |
2251
|
|
|
$rnres = array(); |
2252
|
|
|
if ($path !== '' && $path !== $target) { |
2253
|
|
|
if ($dir = $volume->dir($path)) { |
2254
|
|
|
$_target = $path; |
2255
|
|
|
if (! isset($addedDirs[$path])) { |
2256
|
|
|
$addedDirs[$path] = true; |
2257
|
|
|
$result['added'][] =$dir; |
2258
|
|
|
} |
2259
|
|
|
} else { |
2260
|
|
|
$result['error'] = $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, 'hash@'.$path); |
2261
|
|
|
break; |
2262
|
|
|
} |
2263
|
|
|
} else { |
2264
|
|
|
$_target = $target; |
2265
|
|
|
// file rename for backup |
2266
|
|
|
if (isset($renames[$name])) { |
2267
|
|
|
$dir = $volume->realpath($_target); |
2268
|
|
|
if (isset($hashes[$name])) { |
2269
|
|
|
$hash = $hashes[$name]; |
2270
|
|
|
} else { |
2271
|
|
|
$hash = $volume->getHash($dir, $name); |
2272
|
|
|
} |
2273
|
|
|
$rnres = $this->rename(array('target' => $hash, 'name' => $volume->uniqueName($dir, $name, $suffix, true, 0))); |
2274
|
|
|
if (!empty($rnres['error'])) { |
2275
|
|
|
$result['warning'] = $rnres['error']; |
2276
|
|
|
break; |
2277
|
|
|
} |
2278
|
|
|
} |
2279
|
|
|
} |
2280
|
|
|
if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname, $hashes)) === false) { |
2281
|
|
|
$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error()); |
2282
|
|
|
fclose($fp); |
2283
|
|
|
if (! is_uploaded_file($tmpname)) { |
2284
|
|
|
if ( unlink($tmpname)) unset($GLOBALS['elFinderTempFiles'][$tmpname]);; |
2285
|
|
|
continue; |
2286
|
|
|
} |
2287
|
|
|
break; |
2288
|
|
|
} |
2289
|
|
|
|
2290
|
|
|
is_resource($fp) && fclose($fp); |
2291
|
|
|
if (! is_uploaded_file($tmpname)){ |
2292
|
|
|
clearstatcache(); |
2293
|
|
|
if (!is_file($tmpname) || unlink($tmpname)) { |
2294
|
|
|
unset($GLOBALS['elFinderTempFiles'][$tmpname]); |
2295
|
|
|
} |
2296
|
|
|
} |
2297
|
|
|
$result['added'][] = $file; |
2298
|
|
|
if ($rnres) { |
|
|
|
|
2299
|
|
|
$result = array_merge_recursive($result, $rnres); |
2300
|
|
|
} |
2301
|
|
|
} |
2302
|
|
|
if ($GLOBALS['elFinderTempFiles']) { |
2303
|
|
|
foreach(array_keys($GLOBALS['elFinderTempFiles']) as $_temp) { |
2304
|
|
|
unlink($_temp); |
2305
|
|
|
} |
2306
|
|
|
} |
2307
|
|
|
$result['removed'] = $volume->removed(); |
2308
|
|
|
|
2309
|
|
|
if (!empty($args['node'])) { |
2310
|
|
|
$result['callback'] = array( |
2311
|
|
|
'node' => $args['node'], |
2312
|
|
|
'bind' => 'upload' |
2313
|
|
|
); |
2314
|
|
|
} |
2315
|
|
|
return $result; |
2316
|
|
|
} |
2317
|
|
|
|
2318
|
|
|
/** |
2319
|
|
|
* Copy/move files into new destination |
2320
|
|
|
* |
2321
|
|
|
* @param array command arguments |
2322
|
|
|
* @return array |
2323
|
|
|
* @author Dmitry (dio) Levashov |
2324
|
|
|
**/ |
2325
|
|
|
protected function paste($args) { |
2326
|
|
|
$dst = $args['dst']; |
2327
|
|
|
$targets = is_array($args['targets']) ? $args['targets'] : array(); |
2328
|
|
|
$cut = !empty($args['cut']); |
2329
|
|
|
$error = $cut ? self::ERROR_MOVE : self::ERROR_COPY; |
2330
|
|
|
$result = array('changed' => array(), 'added' => array(), 'removed' => array()); |
2331
|
|
|
|
2332
|
|
|
if (($dstVolume = $this->volume($dst)) == false) { |
2333
|
|
|
return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst)); |
2334
|
|
|
} |
2335
|
|
|
|
2336
|
|
|
$hashes = $renames = array(); |
2337
|
|
|
$suffix = '~'; |
2338
|
|
|
if (!empty($args['renames'])) { |
2339
|
|
|
$renames = array_flip($args['renames']); |
2340
|
|
|
if (is_string($args['suffix']) && ! preg_match('/[\/\\?*:|"<>]/', $args['suffix'])) { |
2341
|
|
|
$suffix = $args['suffix']; |
2342
|
|
|
} |
2343
|
|
|
} |
2344
|
|
|
if (!empty($args['hashes'])) { |
2345
|
|
|
$hashes = array_flip($args['hashes']); |
2346
|
|
|
} |
2347
|
|
|
|
2348
|
|
|
foreach ($targets as $target) { |
2349
|
|
|
if (($srcVolume = $this->volume($target)) == false) { |
2350
|
|
|
$result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND); |
2351
|
|
|
break; |
2352
|
|
|
} |
2353
|
|
|
|
2354
|
|
|
$rnres = array(); |
2355
|
|
|
if ($renames) { |
|
|
|
|
2356
|
|
|
$file = $srcVolume->file($target); |
2357
|
|
|
if (isset($renames[$file['name']])) { |
2358
|
|
|
$dir = $dstVolume->realpath($dst); |
2359
|
|
|
if (isset($hashes[$file['name']])) { |
2360
|
|
|
$hash = $hashes[$file['name']]; |
2361
|
|
|
} else { |
2362
|
|
|
$hash = $dstVolume->getHash($dir, $file['name']); |
2363
|
|
|
} |
2364
|
|
|
$rnres = $this->rename(array('target' => $hash, 'name' => $dstVolume->uniqueName($dir, $file['name'], $suffix, true, 0))); |
2365
|
|
|
if (!empty($rnres['error'])) { |
2366
|
|
|
$result['warning'] = $rnres['error']; |
2367
|
|
|
break; |
2368
|
|
|
} |
2369
|
|
|
} |
2370
|
|
|
} |
2371
|
|
|
|
2372
|
|
|
if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut, $hashes)) == false) { |
2373
|
|
|
$result['warning'] = $this->error($dstVolume->error()); |
2374
|
|
|
break; |
2375
|
|
|
} |
2376
|
|
|
|
2377
|
|
|
$dirChange = ! empty($file['dirChange']); |
2378
|
|
|
unset($file['dirChange']); |
2379
|
|
|
if ($dirChange) { |
2380
|
|
|
$result['changed'][] = $file; |
2381
|
|
|
} else { |
2382
|
|
|
$result['added'][] = $file; |
2383
|
|
|
} |
2384
|
|
|
if ($rnres) { |
|
|
|
|
2385
|
|
|
$result = array_merge_recursive($result, $rnres); |
2386
|
|
|
} |
2387
|
|
|
} |
2388
|
|
|
return $result; |
2389
|
|
|
} |
2390
|
|
|
|
2391
|
|
|
/** |
2392
|
|
|
* Return file content |
2393
|
|
|
* |
2394
|
|
|
* @param array $args command arguments |
2395
|
|
|
* @return array |
2396
|
|
|
* @author Dmitry (dio) Levashov |
2397
|
|
|
**/ |
2398
|
|
|
protected function get($args) { |
2399
|
|
|
$target = $args['target']; |
2400
|
|
|
$volume = $this->volume($target); |
2401
|
|
|
|
2402
|
|
|
if (!$volume || ($file = $volume->file($target)) == false) { |
2403
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND)); |
2404
|
|
|
} |
2405
|
|
|
|
2406
|
|
|
if (($content = $volume->getContents($target)) === false) { |
2407
|
|
|
return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error())); |
2408
|
|
|
} |
2409
|
|
|
|
2410
|
|
|
if ($args['conv'] && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) { |
2411
|
|
|
$mime = isset($file['mime'])? $file['mime'] : ''; |
2412
|
|
|
if ($mime && strtolower(substr($mime, 0, 4)) === 'text') { |
2413
|
|
|
if ($enc = mb_detect_encoding ( $content , mb_detect_order(), true)) { |
2414
|
|
|
if (strtolower($enc) !== 'utf-8') { |
2415
|
|
|
$content = mb_convert_encoding($content, 'UTF-8', $enc); |
2416
|
|
|
} |
2417
|
|
|
} |
2418
|
|
|
} |
2419
|
|
|
} |
2420
|
|
|
|
2421
|
|
|
$json = json_encode($content); |
2422
|
|
|
|
2423
|
|
|
if ($json === false || strlen($json) < strlen($content)) { |
2424
|
|
|
if ($args['conv']) { |
2425
|
|
|
return array('error' => $this->error(self::ERROR_CONV_UTF8,self::ERROR_NOT_UTF8_CONTENT, $volume->path($target))); |
2426
|
|
|
} else { |
2427
|
|
|
return array('doconv' => true); |
2428
|
|
|
} |
2429
|
|
|
} |
2430
|
|
|
|
2431
|
|
|
return array('content' => $content); |
2432
|
|
|
} |
2433
|
|
|
|
2434
|
|
|
/** |
2435
|
|
|
* Save content into text file |
2436
|
|
|
* |
2437
|
|
|
* @param $args |
2438
|
|
|
* @return array |
2439
|
|
|
* @author Dmitry (dio) Levashov |
2440
|
|
|
*/ |
2441
|
|
|
protected function put($args) { |
2442
|
|
|
$target = $args['target']; |
2443
|
|
|
|
2444
|
|
|
if (($volume = $this->volume($target)) == false |
2445
|
|
|
|| ($file = $volume->file($target)) == false) { |
2446
|
|
|
return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); |
2447
|
|
|
} |
2448
|
|
|
|
2449
|
|
|
if (($file = $volume->putContents($target, $args['content'])) == false) { |
2450
|
|
|
return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error())); |
2451
|
|
|
} |
2452
|
|
|
|
2453
|
|
|
return array('changed' => array($file)); |
2454
|
|
|
} |
2455
|
|
|
|
2456
|
|
|
/** |
2457
|
|
|
* Extract files from archive |
2458
|
|
|
* |
2459
|
|
|
* @param array $args command arguments |
2460
|
|
|
* @return array |
2461
|
|
|
* @author Dmitry (dio) Levashov, |
2462
|
|
|
* @author Alexey Sukhotin |
2463
|
|
|
**/ |
2464
|
|
|
protected function extract($args) { |
2465
|
|
|
$target = $args['target']; |
2466
|
|
|
$mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array(); |
|
|
|
|
2467
|
|
|
$error = array(self::ERROR_EXTRACT, '#'.$target); |
|
|
|
|
2468
|
|
|
$makedir = isset($args['makedir'])? (bool)$args['makedir'] : null; |
2469
|
|
|
|
2470
|
|
|
if (($volume = $this->volume($target)) == false |
2471
|
|
|
|| ($file = $volume->file($target)) == false) { |
2472
|
|
|
return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND)); |
2473
|
|
|
} |
2474
|
|
|
|
2475
|
|
|
return ($file = $volume->extract($target, $makedir)) |
2476
|
|
|
? array('added' => isset($file['read'])? array($file) : $file) |
2477
|
|
|
: array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error())); |
2478
|
|
|
} |
2479
|
|
|
|
2480
|
|
|
/** |
2481
|
|
|
* Create archive |
2482
|
|
|
* |
2483
|
|
|
* @param array $args command arguments |
2484
|
|
|
* @return array |
2485
|
|
|
* @author Dmitry (dio) Levashov, |
2486
|
|
|
* @author Alexey Sukhotin |
2487
|
|
|
**/ |
2488
|
|
|
protected function archive($args) { |
2489
|
|
|
$type = $args['type']; |
|
|
|
|
2490
|
|
|
$targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array(); |
2491
|
|
|
$name = isset($args['name'])? $args['name'] : ''; |
2492
|
|
|
|
2493
|
|
|
if (($volume = $this->volume($targets[0])) == false) { |
2494
|
|
|
return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND); |
2495
|
|
|
} |
2496
|
|
|
|
2497
|
|
|
return ($file = $volume->archive($targets, $args['type'], $name)) |
2498
|
|
|
? array('added' => array($file)) |
2499
|
|
|
: array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error())); |
2500
|
|
|
} |
2501
|
|
|
|
2502
|
|
|
/** |
2503
|
|
|
* Search files |
2504
|
|
|
* |
2505
|
|
|
* @param array $args command arguments |
2506
|
|
|
* @return array |
2507
|
|
|
* @author Dmitry Levashov |
2508
|
|
|
**/ |
2509
|
|
|
protected function search($args) { |
2510
|
|
|
$q = trim($args['q']); |
2511
|
|
|
$mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array(); |
2512
|
|
|
$target = !empty($args['target'])? $args['target'] : null; |
2513
|
|
|
$result = array(); |
2514
|
|
|
$errors = array(); |
2515
|
|
|
|
2516
|
|
|
if ($target) { |
2517
|
|
|
if ($volume = $this->volume($target)) { |
2518
|
|
|
$result = $volume->search($q, $mimes, $target); |
2519
|
|
|
$errors = array_merge($errors, $volume->error()); |
2520
|
|
|
} |
2521
|
|
|
} else { |
2522
|
|
|
foreach ($this->volumes as $volume) { |
2523
|
|
|
$result = array_merge($result, $volume->search($q, $mimes)); |
2524
|
|
|
$errors = array_merge($errors, $volume->error()); |
2525
|
|
|
} |
2526
|
|
|
} |
2527
|
|
|
|
2528
|
|
|
$result = array('files' => $result); |
2529
|
|
|
if ($errors) { |
|
|
|
|
2530
|
|
|
$result['warning'] = $errors; |
2531
|
|
|
} |
2532
|
|
|
return $result; |
2533
|
|
|
} |
2534
|
|
|
|
2535
|
|
|
/** |
2536
|
|
|
* Return file info (used by client "places" ui) |
2537
|
|
|
* |
2538
|
|
|
* @param array $args command arguments |
2539
|
|
|
* @return array |
2540
|
|
|
* @author Dmitry Levashov |
2541
|
|
|
**/ |
2542
|
|
|
protected function info($args) { |
2543
|
|
|
$files = array(); |
2544
|
|
|
$sleep = 0; |
|
|
|
|
2545
|
|
|
$compare = null; |
2546
|
|
|
// long polling mode |
2547
|
|
|
if ($args['compare'] && count($args['targets']) === 1) { |
2548
|
|
|
$compare = intval($args['compare']); |
2549
|
|
|
$hash = $args['targets'][0]; |
2550
|
|
|
if ($volume = $this->volume($hash)) { |
2551
|
|
|
$standby = (int)$volume->getOption('plStandby'); |
2552
|
|
|
$_compare = false; |
2553
|
|
|
if (($syncCheckFunc = $volume->getOption('syncCheckFunc')) && is_callable($syncCheckFunc)) { |
2554
|
|
|
$_compare = call_user_func_array($syncCheckFunc, array($volume->realpath($hash), $standby, $compare, $volume, $this)); |
2555
|
|
|
} |
2556
|
|
|
if ($_compare !== false) { |
2557
|
|
|
$compare = $_compare; |
2558
|
|
|
} else { |
2559
|
|
|
$sleep = max(1, (int)$volume->getOption('tsPlSleep')); |
2560
|
|
|
$limit = max(1, $standby / $sleep) + 1; |
2561
|
|
|
do { |
2562
|
|
|
elFinder::extendTimeLimit(30 + $sleep); |
2563
|
|
|
$volume->clearstatcache(); |
2564
|
|
|
if (($info = $volume->file($hash)) != false) { |
2565
|
|
|
if ($info['ts'] != $compare) { |
2566
|
|
|
$compare = $info['ts']; |
2567
|
|
|
break; |
2568
|
|
|
} |
2569
|
|
|
} else { |
2570
|
|
|
$compare = 0; |
2571
|
|
|
break; |
2572
|
|
|
} |
2573
|
|
|
if (--$limit) { |
2574
|
|
|
sleep($sleep); |
2575
|
|
|
} |
2576
|
|
|
} while($limit); |
2577
|
|
|
} |
2578
|
|
|
} |
2579
|
|
|
} else { |
2580
|
|
|
foreach ($args['targets'] as $hash) { |
2581
|
|
|
if (($volume = $this->volume($hash)) != false |
2582
|
|
|
&& ($info = $volume->file($hash)) != false) { |
2583
|
|
|
$info['path'] = $volume->path($hash); |
2584
|
|
|
$files[] = $info; |
2585
|
|
|
} |
2586
|
|
|
} |
2587
|
|
|
} |
2588
|
|
|
|
2589
|
|
|
$result = array('files' => $files); |
2590
|
|
|
if (!is_null($compare)) { |
2591
|
|
|
$result['compare'] = strval($compare); |
2592
|
|
|
} |
2593
|
|
|
return $result; |
2594
|
|
|
} |
2595
|
|
|
|
2596
|
|
|
/** |
2597
|
|
|
* Return image dimensions |
2598
|
|
|
* |
2599
|
|
|
* @param array $args command arguments |
2600
|
|
|
* @return array |
2601
|
|
|
* @author Dmitry (dio) Levashov |
2602
|
|
|
**/ |
2603
|
|
|
protected function dim($args) { |
2604
|
|
|
$target = $args['target']; |
2605
|
|
|
|
2606
|
|
|
if (($volume = $this->volume($target)) != false) { |
2607
|
|
|
$dim = $volume->dimensions($target); |
2608
|
|
|
return $dim ? array('dim' => $dim) : array(); |
2609
|
|
|
} |
2610
|
|
|
return array(); |
2611
|
|
|
} |
2612
|
|
|
|
2613
|
|
|
/** |
2614
|
|
|
* Resize image |
2615
|
|
|
* |
2616
|
|
|
* @param array command arguments |
2617
|
|
|
* @return array |
2618
|
|
|
* @author Dmitry (dio) Levashov |
2619
|
|
|
* @author Alexey Sukhotin |
2620
|
|
|
**/ |
2621
|
|
|
protected function resize($args) { |
2622
|
|
|
$target = $args['target']; |
2623
|
|
|
$width = $args['width']; |
2624
|
|
|
$height = $args['height']; |
2625
|
|
|
$x = (int)$args['x']; |
2626
|
|
|
$y = (int)$args['y']; |
2627
|
|
|
$mode = $args['mode']; |
2628
|
|
|
$bg = null; |
2629
|
|
|
$degree = (int)$args['degree']; |
2630
|
|
|
$quality= (int)$args['quality']; |
2631
|
|
|
|
2632
|
|
|
if (($volume = $this->volume($target)) == false |
2633
|
|
|
|| ($file = $volume->file($target)) == false) { |
2634
|
|
|
return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); |
2635
|
|
|
} |
2636
|
|
|
|
2637
|
|
|
return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree, $quality)) |
2638
|
|
|
? array('changed' => array($file)) |
2639
|
|
|
: array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error())); |
2640
|
|
|
} |
2641
|
|
|
|
2642
|
|
|
/** |
2643
|
|
|
* Return content URL |
2644
|
|
|
* |
2645
|
|
|
* @param array $args command arguments |
2646
|
|
|
* @return array |
2647
|
|
|
* @author Naoki Sawada |
2648
|
|
|
**/ |
2649
|
|
|
protected function url($args) { |
2650
|
|
|
$target = $args['target']; |
2651
|
|
|
$options = isset($args['options'])? $args['options'] : array(); |
2652
|
|
|
if (($volume = $this->volume($target)) != false) { |
2653
|
|
|
$url = $volume->getContentUrl($target, $options); |
2654
|
|
|
return $url ? array('url' => $url) : array(); |
2655
|
|
|
} |
2656
|
|
|
return array(); |
2657
|
|
|
} |
2658
|
|
|
|
2659
|
|
|
/** |
2660
|
|
|
* Output callback result with JavaScript that control elFinder |
2661
|
|
|
* or HTTP redirect to callbackWindowURL |
2662
|
|
|
* |
2663
|
|
|
* @param array command arguments |
2664
|
|
|
* @author Naoki Sawada |
2665
|
|
|
*/ |
2666
|
|
|
protected function callback($args) { |
2667
|
|
|
$checkReg = '/[^a-zA-Z0-9;._-]/'; |
2668
|
|
|
$node = (isset($args['node']) && !preg_match($checkReg, $args['node']))? $args['node'] : ''; |
2669
|
|
|
$json = (isset($args['json']) && json_decode($args['json']))? $args['json'] : '{}'; |
2670
|
|
|
$bind = (isset($args['bind']) && !preg_match($checkReg, $args['bind']))? $args['bind'] : ''; |
2671
|
|
|
$done = (!empty($args['done'])); |
2672
|
|
|
|
2673
|
|
|
while( ob_get_level() ) { |
2674
|
|
|
if (! ob_end_clean()) { |
2675
|
|
|
break; |
2676
|
|
|
} |
2677
|
|
|
} |
2678
|
|
|
|
2679
|
|
|
if ($done || ! $this->callbackWindowURL) { |
2680
|
|
|
$script = ''; |
2681
|
|
|
if ($node) { |
2682
|
|
|
$script .= ' |
2683
|
|
|
var w = window.opener || window.parent || window; |
2684
|
|
|
try { |
2685
|
|
|
var elf = w.document.getElementById(\''.$node.'\').elfinder; |
2686
|
|
|
if (elf) { |
2687
|
|
|
var data = '.$json.'; |
2688
|
|
|
if (data.error) { |
2689
|
|
|
elf.error(data.error); |
2690
|
|
|
} else { |
2691
|
|
|
data.warning && elf.error(data.warning); |
2692
|
|
|
data.removed && data.removed.length && elf.remove(data); |
2693
|
|
|
data.added && data.added.length && elf.add(data); |
2694
|
|
|
data.changed && data.changed.length && elf.change(data);'; |
2695
|
|
|
if ($bind) { |
2696
|
|
|
$script .= ' |
2697
|
|
|
elf.trigger(\''.$bind.'\', data);'; |
2698
|
|
|
} |
2699
|
|
|
$script .= ' |
2700
|
|
|
data.sync && elf.sync(); |
2701
|
|
|
} |
2702
|
|
|
} |
2703
|
|
|
} catch(e) { |
2704
|
|
|
// for CORS |
2705
|
|
|
w.postMessage && w.postMessage(JSON.stringify({bind:\''.$bind.'\',data:'.$json.'}), \'*\'); |
2706
|
|
|
}'; |
2707
|
|
|
} |
2708
|
|
|
$script .= 'window.close();'; |
2709
|
|
|
|
2710
|
|
|
$out = '<!DOCTYPE html><html><head><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><script>'.$script.'</script></head><body><a href="#" onlick="window.close();return false;">Close this window</a></body></html>'; |
2711
|
|
|
|
2712
|
|
|
header('Content-Type: text/html; charset=utf-8'); |
2713
|
|
|
header('Content-Length: '.strlen($out)); |
2714
|
|
|
header('Cache-Control: private'); |
2715
|
|
|
header('Pragma: no-cache'); |
2716
|
|
|
|
2717
|
|
|
echo $out; |
2718
|
|
|
|
2719
|
|
|
} else { |
2720
|
|
|
$url = $this->callbackWindowURL; |
2721
|
|
|
$url .= ((strpos($url, '?') === false)? '?' : '&') |
2722
|
|
|
. '&node=' . rawurlencode($node) |
2723
|
|
|
. (($json !== '{}')? ('&json=' . rawurlencode($json)) : '') |
2724
|
|
|
. ($bind? ('&bind=' . rawurlencode($bind)) : '') |
2725
|
|
|
. '&done=1'; |
2726
|
|
|
|
2727
|
|
|
header('Location: ' . $url); |
2728
|
|
|
|
2729
|
|
|
} |
2730
|
|
|
exit(); |
|
|
|
|
2731
|
|
|
} |
2732
|
|
|
|
2733
|
|
|
/** |
2734
|
|
|
* PHP error handler, catch error types only E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE |
2735
|
|
|
* |
2736
|
|
|
* @param int $errno |
2737
|
|
|
* @param string $errstr |
2738
|
|
|
* @param string $errfile |
2739
|
|
|
* @param int $errline |
2740
|
|
|
* @return void|boolean |
2741
|
|
|
*/ |
2742
|
|
|
public static function phpErrorHandler($errno, $errstr, $errfile, $errline) { |
2743
|
|
|
static $base = null; |
2744
|
|
|
|
2745
|
|
|
if (is_null($base)) { |
2746
|
|
|
$base = dirname(__FILE__) . DIRECTORY_SEPARATOR; |
2747
|
|
|
} |
2748
|
|
|
|
2749
|
|
|
if (! (error_reporting() & $errno)) { |
2750
|
|
|
return; |
2751
|
|
|
} |
2752
|
|
|
|
2753
|
|
|
$errfile = str_replace($base, '', $errfile); |
2754
|
|
|
|
2755
|
|
|
$proc = false; |
2756
|
|
|
switch ($errno) { |
2757
|
|
|
case E_WARNING: |
2758
|
|
|
case E_USER_WARNING: |
2759
|
|
|
elFinder::$phpErrors[] = "WARNING: $errstr in $errfile line $errline."; |
2760
|
|
|
$proc = true; |
2761
|
|
|
break; |
2762
|
|
|
|
2763
|
|
|
case E_NOTICE: |
2764
|
|
|
case E_USER_NOTICE: |
2765
|
|
|
elFinder::$phpErrors[] = "NOTICE: $errstr in $errfile line $errline."; |
2766
|
|
|
$proc = true; |
2767
|
|
|
break; |
2768
|
|
|
} |
2769
|
|
|
|
2770
|
|
|
return $proc; |
2771
|
|
|
} |
2772
|
|
|
|
2773
|
|
|
/***************************************************************************/ |
2774
|
|
|
/* utils */ |
2775
|
|
|
/***************************************************************************/ |
2776
|
|
|
|
2777
|
|
|
/** |
2778
|
|
|
* Return root - file's owner |
2779
|
|
|
* |
2780
|
|
|
* @param string file hash |
2781
|
|
|
* @return elFinderStorageDriver |
2782
|
|
|
* @author Dmitry (dio) Levashov |
2783
|
|
|
**/ |
2784
|
|
|
protected function volume($hash) { |
2785
|
|
|
foreach ($this->volumes as $id => $v) { |
2786
|
|
|
if (strpos(''.$hash, $id) === 0) { |
2787
|
|
|
return $this->volumes[$id]; |
2788
|
|
|
} |
2789
|
|
|
} |
2790
|
|
|
return false; |
2791
|
|
|
} |
2792
|
|
|
|
2793
|
|
|
/** |
2794
|
|
|
* Return files info array |
2795
|
|
|
* |
2796
|
|
|
* @param array $data one file info or files info |
2797
|
|
|
* @return array |
2798
|
|
|
* @author Dmitry (dio) Levashov |
2799
|
|
|
**/ |
2800
|
|
|
protected function toArray($data) { |
2801
|
|
|
return isset($data['hash']) || !is_array($data) ? array($data) : $data; |
2802
|
|
|
} |
2803
|
|
|
|
2804
|
|
|
/** |
2805
|
|
|
* Return fils hashes list |
2806
|
|
|
* |
2807
|
|
|
* @param array $files files info |
2808
|
|
|
* @return array |
2809
|
|
|
* @author Dmitry (dio) Levashov |
2810
|
|
|
**/ |
2811
|
|
|
protected function hashes($files) { |
2812
|
|
|
$ret = array(); |
2813
|
|
|
foreach ($files as $file) { |
2814
|
|
|
$ret[] = $file['hash']; |
2815
|
|
|
} |
2816
|
|
|
return $ret; |
2817
|
|
|
} |
2818
|
|
|
|
2819
|
|
|
/** |
2820
|
|
|
* Remove from files list hidden files and files with required mime types |
2821
|
|
|
* |
2822
|
|
|
* @param array $files files info |
2823
|
|
|
* @return array |
2824
|
|
|
* @author Dmitry (dio) Levashov |
2825
|
|
|
**/ |
2826
|
|
|
protected function filter($files) { |
2827
|
|
|
$exists = array(); |
2828
|
|
|
foreach ($files as $i => $file) { |
2829
|
|
|
if (isset($exists[$file['hash']]) || !empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) { |
2830
|
|
|
unset($files[$i]); |
2831
|
|
|
} |
2832
|
|
|
$exists[$file['hash']] = true; |
2833
|
|
|
} |
2834
|
|
|
return array_values($files); |
2835
|
|
|
} |
2836
|
|
|
|
2837
|
|
|
protected function utime() { |
2838
|
|
|
$time = explode(" ", microtime()); |
2839
|
|
|
return (double)$time[1] + (double)$time[0]; |
2840
|
|
|
} |
2841
|
|
|
|
2842
|
|
|
/** |
2843
|
|
|
* Return Network mount volume unique ID |
2844
|
|
|
* |
2845
|
|
|
* @param array $netVolumes Saved netvolumes array |
2846
|
|
|
* @param string $prefix Id prefix |
2847
|
|
|
* @return string|false |
2848
|
|
|
* @author Naoki Sawada |
2849
|
|
|
**/ |
2850
|
|
|
protected function getNetVolumeUniqueId($netVolumes = null, $prefix = 'nm') { |
2851
|
|
|
$id = false; |
|
|
|
|
2852
|
|
|
if (is_null($netVolumes)) { |
2853
|
|
|
$netVolumes = $this->getNetVolumes(); |
2854
|
|
|
} |
2855
|
|
|
$ids = array(); |
2856
|
|
|
foreach($netVolumes as $vOps) { |
2857
|
|
|
if (isset($vOps['id']) && strpos($vOps['id'], $prefix) === 0) { |
2858
|
|
|
$ids[$vOps['id']] = true; |
2859
|
|
|
} |
2860
|
|
|
} |
2861
|
|
|
if (! $ids) { |
|
|
|
|
2862
|
|
|
$id = $prefix.'1'; |
2863
|
|
|
} else { |
2864
|
|
|
$i = 0; |
2865
|
|
|
while(isset($ids[$prefix.++$i]) && $i < 10000); |
2866
|
|
|
$id = $prefix.$i; |
2867
|
|
|
if (isset($ids[$id])) { |
2868
|
|
|
$id = false; |
2869
|
|
|
} |
2870
|
|
|
} |
2871
|
|
|
return $id; |
2872
|
|
|
} |
2873
|
|
|
|
2874
|
|
|
/** |
2875
|
|
|
* Ensure directories recursively |
2876
|
|
|
* |
2877
|
|
|
* @param object $volume Volume object |
2878
|
|
|
* @param string $target Target hash |
2879
|
|
|
* @param string $dirs Array of directory tree to ensure |
2880
|
|
|
* @param string $path Relative path form target hash |
2881
|
|
|
* @return array|false array('stats' => array([stat of maked directory]), 'hashes' => array('[path]' => '[hash]')) |
2882
|
|
|
* @author Naoki Sawada |
2883
|
|
|
**/ |
2884
|
|
|
protected function ensureDirsRecursively($volume, $target, $dirs, $path = '') { |
2885
|
|
|
$res = array('stats' => array(), 'hashes' => array()); |
2886
|
|
|
foreach($dirs as $name => $sub) { |
|
|
|
|
2887
|
|
|
$name = (string)$name; |
2888
|
|
|
if ((($parent = $volume->realpath($target)) && ($dir = $volume->dir($volume->getHash($parent, $name)))) || ($dir = $volume->mkdir($target, $name))) { |
2889
|
|
|
$_path = $path . '/' . $name; |
2890
|
|
|
$res['stats'][] = $dir; |
2891
|
|
|
$res['hashes'][$_path] = $dir['hash']; |
2892
|
|
|
if (count($sub)) { |
2893
|
|
|
if ($subRes = $this->ensureDirsRecursively($volume, $dir['hash'], $sub, $_path)) { |
2894
|
|
|
$res = array_merge_recursive($res, $subRes); |
2895
|
|
|
} else { |
2896
|
|
|
return false; |
2897
|
|
|
} |
2898
|
|
|
} |
2899
|
|
|
} else { |
2900
|
|
|
return false; |
2901
|
|
|
} |
2902
|
|
|
} |
2903
|
|
|
return $res; |
2904
|
|
|
} |
2905
|
|
|
|
2906
|
|
|
/***************************************************************************/ |
2907
|
|
|
/* static utils */ |
2908
|
|
|
/***************************************************************************/ |
2909
|
|
|
|
2910
|
|
|
/** |
2911
|
|
|
* Return Is Animation Gif |
2912
|
|
|
* |
2913
|
|
|
* @param string $path server local path of target image |
2914
|
|
|
* @return bool |
2915
|
|
|
*/ |
2916
|
|
|
public static function isAnimationGif($path) { |
2917
|
|
|
list($width, $height, $type, $attr) = getimagesize($path); |
|
|
|
|
2918
|
|
|
switch ($type) { |
2919
|
|
|
case IMAGETYPE_GIF: |
2920
|
|
|
break; |
2921
|
|
|
default: |
2922
|
|
|
return false; |
2923
|
|
|
} |
2924
|
|
|
|
2925
|
|
|
$imgcnt = 0; |
2926
|
|
|
$fp = fopen($path, 'rb'); |
2927
|
|
|
fread($fp, 4); |
2928
|
|
|
$c = fread($fp,1); |
2929
|
|
|
if (ord($c) != 0x39) { // GIF89a |
2930
|
|
|
return false; |
2931
|
|
|
} |
2932
|
|
|
|
2933
|
|
|
while (!feof($fp)) { |
2934
|
|
|
do { |
2935
|
|
|
$c = fread($fp, 1); |
2936
|
|
|
} while(ord($c) != 0x21 && !feof($fp)); |
2937
|
|
|
|
2938
|
|
|
if (feof($fp)) { |
2939
|
|
|
break; |
2940
|
|
|
} |
2941
|
|
|
|
2942
|
|
|
$c2 = fread($fp,2); |
2943
|
|
|
if (bin2hex($c2) == "f904") { |
2944
|
|
|
$imgcnt++; |
2945
|
|
|
} |
2946
|
|
|
|
2947
|
|
|
if (feof($fp)) { |
2948
|
|
|
break; |
2949
|
|
|
} |
2950
|
|
|
} |
2951
|
|
|
|
2952
|
|
|
if ($imgcnt > 1) { |
2953
|
|
|
return true; |
2954
|
|
|
} else { |
2955
|
|
|
return false; |
2956
|
|
|
} |
2957
|
|
|
} |
2958
|
|
|
|
2959
|
|
|
/** |
2960
|
|
|
* Return Is seekable stream resource |
2961
|
|
|
* |
2962
|
|
|
* @param resource $resource |
2963
|
|
|
* @return bool |
2964
|
|
|
*/ |
2965
|
|
|
public static function isSeekableStream($resource) { |
2966
|
|
|
$metadata = stream_get_meta_data($resource); |
2967
|
|
|
return $metadata['seekable']; |
2968
|
|
|
} |
2969
|
|
|
|
2970
|
|
|
/** |
2971
|
|
|
* Rewind stream resource |
2972
|
|
|
* |
2973
|
|
|
* @param resource $resource |
2974
|
|
|
* @return void |
2975
|
|
|
*/ |
2976
|
|
|
public static function rewind($resource) { |
2977
|
|
|
self::isSeekableStream($resource) && rewind($resource); |
2978
|
|
|
} |
2979
|
|
|
|
2980
|
|
|
/** |
2981
|
|
|
* serialize and base64_encode of session data (If needed) |
2982
|
|
|
* |
2983
|
|
|
* @deprecated |
2984
|
|
|
* @param mixed $var target variable |
2985
|
|
|
* @author Naoki Sawada |
2986
|
|
|
* @return mixed|string |
2987
|
|
|
*/ |
2988
|
|
|
public static function sessionDataEncode($var) { |
2989
|
|
|
if (self::$base64encodeSessionData) { |
2990
|
|
|
$var = base64_encode(serialize($var)); |
2991
|
|
|
} |
2992
|
|
|
return $var; |
2993
|
|
|
} |
2994
|
|
|
|
2995
|
|
|
/** |
2996
|
|
|
* base64_decode and unserialize of session data (If needed) |
2997
|
|
|
* |
2998
|
|
|
* @deprecated |
2999
|
|
|
* @param mixed $var target variable |
3000
|
|
|
* @param bool $checkIs data type for check (array|string|object|int) |
3001
|
|
|
* @author Naoki Sawada |
3002
|
|
|
* @return bool|mixed |
3003
|
|
|
*/ |
3004
|
|
|
public static function sessionDataDecode(&$var, $checkIs = null) { |
3005
|
|
|
if (self::$base64encodeSessionData) { |
3006
|
|
|
$data = unserialize(base64_decode($var)); |
3007
|
|
|
} else { |
3008
|
|
|
$data = $var; |
3009
|
|
|
} |
3010
|
|
|
$chk = true; |
3011
|
|
|
if ($checkIs) { |
3012
|
|
|
switch ($checkIs) { |
3013
|
|
|
case 'array': |
3014
|
|
|
$chk = is_array($data); |
3015
|
|
|
break; |
3016
|
|
|
case 'string': |
3017
|
|
|
$chk = is_string($data); |
3018
|
|
|
break; |
3019
|
|
|
case 'object': |
3020
|
|
|
$chk = is_object($data); |
3021
|
|
|
break; |
3022
|
|
|
case 'int': |
3023
|
|
|
$chk = is_int($data); |
3024
|
|
|
break; |
3025
|
|
|
} |
3026
|
|
|
} |
3027
|
|
|
if (!$chk) { |
3028
|
|
|
unset($var); |
3029
|
|
|
return false; |
3030
|
|
|
} |
3031
|
|
|
return $data; |
3032
|
|
|
} |
3033
|
|
|
|
3034
|
|
|
/** |
3035
|
|
|
* Call session_write_close() if session is restarted |
3036
|
|
|
* |
3037
|
|
|
* @deprecated |
3038
|
|
|
* @return void |
3039
|
|
|
*/ |
3040
|
|
|
public static function sessionWrite() { |
3041
|
|
|
if (session_id()) { |
3042
|
|
|
session_write_close(); |
3043
|
|
|
} |
3044
|
|
|
} |
3045
|
|
|
|
3046
|
|
|
/** |
3047
|
|
|
* Return elFinder static variable |
3048
|
|
|
* |
3049
|
|
|
* @param $key |
3050
|
|
|
* @return mixed|null |
3051
|
|
|
*/ |
3052
|
|
|
public static function getStaticVar($key) { |
3053
|
|
|
return isset(elFinder::$$key)? elFinder::$$key : null; |
3054
|
|
|
} |
3055
|
|
|
|
3056
|
|
|
/** |
3057
|
|
|
* Extend PHP execution time limit |
3058
|
|
|
* |
3059
|
|
|
* @param Int $time |
3060
|
|
|
* @return void |
3061
|
|
|
*/ |
3062
|
|
|
public static function extendTimeLimit($time = null) { |
3063
|
|
|
static $defLimit = null; |
3064
|
|
|
if (is_null($defLimit)) { |
3065
|
|
|
$defLimit = ini_get('max_execution_time'); |
3066
|
|
|
} |
3067
|
|
|
if ($defLimit != 0) { |
3068
|
|
|
$time = is_null($time)? $defLimit : max($defLimit, $time); |
3069
|
|
|
set_time_limit($time); |
3070
|
|
|
} |
3071
|
|
|
} |
3072
|
|
|
|
3073
|
|
|
} // END class |
3074
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.