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