fdafa0943eeb108ae13c1bb5c76e197a3af89022
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Utility / File / ExtendedFileUtility.php
1 <?php
2 namespace TYPO3\CMS\Core\Utility\File;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Extending class to class t3lib_basicFileFunctions
31 *
32 * Revised for TYPO3 3.6 May/2004 by Kasper Skårhøj
33 *
34 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
35 */
36 /**
37 * Contains functions for performing file operations like copying, pasting, uploading, moving, deleting etc. through the TCE
38 * Extending class to class t3lib_basicFileFunctions.
39 *
40 * see basicFileFunctions
41 * see document "TYPO3 Core API" for syntax
42 *
43 * This class contains functions primarily used by tce_file.php (TYPO3 Core Engine for file manipulation)
44 * Functions include copying, moving, deleting, uploading and so on...
45 *
46 * Important internal variables:
47 *
48 * $filemounts (see basicFileFunctions)
49 * $f_ext (see basicFileFunctions)
50 * ... All fileoperations must be within the filemount-paths. Further the fileextension MUST validate TRUE with the f_ext array
51 *
52 * The unzip-function allows unzip only if the destination path has it's f_ext[]['allow'] set to '*'!!
53 * You are allowed to copy/move folders within the same 'space' (web/ftp).
54 * You are allowed to copy/move folders between spaces (web/ftp) IF the destination has it's f_ext[]['allow'] set to '*'!
55 *
56 * Advice:
57 * You should always exclude php-files from the webspace. This will keep people from uploading, copy/moving and renaming files to become executable php scripts.
58 * You should never mount a ftp_space 'below' the webspace so that it reaches into the webspace. This is because if somebody unzips a zip-file in the ftp-space so that it reaches out into the webspace this will be a violation of the safety
59 * For example this is a bad idea: you have an ftp-space that is '/www/' and a web-space that is '/www/htdocs/'
60 *
61 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
62 * @package TYPO3
63 * @subpackage t3lib
64 */
65 class ExtendedFileUtility extends \TYPO3\CMS\Core\Utility\File\BasicFileUtility {
66
67 // External static variables:
68 // Notice; some of these are overridden in the start() method with values from $GLOBALS['TYPO3_CONF_VARS']['BE']
69 // Path to unzip-program (with trailing '/')
70 /**
71 * @todo Define visibility
72 */
73 public $unzipPath = '';
74
75 // If set, the uploaded files will overwrite existing files.
76 /**
77 * @todo Define visibility
78 */
79 public $dontCheckForUnique = 0;
80
81 // This array is self-explaining (look in the class below).
82 // It grants access to the functions. This could be set from outside in order to enabled functions to users.
83 // See also the function init_actionPerms() which takes input directly from the user-record
84 /**
85 * @todo Define visibility
86 */
87 public $actionPerms = array(
88 'deleteFile' => 0,
89 // Deleting files physically
90 'deleteFolder' => 0,
91 // Deleting folders physically
92 'deleteFolderRecursively' => 0,
93 // normally folders are deleted by the PHP-function rmdir(), but with this option a user deletes with 'rm -Rf ....' which is pretty wild!
94 'moveFile' => 0,
95 'moveFolder' => 0,
96 'copyFile' => 0,
97 'copyFolder' => 0,
98 'newFolder' => 0,
99 'newFile' => 0,
100 'editFile' => 0,
101 'unzipFile' => 0,
102 'uploadFile' => 0,
103 'renameFile' => 0,
104 'renameFolder' => 0
105 );
106
107 // This is regarded to be the recycler folder
108 /**
109 * @todo Define visibility
110 */
111 public $recyclerFN = '_recycler_';
112
113 /**
114 * Whether to use recycler (0 = no, 1 = if available, 2 = always)
115 *
116 * @var integer
117 * @deprecated since TYPO3 6.0
118 * @todo Define visibility
119 */
120 public $useRecycler = 1;
121
122 // Internal, dynamic
123 // Will contain map between upload ID and the final filename
124 /**
125 * @todo Define visibility
126 */
127 public $internalUploadMap = array();
128
129 /**
130 * @todo Define visibility
131 */
132 public $lastError = '';
133
134 /**
135 * @var array
136 */
137 protected $fileCmdMap;
138
139 /**
140 * The File Factory
141 *
142 * @var \TYPO3\CMS\Core\Resource\ResourceFactory
143 */
144 protected $fileFactory;
145
146 /**
147 * Initialization of the class
148 *
149 * @param array $fileCmds Array with the commands to execute. See "TYPO3 Core API" document
150 * @return void
151 * @todo Define visibility
152 */
153 public function start($fileCmds) {
154 $unzipPath = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path']);
155 if (substr($unzipPath, -1) !== '/' && is_dir($unzipPath)) {
156 // Make sure the path ends with a slash
157 $unzipPath .= '/';
158 }
159 $this->unzipPath = $unzipPath;
160 // Initialize Object Factory
161 $this->fileFactory = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
162 // Initializing file processing commands:
163 $this->fileCmdMap = $fileCmds;
164 }
165
166 /**
167 * Sets up permission to perform file/directory operations.
168 * See below or the be_user-table for the significance of the various bits in $setup.
169 *
170 * @param integer $setup File permission integer from BE_USER OR'ed with permissions of back-end groups this user is a member of
171 * @return void
172 * @todo Define visibility
173 */
174 public function init_actionPerms($setup) {
175 // Files: Upload,Copy,Move,Delete,Rename
176 if (($setup & 1) == 1) {
177 $this->actionPerms['uploadFile'] = 1;
178 $this->actionPerms['copyFile'] = 1;
179 $this->actionPerms['moveFile'] = 1;
180 $this->actionPerms['deleteFile'] = 1;
181 $this->actionPerms['renameFile'] = 1;
182 $this->actionPerms['editFile'] = 1;
183 $this->actionPerms['newFile'] = 1;
184 }
185 // Files: Unzip
186 if (($setup & 2) == 2) {
187 $this->actionPerms['unzipFile'] = 1;
188 }
189 // Directory: Move,Delete,Rename,New
190 if (($setup & 4) == 4) {
191 $this->actionPerms['moveFolder'] = 1;
192 $this->actionPerms['deleteFolder'] = 1;
193 $this->actionPerms['renameFolder'] = 1;
194 $this->actionPerms['newFolder'] = 1;
195 }
196 // Directory: Copy
197 if (($setup & 8) == 8) {
198 $this->actionPerms['copyFolder'] = 1;
199 }
200 // Directory: Delete recursively (rm -Rf)
201 if (($setup & 16) == 16) {
202 $this->actionPerms['deleteFolderRecursively'] = 1;
203 }
204 }
205
206 /**
207 * Processing the command array in $this->fileCmdMap
208 *
209 * @return mixed FALSE, if the file functions were not initialized
210 * @todo Define visibility
211 */
212 public function processData() {
213 $result = array();
214 if (!$this->isInit) {
215 return FALSE;
216 }
217 if (is_array($this->fileCmdMap)) {
218 // Check if there were uploads expected, but no one made
219 if ($this->fileCmdMap['upload']) {
220 $uploads = $this->fileCmdMap['upload'];
221 foreach ($uploads as $upload) {
222 if (!$_FILES[('upload_' . $upload['data'])]['name']) {
223 unset($this->fileCmdMap['upload'][$upload['data']]);
224 }
225 }
226 if (count($this->fileCmdMap['upload']) == 0) {
227 $this->writelog(1, 1, 108, 'No file was uploaded!', '');
228 }
229 }
230 // Traverse each set of actions
231 foreach ($this->fileCmdMap as $action => $actionData) {
232 // Traverse all action data. More than one file might be affected at the same time.
233 if (is_array($actionData)) {
234 $result[$action] = array();
235 foreach ($actionData as $cmdArr) {
236 // Clear file stats
237 clearstatcache();
238 // Branch out based on command:
239 switch ($action) {
240 case 'delete':
241 $result[$action][] = $this->func_delete($cmdArr);
242 break;
243 case 'copy':
244 $result[$action][] = $this->func_copy($cmdArr);
245 break;
246 case 'move':
247 $result[$action][] = $this->func_move($cmdArr);
248 break;
249 case 'rename':
250 $result[$action][] = $this->func_rename($cmdArr);
251 break;
252 case 'newfolder':
253 $result[$action][] = $this->func_newfolder($cmdArr);
254 break;
255 case 'newfile':
256 $result[$action][] = $this->func_newfile($cmdArr);
257 break;
258 case 'editfile':
259 $result[$action][] = $this->func_edit($cmdArr);
260 break;
261 case 'upload':
262 $result[$action][] = $this->func_upload($cmdArr);
263 break;
264 case 'unzip':
265 $result[$action][] = $this->func_unzip($cmdArr);
266 break;
267 }
268 // Hook for post-processing the action
269 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_extfilefunc.php']['processData'])) {
270 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_extfilefunc.php']['processData'] as $classRef) {
271 $hookObject = \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($classRef);
272 if (!$hookObject instanceof \TYPO3\CMS\Core\Utility\File\ExtendedFileUtilityProcessDataHookInterface) {
273 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Core\\Utility\\File\\ExtendedFileUtilityProcessDataHookInterface', 1279719168);
274 }
275 $hookObject->processData_postProcessAction($action, $cmdArr, $result[$action], $this);
276 }
277 }
278 }
279 }
280 }
281 }
282 return $result;
283 }
284
285 /**
286 * Adds log error messages from the operations of this script instance to the FlashMessageQueue
287 *
288 * @param string $redirect Redirect URL (for creating link in message)
289 * @return void
290 * @todo Define visibility
291 */
292 public function printLogErrorMessages($redirect = '') {
293 $this->getErrorMessages();
294 }
295
296 /**
297 * Adds log error messages from the previous file operations of this script instance
298 * to the FlashMessageQueue
299 *
300 * @return void
301 * @todo Define visibility
302 */
303 public function getErrorMessages() {
304 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_log', 'type = 2 AND userid = ' . intval($GLOBALS['BE_USER']->user['uid']) . ' AND tstamp=' . intval($GLOBALS['EXEC_TIME']) . ' AND error<>0');
305 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
306 $logData = unserialize($row['log_data']);
307 $msg = $row['error'] . ': ' . sprintf($row['details'], $logData[0], $logData[1], $logData[2], $logData[3], $logData[4]);
308 $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $msg, '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR, TRUE);
309 \TYPO3\CMS\Core\Messaging\FlashMessageQueue::addMessage($flashMessage);
310 }
311 $GLOBALS['TYPO3_DB']->sql_free_result($res);
312 }
313
314 /**
315 * Goes back in the path and checks in each directory if a folder named $this->recyclerFN (usually '_recycler_') is present.
316 * If a folder in the tree happens to be a _recycler_-folder (which means that we're deleting something inside a _recycler_-folder) this is ignored
317 *
318 * @param string $theFile Takes a valid Path ($theFile)
319 * @return string Returns the path (without trailing slash) of the closest recycle-folder if found. Else FALSE.
320 * @todo To be put in Storage with a better concept
321 * @todo Define visibility
322 * @deprecated since TYPO3 6.0, use \TYPO3\CMS\Core\Resource\ResourceStorage method instead
323 */
324 public function findRecycler($theFile) {
325 \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
326 if ($this->isPathValid($theFile)) {
327 $theFile = $this->cleanDirectoryName($theFile);
328 $fI = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($theFile);
329 $c = 0;
330 // !!! Method has been put in the storage, can be saftely removed
331 $rDir = $fI['path'] . $this->recyclerFN;
332 while ($this->checkPathAgainstMounts($fI['path']) && $c < 20) {
333 if (@is_dir($rDir) && $this->recyclerFN != $fI['file']) {
334 return $rDir;
335 }
336 $theFile = $fI['path'];
337 $theFile = $this->cleanDirectoryName($theFile);
338 $fI = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($theFile);
339 $c++;
340 }
341 }
342 }
343
344 /**
345 * Logging file operations
346 *
347 * @param integer $action The action number. See the functions in the class for a hint. Eg. edit is '9', upload is '1' ...
348 * @param integer $error The severity: 0 = message, 1 = error, 2 = System Error, 3 = security notice (admin)
349 * @param integer $details_nr This number is unique for every combination of $type and $action. This is the error-message number, which can later be used to translate error messages.
350 * @param string $details This is the default, raw error message in english
351 * @param array $data Array with special information that may go into $details by "%s" marks / sprintf() when the log is shown
352 * @return void
353 * @see class.t3lib_beuserauth.php
354 * @todo Define visibility
355 */
356 public function writeLog($action, $error, $details_nr, $details, $data) {
357 // Type value for tce_file.php
358 $type = 2;
359 if (is_object($GLOBALS['BE_USER'])) {
360 $GLOBALS['BE_USER']->writelog($type, $action, $error, $details_nr, $details, $data);
361 }
362 $this->lastError = vsprintf($details, $data);
363 }
364
365 /*************************************
366 *
367 * File operation functions
368 *
369 **************************************/
370 /**
371 * Deleting files and folders (action=4)
372 *
373 * @param array $cmds $cmds['data'] is the file/folder to delete
374 * @return boolean Returns TRUE upon success
375 * @todo Define visibility
376 */
377 public function func_delete($cmds) {
378 $result = FALSE;
379 if (!$this->isInit) {
380 return $result;
381 }
382 // Example indentifier for $cmds['data'] => "4:mypath/tomyfolder/myfile.jpg"
383 // for backwards compatibility: the combined file identifier was the path+filename
384 $fileObject = $this->getFileObject($cmds['data']);
385 // @todo implement the recycler feature which has been removed from the original implementation
386 // checks to delete the file
387 if ($fileObject instanceof \TYPO3\CMS\Core\Resource\File) {
388 $refIndexRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
389 '*',
390 'sys_refindex',
391 'deleted=0 AND ref_table="sys_file" AND ref_uid=' . intval($fileObject->getUid())
392 );
393 // check if the file still has references
394 if (count($refIndexRecords) > 0) {
395 $shortcutContent = array();
396 foreach ($refIndexRecords as $row) {
397 $shortcutRecord = NULL;
398 $shortcutRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($row['tablename'], $row['recuid']);
399 if (is_array($shortcutRecord) && $row['tablename'] !== 'sys_file_reference') {
400 $icon = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord($row['tablename'], $shortcutRecord);
401 $onClick = 'showClickmenu("' . $row['tablename'] . '", "' . $row['recuid'] . '", "1", "+info,history,edit,delete", "|", "");return false;';
402 $shortcutContent[] = '<a href="#" oncontextmenu="' . htmlspecialchars($onClick) . '" onclick="' . htmlspecialchars($onClick) . '">' . $icon . '</a>' . htmlspecialchars((\TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle($row['tablename'], $shortcutRecord) . ' [' . \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordPath($shortcutRecord['pid'], '', 80) . ']'));
403 }
404 }
405 $out = '<p>The file cannot be deleted since it is still used at the following places:<br />' . implode('<br />', $shortcutContent) . '</p>';
406 $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('t3lib_flashMessage', $out, 'File not deleted', \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING, TRUE);
407 \TYPO3\CMS\Core\Messaging\FlashMessageQueue::addMessage($flashMessage);
408 return;
409 } else {
410 try {
411 $result = $fileObject->delete();
412 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
413 $this->writelog(4, 1, 112, 'You are not allowed to access the file', array($fileObject->getIdentifier()));
414 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
415 $this->writelog(4, 1, 111, 'Target was not within your mountpoints! T="%s"', array($fileObject->getIdentifier()));
416 } catch (\RuntimeException $e) {
417 $this->writelog(4, 1, 110, 'Could not delete file "%s". Write-permission problem?', array($fileObject->getIdentifier()));
418 }
419 // Log success
420 $this->writelog(4, 0, 1, 'File "%s" deleted', array($fileObject->getIdentifier()));
421 }
422 } else {
423 try {
424 /** @var $fileObject \TYPO3\CMS\Core\Resource\FolderInterface */
425 $result = $fileObject->delete(TRUE);
426 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
427 $this->writelog(4, 1, 123, 'You are not allowed to access the directory', array($fileObject->getIdentifier()));
428 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
429 $this->writelog(4, 1, 121, 'Target was not within your mountpoints! T="%s"', array($fileObject->getIdentifier()));
430 } catch (\RuntimeException $e) {
431 $this->writelog(4, 1, 120, 'Could not delete directory! Write-permission problem? Is directory "%s" empty? (You are not allowed to delete directories recursively).', array($fileObject->getIdentifier()));
432 }
433 // Log success
434 $this->writelog(4, 0, 3, 'Directory "%s" deleted', array($fileObject->getIdentifier()));
435 }
436 return $result;
437 }
438
439 /**
440 * Gets a File or a Folder object from an identifier [storage]:[fileId]
441 *
442 * @param string $identifier
443 * @return \TYPO3\CMS\Core\Resource\Folder|\TYPO3\CMS\Core\Resource\File
444 */
445 protected function getFileObject($identifier) {
446 $object = $this->fileFactory->retrieveFileOrFolderObject($identifier);
447 if (!is_object($object)) {
448 throw new \TYPO3\CMS\Core\Resource\Exception\InvalidFileException('The item ' . $identifier . ' was not a file or directory!!', 1320122453);
449 }
450 return $object;
451 }
452
453 /**
454 * Copying files and folders (action=2)
455 *
456 * $cmds['data'] (string): The file/folder to copy
457 * + example "4:mypath/tomyfolder/myfile.jpg")
458 * + for backwards compatibility: the identifier was the path+filename
459 * $cmds['target'] (string): The path where to copy to.
460 * + example "2:targetpath/targetfolder/"
461 * $cmds['altName'] (string): Use an alternative name if the target already exists
462 *
463 * @param array $cmds Command details as described above
464 * @return \TYPO3\CMS\Core\Resource\File
465 */
466 protected function func_copy($cmds) {
467 if (!$this->isInit) {
468 return FALSE;
469 }
470 $sourceFileObject = $this->getFileObject($cmds['data']);
471 /** @var $targetFolderObject \TYPO3\CMS\Core\Resource\Folder */
472 $targetFolderObject = $this->getFileObject($cmds['target']);
473 // Basic check
474 if (!$targetFolderObject instanceof \TYPO3\CMS\Core\Resource\Folder) {
475 $this->writelog(2, 2, 100, 'Destination "%s" was not a directory', array($cmds['target']));
476 return FALSE;
477 }
478 // If this is TRUE, we append _XX to the file name if
479 $appendSuffixOnConflict = (string) $cmds['altName'];
480 $resultObject = NULL;
481 // Copying the file
482 if ($sourceFileObject instanceof \TYPO3\CMS\Core\Resource\File) {
483 try {
484 $conflictMode = $appendSuffixOnConflict !== '' ? 'renameNewFile' : 'cancel';
485 $resultObject = $sourceFileObject->copyTo($targetFolderObject, NULL, $conflictMode);
486 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
487 $this->writelog(2, 1, 114, 'You are not allowed to copy files', '');
488 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
489 $this->writelog(2, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
490 } catch (\TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException $e) {
491 $this->writelog(2, 1, 111, 'Extension of file name "%s" is not allowed in "%s"!', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
492 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
493 $this->writelog(2, 1, 112, 'File "%s" already exists in folder "%s"!', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
494 } catch (\RuntimeException $e) {
495 $this->writelog(2, 2, 109, 'File "%s" WAS NOT copied to "%s"! Write-permission problem?', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
496 }
497 $this->writelog(2, 0, 1, 'File "%s" copied to "%s"', array($sourceFileObject->getIdentifier(), $resultObject->getIdentifier()));
498 } else {
499 // Else means this is a Folder
500 $sourceFolderObject = $sourceFileObject;
501 try {
502 $conflictMode = $appendSuffixOnConflict !== '' ? 'renameNewFile' : 'cancel';
503 $resultObject = $sourceFolderObject->copyTo($targetFolderObject, NULL, $conflictMode);
504 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
505 $this->writelog(2, 1, 125, 'You are not allowed to copy directories', '');
506 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
507 $this->writelog(2, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
508 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException $e) {
509 $this->writelog(2, 1, 121, 'You don\'t have full access to the destination directory "%s"!', array($targetFolderObject->getIdentifier()));
510 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException $e) {
511 $this->writelog(2, 1, 122, 'Destination cannot be inside the target! D="%s", T="%s"', array($targetFolderObject->getIdentifier(), $sourceFolderObject->getIdentifier()));
512 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException $e) {
513 $this->writelog(2, 1, 123, 'Target "%s" already exists!', array($targetFolderObject->getIdentifier()));
514 } catch (\RuntimeException $e) {
515 $this->writelog(2, 2, 119, 'Directory "%s" WAS NOT copied to "%s"! Write-permission problem?', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
516 }
517 $this->writelog(2, 0, 2, 'Directory "%s" copied to "%s"', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
518 }
519 return $resultObject;
520 }
521
522 /**
523 * Moving files and folders (action=3)
524 *
525 * $cmds['data'] (string): The file/folder to move
526 * + example "4:mypath/tomyfolder/myfile.jpg")
527 * + for backwards compatibility: the identifier was the path+filename
528 * $cmds['target'] (string): The path where to move to.
529 * + example "2:targetpath/targetfolder/"
530 * $cmds['altName'] (string): Use an alternative name if the target already exists
531 *
532 * @param array $cmds Command details as described above
533 * @return \TYPO3\CMS\Core\Resource\File
534 */
535 protected function func_move($cmds) {
536 if (!$this->isInit) {
537 return FALSE;
538 }
539 $sourceFileObject = $this->getFileObject($cmds['data']);
540 $targetFolderObject = $this->getFileObject($cmds['target']);
541 // Basic check
542 if (!$targetFolderObject instanceof \TYPO3\CMS\Core\Resource\Folder) {
543 $this->writelog(3, 2, 100, 'Destination "%s" was not a directory', array($cmds['target']));
544 return FALSE;
545 }
546 $alternativeName = (string) $cmds['altName'];
547 $resultObject = NULL;
548 // Moving the file
549 if ($sourceFileObject instanceof \TYPO3\CMS\Core\Resource\File) {
550 try {
551 if ($alternativeName !== '') {
552 // Don't allow overwriting existing files, but find a new name
553 $resultObject = $sourceFileObject->moveTo($targetFolderObject, $alternativeName, 'renameNewFile');
554 } else {
555 // Don't allow overwriting existing files
556 $resultObject = $sourceFileObject->moveTo($targetFolderObject, NULL, 'cancel');
557 }
558 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
559 $this->writelog(3, 1, 114, 'You are not allowed to move files', '');
560 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
561 $this->writelog(3, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
562 } catch (\TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException $e) {
563 $this->writelog(3, 1, 111, 'Extension of file name "%s" is not allowed in "%s"!', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
564 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
565 $this->writelog(3, 1, 112, 'File "%s" already exists in folder "%s"!', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
566 } catch (\RuntimeException $e) {
567 $this->writelog(3, 2, 109, 'File "%s" WAS NOT copied to "%s"! Write-permission problem?', array($sourceFileObject->getIdentifier(), $targetFolderObject->getIdentifier()));
568 }
569 $this->writelog(3, 0, 1, 'File "%s" moved to "%s"', array($sourceFileObject->getIdentifier(), $resultObject->getIdentifier()));
570 } else {
571 // Else means this is a Folder
572 $sourceFolderObject = $sourceFileObject;
573 try {
574 if ($alternativeName !== '') {
575 // Don't allow overwriting existing files, but find a new name
576 $resultObject = $sourceFolderObject->moveTo($targetFolderObject, $alternativeName, 'renameNewFile');
577 } else {
578 // Don't allow overwriting existing files
579 $resultObject = $sourceFolderObject->moveTo($targetFolderObject, NULL, 'renameNewFile');
580 }
581 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
582 $this->writelog(3, 1, 125, 'You are not allowed to move directories', '');
583 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException $e) {
584 $this->writelog(3, 1, 110, 'Could not access all necessary resources. Source file or destination maybe was not within your mountpoints? T="%s", D="%s"', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
585 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException $e) {
586 $this->writelog(3, 1, 121, 'You don\'t have full access to the destination directory "%s"!', array($targetFolderObject->getIdentifier()));
587 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidTargetFolderException $e) {
588 $this->writelog(3, 1, 122, 'Destination cannot be inside the target! D="%s", T="%s"', array($targetFolderObject->getIdentifier(), $sourceFolderObject->getIdentifier()));
589 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException $e) {
590 $this->writelog(3, 1, 123, 'Target "%s" already exists!', array($targetFolderObject->getIdentifier()));
591 } catch (\RuntimeException $e) {
592 $this->writelog(3, 2, 119, 'Directory "%s" WAS NOT moved to "%s"! Write-permission problem?', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
593 }
594 $this->writelog(3, 0, 2, 'Directory "%s" moved to "%s"', array($sourceFolderObject->getIdentifier(), $targetFolderObject->getIdentifier()));
595 }
596 return $resultObject;
597 }
598
599 /**
600 * Renaming files or foldes (action=5)
601 *
602 * $cmds['data'] (string): The file/folder to copy
603 * + example "4:mypath/tomyfolder/myfile.jpg")
604 * + for backwards compatibility: the identifier was the path+filename
605 * $cmds['target'] (string): New name of the file/folder
606 *
607 * @param array $cmds Command details as described above
608 * @return \TYPO3\CMS\Core\Resource\File Returns the new file upon success
609 * @todo Define visibility
610 */
611 public function func_rename($cmds) {
612 if (!$this->isInit) {
613 return FALSE;
614 }
615 $sourceFileObject = $this->getFileObject($cmds['data']);
616 $targetFile = $cmds['target'];
617 $resultObject = NULL;
618 if ($sourceFileObject instanceof \TYPO3\CMS\Core\Resource\File) {
619 try {
620 // Try to rename the File
621 $resultObject = $sourceFileObject->rename($targetFile);
622 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
623 $this->writelog(5, 1, 102, 'You are not allowed to rename files!', '');
624 } catch (\TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException $e) {
625 $this->writelog(5, 1, 101, 'Extension of file name "%s" was not allowed!', array($targetFile));
626 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
627 $this->writelog(5, 1, 120, 'Destination "%s" existed already!', array($targetFile));
628 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
629 $this->writelog(5, 1, 121, 'Destination path "%s" was not within your mountpoints!', array($targetFile));
630 } catch (\RuntimeException $e) {
631 $this->writelog(5, 1, 100, 'File "%s" was not renamed! Write-permission problem in "%s"?', array($sourceFileObject->getName(), $targetFile));
632 }
633 $this->writelog(5, 0, 1, 'File renamed from "%s" to "%s"', array($sourceFileObject->getName(), $targetFile));
634 } else {
635 // Else means this is a Folder
636 try {
637 // Try to rename the Folder
638 $resultObject = $sourceFileObject->rename($targetFile);
639 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
640 $this->writelog(5, 1, 111, 'You are not allowed to rename directories!', '');
641 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
642 $this->writelog(5, 1, 120, 'Destination "%s" existed already!', array($targetFile));
643 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
644 $this->writelog(5, 1, 121, 'Destination path "%s" was not within your mountpoints!', array($targetFile));
645 } catch (\RuntimeException $e) {
646 $this->writelog(5, 1, 110, 'Directory "%s" was not renamed! Write-permission problem in "%s"?', array($sourceFileObject->getName(), $targetFile));
647 }
648 $this->writelog(5, 0, 2, 'Directory renamed from "%s" to "%s"', array($sourceFileObject->getName(), $targetFile));
649 }
650 return $resultObject;
651 }
652
653 /**
654 * This creates a new folder. (action=6)
655 *
656 * $cmds['data'] (string): The new folder name
657 * $cmds['target'] (string): The path where to copy to.
658 * + example "2:targetpath/targetfolder/"
659 *
660 * @param array $cmds Command details as described above
661 * @return \TYPO3\CMS\Core\Resource\Folder Returns the new foldername upon success
662 * @todo Define visibility
663 */
664 public function func_newfolder($cmds) {
665 if (!$this->isInit) {
666 return FALSE;
667 }
668 $targetFolderObject = $this->getFileObject($cmds['target']);
669 if (!$targetFolderObject instanceof \TYPO3\CMS\Core\Resource\Folder) {
670 $this->writelog(6, 2, 104, 'Destination "%s" was not a directory', array($cmds['target']));
671 return FALSE;
672 }
673 $resultObject = NULL;
674 try {
675 $folderName = $cmds['data'];
676 $resultObject = $targetFolderObject->createFolder($folderName);
677 $this->writelog(6, 0, 1, 'Directory "%s" created in "%s"', array($folderName, $targetFolderObject->getIdentifier() . '/'));
678 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException $e) {
679 $this->writelog(6, 1, 103, 'You are not allowed to create directories!', '');
680 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
681 $this->writelog(6, 1, 102, 'Destination path "%s" was not within your mountpoints!', array($targetFolderObject->getIdentifier() . '/'));
682 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFolderException $e) {
683 $this->writelog(6, 1, 101, 'File or directory "%s" existed already!', array($folderName));
684 } catch (\RuntimeException $e) {
685 $this->writelog(6, 1, 100, 'Directory "%s" not created. Write-permission problem in "%s"?', array($folderName, $targetFolderObject->getIdentifier() . '/'));
686 }
687 return $resultObject;
688 }
689
690 /**
691 * This creates a new file. (action=8)
692 * $cmds['data'] (string): The new file name
693 * $cmds['target'] (string): The path where to create it.
694 * + example "2:targetpath/targetfolder/"
695 *
696 * @param array $cmds Command details as described above
697 * @return string Returns the new filename upon success
698 * @todo Define visibility
699 */
700 public function func_newfile($cmds) {
701 if (!$this->isInit) {
702 return FALSE;
703 }
704 $targetFolderObject = $this->getFileObject($cmds['target']);
705 if (!$targetFolderObject instanceof \TYPO3\CMS\Core\Resource\Folder) {
706 $this->writelog(8, 2, 104, 'Destination "%s" was not a directory', array($cmds['target']));
707 return FALSE;
708 }
709 $resultObject = NULL;
710 try {
711 $fileName = $cmds['data'];
712 $resultObject = $targetFolderObject->createFile($fileName);
713 $this->writelog(8, 0, 1, 'File created: "%s"', array($fileName));
714 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException $e) {
715 $this->writelog(8, 1, 103, 'You are not allowed to create files!', '');
716 } catch (\TYPO3\CMS\Core\Resource\Exception\NotInMountPointException $e) {
717 $this->writelog(8, 1, 102, 'Destination path "%s" was not within your mountpoints!', array($targetFolderObject->getIdentifier()));
718 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
719 $this->writelog(8, 1, 101, 'File existed already in "%s"!', array($targetFolderObject->getIdentifier()));
720 } catch (\TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException $e) {
721 $this->writelog(8, 1, 106, 'File name "%s" was not allowed!', $fileName);
722 } catch (\RuntimeException $e) {
723 $this->writelog(8, 1, 100, 'File "%s" was not created! Write-permission problem in "%s"?', array($fileName, $targetFolderObject->getIdentifier()));
724 }
725 return $resultObject;
726 }
727
728 /**
729 * Editing textfiles or folders (action=9)
730 *
731 * @param array $cmds $cmds['data'] is the new content. $cmds['target'] is the target (file or dir)
732 * @return boolean Returns TRUE on success
733 * @todo Define visibility
734 */
735 public function func_edit($cmds) {
736 if (!$this->isInit) {
737 return FALSE;
738 }
739 // Example indentifier for $cmds['target'] => "4:mypath/tomyfolder/myfile.jpg"
740 // for backwards compatibility: the combined file identifier was the path+filename
741 $fileIdentifier = $cmds['target'];
742 $fileObject = $this->getFileObject($fileIdentifier);
743 // Example indentifier for $cmds['target'] => "2:targetpath/targetfolder/"
744 $content = $cmds['data'];
745 if (!$fileObject instanceof \TYPO3\CMS\Core\Resource\File) {
746 $this->writelog(9, 2, 123, 'Target "%s" was not a file!', array($fileIdentifier));
747 return FALSE;
748 }
749 $extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
750 if (!\TYPO3\CMS\Core\Utility\GeneralUtility::inList($extList, $fileObject->getExtension())) {
751 $this->writelog(9, 1, 102, 'File extension "%s" is not a textfile format! (%s)', array($fileObject->getExtension(), $extList));
752 return FALSE;
753 }
754 try {
755 $fileObject->setContents($content);
756 clearstatcache();
757 $this->writelog(9, 0, 1, 'File saved to "%s", bytes: %s, MD5: %s ', array($fileObject->getIdentifier(), $fileObject->getSize(), md5($content)));
758 return TRUE;
759 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
760 $this->writelog(9, 1, 104, 'You are not allowed to edit files!', '');
761 return FALSE;
762 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFileWritePermissionsException $e) {
763 $this->writelog(9, 1, 100, 'File "%s" was not saved! Write-permission problem?', array($fileObject->getIdentifier()));
764 return FALSE;
765 }
766 }
767
768 /**
769 * Upload of files (action=1)
770 * when having multiple uploads (HTML5-style), the array $_FILES looks like this:
771 * Array(
772 * [upload_1] => Array(
773 * [name] => Array(
774 * [0] => GData - Content-Elements and Media-Gallery.pdf
775 * [1] => CMS Expo 2011.txt
776 * )
777 * [type] => Array(
778 * [0] => application/pdf
779 * [1] => text/plain
780 * )
781 * [tmp_name] => Array(
782 * [0] => /Applications/MAMP/tmp/php/phpNrOB43
783 * [1] => /Applications/MAMP/tmp/php/phpD2HQAK
784 * )
785 * [size] => Array(
786 * [0] => 373079
787 * [1] => 1291
788 * )
789 * )
790 * )
791 * in HTML you'd need sth like this: <input type="file" name="upload_1[]" multiple="true" />
792 *
793 * @param array $cmds $cmds['data'] is the ID-number (points to the global var that holds the filename-ref ($_FILES['upload_' . $id]['name']) . $cmds['target'] is the target directory, $cmds['charset'] is the the character set of the file name (utf-8 is needed for JS-interaction)
794 * @return string Returns the new filename upon success
795 * @todo Define visibility
796 */
797 public function func_upload($cmds) {
798 if (!$this->isInit) {
799 return FALSE;
800 }
801 $uploadPosition = $cmds['data'];
802 $uploadedFileData = $_FILES['upload_' . $uploadPosition];
803 if (empty($uploadedFileData['name']) || is_array($uploadedFileData['name']) && empty($uploadedFileData['name'][0])) {
804 $this->writelog(1, 2, 108, 'No file was uploaded!', '');
805 return FALSE;
806 }
807 // Example indentifier for $cmds['target'] => "2:targetpath/targetfolder/"
808 $targetFolderObject = $this->getFileObject($cmds['target']);
809 // Uploading with non HTML-5-style, thus, make an array out of it, so we can loop over it
810 if (!is_array($uploadedFileData['name'])) {
811 $uploadedFileData = array(
812 'name' => array($uploadedFileData['name']),
813 'type' => array($uploadedFileData['type']),
814 'tmp_name' => array($uploadedFileData['tmp_name']),
815 'size' => array($uploadedFileData['size'])
816 );
817 }
818 $resultObjects = array();
819 $numberOfUploadedFilesForPosition = count($uploadedFileData['name']);
820 // Loop through all uploaded files
821 for ($i = 0; $i < $numberOfUploadedFilesForPosition; $i++) {
822 $fileInfo = array(
823 'name' => $uploadedFileData['name'][$i],
824 'type' => $uploadedFileData['type'][$i],
825 'tmp_name' => $uploadedFileData['tmp_name'][$i],
826 'size' => $uploadedFileData['size'][$i]
827 );
828 try {
829 // @todo can be improved towards conflict mode naming
830 if ($this->dontCheckForUnique) {
831 $conflictMode = 'replace';
832 } else {
833 $conflictMode = 'cancel';
834 }
835 $resultObjects[] = $targetFolderObject->addUploadedFile($fileInfo, $conflictMode);
836 $this->writelog(1, 0, 1, 'Uploading file "%s" to "%s"', array($fileInfo['name'], $targetFolderObject->getIdentifier()));
837 } catch (\TYPO3\CMS\Core\Resource\Exception\UploadException $e) {
838 $this->writelog(1, 2, 106, 'The upload has failed, no uploaded file found!', '');
839 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException $e) {
840 $this->writelog(1, 1, 105, 'You are not allowed to upload files!', '');
841 } catch (\TYPO3\CMS\Core\Resource\Exception\UploadSizeException $e) {
842 $this->writelog(1, 1, 104, 'The uploaded file "%s" exceeds the size-limit', array($fileInfo['name']));
843 } catch (\TYPO3\CMS\Core\Resource\Exception\InsufficientFolderWritePermissionsException $e) {
844 $this->writelog(1, 1, 103, 'Destination path "%s" was not within your mountpoints!', array($targetFolderObject->getIdentifier()));
845 } catch (\TYPO3\CMS\Core\Resource\Exception\IllegalFileExtensionException $e) {
846 $this->writelog(1, 1, 102, 'Extension of file name "%s" is not allowed in "%s"!', array($fileInfo['name'], $targetFolderObject->getIdentifier()));
847 } catch (\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException $e) {
848 $this->writelog(1, 1, 101, 'No unique filename available in "%s"!', array($targetFolderObject->getIdentifier()));
849 } catch (\RuntimeException $e) {
850 $this->writelog(1, 1, 100, 'Uploaded file could not be moved! Write-permission problem in "%s"?', array($targetFolderObject->getIdentifier()));
851 }
852 }
853 return $resultObjects;
854 }
855
856 /**
857 * Unzipping file (action=7)
858 * This is permitted only if the user has fullAccess or if the file resides
859 *
860 * @param array $cmds $cmds['data'] is the zip-file. $cmds['target'] is the target directory. If not set we'll default to the same directory as the file is in.
861 * @return boolean Returns TRUE on success
862 * @todo Define visibility
863 */
864 public function func_unzip($cmds) {
865 if (!$this->isInit || $this->dont_use_exec_commands) {
866 return FALSE;
867 }
868 $theFile = $cmds['data'];
869 if (!@is_file($theFile)) {
870 $this->writelog(7, 2, 105, 'The file "%s" did not exist!', array($theFile));
871 return FALSE;
872 }
873 $fI = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($theFile);
874 if (!isset($cmds['target'])) {
875 $cmds['target'] = $fI['path'];
876 }
877 // Clean up destination directory
878 // !!! Method has been put in the local driver, can be saftely removed
879 $theDest = $this->is_directory($cmds['target']);
880 if (!$theDest) {
881 $this->writelog(7, 2, 104, 'Destination "%s" was not a directory', array($cmds['target']));
882 return FALSE;
883 }
884 if (!$this->actionPerms['unzipFile']) {
885 $this->writelog(7, 1, 103, 'You are not allowed to unzip files', '');
886 return FALSE;
887 }
888 if ($fI['fileext'] != 'zip') {
889 $this->writelog(7, 1, 102, 'File extension is not "zip"', '');
890 return FALSE;
891 }
892 if (!$this->checkIfFullAccess($theDest)) {
893 $this->writelog(7, 1, 101, 'You don\'t have full access to the destination directory "%s"!', array($theDest));
894 return FALSE;
895 }
896 // !!! Method has been put in the sotrage driver, can be saftely removed
897 if ($this->checkPathAgainstMounts($theFile) && $this->checkPathAgainstMounts($theDest . '/')) {
898 // No way to do this under windows.
899 $cmd = $this->unzipPath . 'unzip -qq ' . escapeshellarg($theFile) . ' -d ' . escapeshellarg($theDest);
900 \TYPO3\CMS\Core\Utility\CommandUtility::exec($cmd);
901 $this->writelog(7, 0, 1, 'Unzipping file "%s" in "%s"', array($theFile, $theDest));
902 return TRUE;
903 } else {
904 $this->writelog(7, 1, 100, 'File "%s" or destination "%s" was not within your mountpoints!', array($theFile, $theDest));
905 return FALSE;
906 }
907 }
908
909 }
910
911
912 ?>