53bcab50c2653a98bbe9e5aa0a6227489c147546
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_userauthgroup.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 * Contains an extension class specifically for authentication/initialization of backend users in TYPO3
29 *
30 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
31 *
32 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
33 */
34
35
36 /**
37 * Extension to class.t3lib_userauth.php; Authentication of users in TYPO3 Backend
38 *
39 * Actually this class is extended again by t3lib_beuserauth which is the actual backend user class that will be instantiated.
40 * In fact the two classes t3lib_beuserauth and this class could just as well be one, single class since t3lib_userauthgroup is not - to my knowledge - used separately elsewhere. But for historical reasons they are two separate classes.
41 *
42 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
43 * @package TYPO3
44 * @subpackage t3lib
45 */
46 abstract class t3lib_userAuthGroup extends t3lib_userAuth {
47 var $usergroup_column = 'usergroup'; // Should be set to the usergroup-column (id-list) in the user-record
48 var $usergroup_table = 'be_groups'; // The name of the group-table
49
50 // internal
51 var $groupData = array( // This array holds lists of eg. tables, fields and other values related to the permission-system. See fetchGroupData
52 'filemounts' => array() // Filemounts are loaded here
53 );
54 var $workspace = -99; // User workspace. -99 is ERROR (none available), -1 is offline, 0 is online, >0 is custom workspaces.
55 var $workspaceRec = array(); // Custom workspace record if any
56
57 var $userGroups = array(); // This array will hold the groups that the user is a member of
58 var $userGroupsUID = array(); // This array holds the uid's of the groups in the listed order
59 var $groupList = ''; // This is $this->userGroupsUID imploded to a comma list... Will correspond to the 'usergroup_cached_list'
60 var $dataLists = array( // Used internally to accumulate data for the user-group. DONT USE THIS EXTERNALLY! Use $this->groupData instead
61 'webmount_list' => '',
62 'filemount_list' => '',
63 'fileoper_perms' => 0,
64 'modList' => '',
65 'tables_select' => '',
66 'tables_modify' => '',
67 'pagetypes_select' => '',
68 'non_exclude_fields' => '',
69 'explicit_allowdeny' => '',
70 'allowed_languages' => '',
71 'workspace_perms' => '',
72 'custom_options' => '',
73 );
74 var $includeHierarchy = array(); // For debugging/display of order in which subgroups are included.
75 var $includeGroupArray = array(); // List of group_id's in the order they are processed.
76
77 var $OS = ''; // Set to 'WIN', if windows
78 var $TSdataArray = array(); // Used to accumulate the TSconfig data of the user
79 var $userTS_text = ''; // Contains the non-parsed user TSconfig
80 var $userTS = array(); // Contains the parsed user TSconfig
81 var $userTSUpdated = 0; // Set internally if the user TSconfig was parsed and needs to be cached.
82 var $userTS_dontGetCached = 0; // Set this from outside if you want the user TSconfig to ALWAYS be parsed and not fetched from cache.
83
84 var $RTE_errors = array(); // RTE availability errors collected.
85 var $errorMsg = ''; // Contains last error message
86
87 var $checkWorkspaceCurrent_cache = NULL; // Cache for checkWorkspaceCurrent()
88
89
90 /**
91 * @var t3lib_file_Storage[]
92 */
93 protected $fileStorages;
94
95 /**
96 * @var array
97 */
98 protected $filePermissions;
99
100
101 /************************************
102 *
103 * Permission checking functions:
104 *
105 ************************************/
106
107 /**
108 * Returns TRUE if user is admin
109 * Basically this function evaluates if the ->user[admin] field has bit 0 set. If so, user is admin.
110 *
111 * @return boolean
112 */
113 function isAdmin() {
114 return (($this->user['admin'] & 1) == 1);
115 }
116
117 /**
118 * Returns TRUE if the current user is a member of group $groupId
119 * $groupId must be set. $this->groupList must contain groups
120 * Will return TRUE also if the user is a member of a group through subgroups.
121 *
122 * @param integer Group ID to look for in $this->groupList
123 * @return boolean
124 */
125 function isMemberOfGroup($groupId) {
126 $groupId = intval($groupId);
127 if ($this->groupList && $groupId) {
128 return t3lib_div::inList($this->groupList, $groupId);
129 }
130 }
131
132 /**
133 * Checks if the permissions is granted based on a page-record ($row) and $perms (binary and'ed)
134 *
135 * Bits for permissions, see $perms variable:
136 *
137 * 1 - Show: See/Copy page and the pagecontent.
138 * 16- Edit pagecontent: Change/Add/Delete/Move pagecontent.
139 * 2- Edit page: Change/Move the page, eg. change title, startdate, hidden.
140 * 4- Delete page: Delete the page and pagecontent.
141 * 8- New pages: Create new pages under the page.
142 *
143 * @param array $row is the pagerow for which the permissions is checked
144 * @param integer $perms is the binary representation of the permission we are going to check. Every bit in this number represents a permission that must be set. See function explanation.
145 * @return boolean TRUE or False upon evaluation
146 */
147 function doesUserHaveAccess($row, $perms) {
148 $userPerms = $this->calcPerms($row);
149 return ($userPerms & $perms) == $perms;
150 }
151
152 /**
153 * Checks if the page id, $id, is found within the webmounts set up for the user.
154 * This should ALWAYS be checked for any page id a user works with, whether it's about reading, writing or whatever.
155 * The point is that this will add the security that a user can NEVER touch parts outside his mounted pages in the page tree. This is otherwise possible if the raw page permissions allows for it. So this security check just makes it easier to make safe user configurations.
156 * If the user is admin OR if this feature is disabled (fx. by setting TYPO3_CONF_VARS['BE']['lockBeUserToDBmounts']=0) then it returns "1" right away
157 * Otherwise the function will return the uid of the webmount which was first found in the rootline of the input page $id
158 *
159 * @param integer Page ID to check
160 * @param string Content of "->getPagePermsClause(1)" (read-permissions). If not set, they will be internally calculated (but if you have the correct value right away you can save that database lookup!)
161 * @param boolean If set, then the function will exit with an error message.
162 * @return integer The page UID of a page in the rootline that matched a mount point
163 */
164 function isInWebMount($id, $readPerms = '', $exitOnError = 0) {
165 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin()) {
166 return 1;
167 }
168 $id = intval($id);
169
170 // Check if input id is an offline version page in which case we will map id to the online version:
171 $checkRec = t3lib_beFUnc::getRecord('pages', $id, 'pid,t3ver_oid');
172 if ($checkRec['pid'] == -1) {
173 $id = intval($checkRec['t3ver_oid']);
174 }
175
176 if (!$readPerms) {
177 $readPerms = $this->getPagePermsClause(1);
178 }
179 if ($id > 0) {
180 $wM = $this->returnWebmounts();
181 $rL = t3lib_BEfunc::BEgetRootLine($id, ' AND ' . $readPerms);
182
183 foreach ($rL as $v) {
184 if ($v['uid'] && in_array($v['uid'], $wM)) {
185 return $v['uid'];
186 }
187 }
188 }
189 if ($exitOnError) {
190 throw new RuntimeException('Access Error: This page is not within your DB-mounts', 1294586445);
191 }
192 }
193
194 /**
195 * Checks access to a backend module with the $MCONF passed as first argument
196 *
197 * @param array $MCONF array of a backend module!
198 * @param boolean If set, an array will issue an error message and exit.
199 * @return boolean Will return TRUE if $MCONF['access'] is not set at all, if the BE_USER is admin or if the module is enabled in the be_users/be_groups records of the user (specifically enabled). Will return FALSE if the module name is not even found in $TBE_MODULES
200 */
201 function modAccess($conf, $exitOnError) {
202 if (!t3lib_BEfunc::isModuleSetInTBE_MODULES($conf['name'])) {
203 if ($exitOnError) {
204 throw new RuntimeException('Fatal Error: This module "' . $conf['name'] . '" is not enabled in TBE_MODULES', 1294586446);
205 }
206 return FALSE;
207 }
208
209 // Workspaces check:
210
211 if (t3lib_extMgm::isLoaded('workspaces') && $conf['workspaces']) {
212 if (($this->workspace === 0 && t3lib_div::inList($conf['workspaces'], 'online')) ||
213 ($this->workspace === -1 && t3lib_div::inList($conf['workspaces'], 'offline')) ||
214 ($this->workspace > 0 && t3lib_div::inList($conf['workspaces'], 'custom'))) {
215 // ok, go on...
216 } else {
217 if ($exitOnError) {
218 throw new RuntimeException('Workspace Error: This module "' . $conf['name'] . '" is not available under the current workspace', 1294586447);
219 }
220 return FALSE;
221 }
222 }
223
224 // Returns TRUE if conf[access] is not set at all or if the user is admin
225 if (!$conf['access'] || $this->isAdmin()) {
226 return TRUE;
227 }
228
229 // If $conf['access'] is set but not with 'admin' then we return TRUE, if the module is found in the modList
230 if (!strstr($conf['access'], 'admin') && $conf['name']) {
231 $acs = $this->check('modules', $conf['name']);
232 }
233 if (!$acs && $exitOnError) {
234 throw new RuntimeException('Access Error: You don\'t have access to this module.', 1294586448);
235 } else {
236 return $acs;
237 }
238 }
239
240 /**
241 * Returns a WHERE-clause for the pages-table where user permissions according to input argument, $perms, is validated.
242 * $perms is the "mask" used to select. Fx. if $perms is 1 then you'll get all pages that a user can actually see!
243 * 2^0 = show (1)
244 * 2^1 = edit (2)
245 * 2^2 = delete (4)
246 * 2^3 = new (8)
247 * If the user is 'admin' " 1=1" is returned (no effect)
248 * If the user is not set at all (->user is not an array), then " 1=0" is returned (will cause no selection results at all)
249 * The 95% use of this function is "->getPagePermsClause(1)" which will return WHERE clauses for *selecting* pages in backend listings - in other words this will check read permissions.
250 *
251 * @param integer Permission mask to use, see function description
252 * @return string Part of where clause. Prefix " AND " to this.
253 */
254 function getPagePermsClause($perms) {
255 if (is_array($this->user)) {
256 if ($this->isAdmin()) {
257 return ' 1=1';
258 }
259
260 $perms = intval($perms); // Make sure it's integer.
261 $str = ' (' .
262 '(pages.perms_everybody & ' . $perms . ' = ' . $perms . ')' . // Everybody
263 ' OR (pages.perms_userid = ' . $this->user['uid'] . ' AND pages.perms_user & ' . $perms . ' = ' . $perms . ')'; // User
264 if ($this->groupList) {
265 $str .= ' OR (pages.perms_groupid in (' . $this->groupList . ') AND pages.perms_group & ' . $perms . ' = ' . $perms . ')'; // Group (if any is set)
266 }
267 $str .= ')';
268
269 // ****************
270 // getPagePermsClause-HOOK
271 // ****************
272 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'])) {
273
274 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'] as $_funcRef) {
275 $_params = array('currentClause' => $str, 'perms' => $perms);
276 $str = t3lib_div::callUserFunction($_funcRef, $_params, $this);
277 }
278 }
279
280 return $str;
281 } else {
282 return ' 1=0';
283 }
284 }
285
286 /**
287 * Returns a combined binary representation of the current users permissions for the page-record, $row.
288 * The perms for user, group and everybody is OR'ed together (provided that the page-owner is the user and for the groups that the user is a member of the group
289 * If the user is admin, 31 is returned (full permissions for all five flags)
290 *
291 * @param array Input page row with all perms_* fields available.
292 * @return integer Bitwise representation of the users permissions in relation to input page row, $row
293 */
294 function calcPerms($row) {
295 if ($this->isAdmin()) {
296 return 31;
297 } // Return 31 for admin users.
298
299 $out = 0;
300 if (isset($row['perms_userid']) && isset($row['perms_user']) && isset($row['perms_groupid']) && isset($row['perms_group']) && isset($row['perms_everybody']) && isset($this->groupList)) {
301 if ($this->user['uid'] == $row['perms_userid']) {
302 $out |= $row['perms_user'];
303 }
304 if ($this->isMemberOfGroup($row['perms_groupid'])) {
305 $out |= $row['perms_group'];
306 }
307 $out |= $row['perms_everybody'];
308 }
309
310 // ****************
311 // CALCPERMS hook
312 // ****************
313 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'])) {
314 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'] as $_funcRef) {
315 $_params = array(
316 'row' => $row,
317 'outputPermissions' => $out
318 );
319 $out = t3lib_div::callUserFunction($_funcRef, $_params, $this);
320 }
321 }
322
323 return $out;
324 }
325
326 /**
327 * Returns TRUE if the RTE (Rich Text Editor) can be enabled for the user
328 * Strictly this is not permissions being checked but rather a series of settings like a loaded extension, browser/client type and a configuration option in ->uc[edit_RTE]
329 * The reasons for a FALSE return can be found in $this->RTE_errors
330 *
331 * @return boolean
332 */
333 function isRTE() {
334 // Start:
335 $this->RTE_errors = array();
336 if (!$this->uc['edit_RTE']) {
337 $this->RTE_errors[] = 'RTE is not enabled for user!';
338 }
339 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled']) {
340 $this->RTE_errors[] = 'RTE is not enabled in $TYPO3_CONF_VARS["BE"]["RTEenabled"]';
341 }
342
343
344 // Acquire RTE object:
345 $RTE = t3lib_BEfunc::RTEgetObj();
346 if (!is_object($RTE)) {
347 $this->RTE_errors = array_merge($this->RTE_errors, $RTE);
348 }
349
350 if (!count($this->RTE_errors)) {
351 return TRUE;
352 } else {
353 return FALSE;
354 }
355 }
356
357 /**
358 * Returns TRUE if the $value is found in the list in a $this->groupData[] index pointed to by $type (array key).
359 * Can thus be users to check for modules, exclude-fields, select/modify permissions for tables etc.
360 * If user is admin TRUE is also returned
361 * Please see the document Inside TYPO3 for examples.
362 *
363 * @param string The type value; "webmounts", "filemounts", "pagetypes_select", "tables_select", "tables_modify", "non_exclude_fields", "modules"
364 * @param string String to search for in the groupData-list
365 * @return boolean TRUE if permission is granted (that is, the value was found in the groupData list - or the BE_USER is "admin")
366 */
367 function check($type, $value) {
368 if (isset($this->groupData[$type])) {
369 if ($this->isAdmin() || t3lib_div::inList($this->groupData[$type], $value)) {
370 return TRUE;
371 }
372 }
373 return FALSE;
374 }
375
376 /**
377 * Checking the authMode of a select field with authMode set
378 *
379 * @param string Table name
380 * @param string Field name (must be configured in TCA and of type "select" with authMode set!)
381 * @param string Value to evaluation (single value, must not contain any of the chars ":,|")
382 * @param string Auth mode keyword (explicitAllow, explicitDeny, individual)
383 * @return boolean TRUE or FALSE whether access is granted or not.
384 */
385 function checkAuthMode($table, $field, $value, $authMode) {
386
387 // Admin users can do anything:
388 if ($this->isAdmin()) {
389 return TRUE;
390 }
391
392 // Allow all blank values:
393 if (!strcmp($value, '')) {
394 return TRUE;
395 }
396
397 // Certain characters are not allowed in the value
398 if (preg_match('/[:|,]/', $value)) {
399 return FALSE;
400 }
401
402 // Initialize:
403 $testValue = $table . ':' . $field . ':' . $value;
404 $out = TRUE;
405
406 // Checking value:
407 switch ((string) $authMode) {
408 case 'explicitAllow':
409 if (!t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':ALLOW')) {
410 $out = FALSE;
411 }
412 break;
413 case 'explicitDeny':
414 if (t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
415 $out = FALSE;
416 }
417 break;
418 case 'individual':
419 t3lib_div::loadTCA($table);
420 if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
421 $items = $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'];
422 if (is_array($items)) {
423 foreach ($items as $iCfg) {
424 if (!strcmp($iCfg[1], $value) && $iCfg[4]) {
425 switch ((string) $iCfg[4]) {
426 case 'EXPL_ALLOW':
427 if (!t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':ALLOW')) {
428 $out = FALSE;
429 }
430 break;
431 case 'EXPL_DENY':
432 if (t3lib_div::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
433 $out = FALSE;
434 }
435 break;
436 }
437 break;
438 }
439 }
440 }
441 }
442 break;
443 }
444
445 return $out;
446 }
447
448 /**
449 * Checking if a language value (-1, 0 and >0 for sys_language records) is allowed to be edited by the user.
450 *
451 * @param integer Language value to evaluate
452 * @return boolean Returns TRUE if the language value is allowed, otherwise FALSE.
453 */
454 function checkLanguageAccess($langValue) {
455 if (strcmp(trim($this->groupData['allowed_languages']), '')) { // The users language list must be non-blank - otherwise all languages are allowed.
456 $langValue = intval($langValue);
457 if ($langValue != -1 && !$this->check('allowed_languages', $langValue)) { // Language must either be explicitly allowed OR the lang Value be "-1" (all languages)
458 return FALSE;
459 }
460 }
461 return TRUE;
462 }
463
464 /**
465 * Check if user has access to all existing localizations for a certain record
466 *
467 * @param string the table
468 * @param array the current record
469 * @return boolean
470 */
471 function checkFullLanguagesAccess($table, $record) {
472 $recordLocalizationAccess = $this->checkLanguageAccess(0);
473 if ($recordLocalizationAccess
474 && (
475 t3lib_BEfunc::isTableLocalizable($table)
476 || isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])
477 )
478 ) {
479
480 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
481 $l10nTable = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
482 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
483 $pointerValue = $record['uid'];
484 } else {
485 $l10nTable = $table;
486 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
487 $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid'];
488 }
489
490 $recordLocalizations = t3lib_BEfunc::getRecordsByField(
491 $l10nTable,
492 $pointerField,
493 $pointerValue,
494 '',
495 '',
496 '',
497 '1'
498 );
499
500 if (is_array($recordLocalizations)) {
501 foreach ($recordLocalizations as $localization) {
502 $recordLocalizationAccess = $recordLocalizationAccess
503 && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]);
504 if (!$recordLocalizationAccess) {
505 break;
506 }
507 }
508 }
509
510 }
511 return $recordLocalizationAccess;
512 }
513
514 /**
515 * Checking if a user has editing access to a record from a $GLOBALS['TCA'] table.
516 * The checks does not take page permissions and other "environmental" things into account. It only deal with record internals; If any values in the record fields disallows it.
517 * For instance languages settings, authMode selector boxes are evaluated (and maybe more in the future).
518 * It will check for workspace dependent access.
519 * The function takes an ID (integer) or row (array) as second argument.
520 *
521 * @param string Table name
522 * @param mixed If integer, then this is the ID of the record. If Array this just represents fields in the record.
523 * @param boolean Set, if testing a new (non-existing) record array. Will disable certain checks that doesn't make much sense in that context.
524 * @param boolean Set, if testing a deleted record array.
525 * @param boolean Set, whenever access to all translations of the record is required
526 * @return boolean TRUE if OK, otherwise FALSE
527 */
528 function recordEditAccessInternals($table, $idOrRow, $newRecord = FALSE, $deletedRecord = FALSE, $checkFullLanguageAccess = FALSE) {
529 if (isset($GLOBALS['TCA'][$table])) {
530 t3lib_div::loadTCA($table);
531
532 // Always return TRUE for Admin users.
533 if ($this->isAdmin()) {
534 return TRUE;
535 }
536
537 // Fetching the record if the $idOrRow variable was not an array on input:
538 if (!is_array($idOrRow)) {
539 if ($deletedRecord) {
540 $idOrRow = t3lib_BEfunc::getRecord($table, $idOrRow, '*', '', FALSE);
541 } else {
542 $idOrRow = t3lib_BEfunc::getRecord($table, $idOrRow);
543 }
544 if (!is_array($idOrRow)) {
545 $this->errorMsg = 'ERROR: Record could not be fetched.';
546 return FALSE;
547 }
548 }
549
550 // Checking languages:
551 if ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
552 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) { // Language field must be found in input row - otherwise it does not make sense.
553 if (!$this->checkLanguageAccess($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
554 $this->errorMsg = 'ERROR: Language was not allowed.';
555 return FALSE;
556 } elseif ($checkFullLanguageAccess && $idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']] == 0 && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
557 $this->errorMsg = 'ERROR: Related/affected language was not allowed.';
558 return FALSE;
559 }
560 } else {
561 $this->errorMsg = 'ERROR: The "languageField" field named "' .
562 $GLOBALS['TCA'][$table]['ctrl']['languageField'] .
563 '" was not found in testing record!';
564 return FALSE;
565 }
566 } elseif (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && $checkFullLanguageAccess && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
567 return FALSE;
568 }
569
570 // Checking authMode fields:
571 if (is_array($GLOBALS['TCA'][$table]['columns'])) {
572 foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldValue) {
573 if (isset($idOrRow[$fieldName])) {
574 if ($fieldValue['config']['type'] == 'select' && $fieldValue['config']['authMode'] && !strcmp($fieldValue['config']['authMode_enforce'], 'strict')) {
575 if (!$this->checkAuthMode($table, $fieldName, $idOrRow[$fieldName], $fieldValue['config']['authMode'])) {
576 $this->errorMsg = 'ERROR: authMode "' . $fieldValue['config']['authMode'] . '" failed for field "' . $fieldName . '" with value "' . $idOrRow[$fieldName] . '" evaluated';
577 return FALSE;
578 }
579 }
580 }
581 }
582 }
583
584 // Checking "editlock" feature (doesn't apply to new records)
585 if (!$newRecord && $GLOBALS['TCA'][$table]['ctrl']['editlock']) {
586 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']])) {
587 if ($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']]) {
588 $this->errorMsg = 'ERROR: Record was locked for editing. Only admin users can change this state.';
589 return FALSE;
590 }
591 } else {
592 $this->errorMsg = 'ERROR: The "editLock" field named "' .
593 $GLOBALS['TCA'][$table]['ctrl']['editlock'] .
594 '" was not found in testing record!';
595 return FALSE;
596 }
597 }
598
599 // Checking record permissions
600 // THIS is where we can include a check for "perms_" fields for other records than pages...
601
602 // Process any hooks
603 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'])) {
604 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'] as $funcRef) {
605 $params = array(
606 'table' => $table,
607 'idOrRow' => $idOrRow,
608 'newRecord' => $newRecord
609 );
610 if (!t3lib_div::callUserFunction($funcRef, $params, $this)) {
611 return FALSE;
612 }
613 }
614 }
615
616 // Finally, return TRUE if all is well.
617 return TRUE;
618 }
619 }
620
621 /**
622 * Checks a type of permission against the compiled permission integer, $compiledPermissions, and in relation to table, $tableName
623 *
624 * @param integer $compiledPermissions could typically be the "compiled permissions" integer returned by ->calcPerms
625 * @param string $tableName is the tablename to check: If "pages" table then edit,new,delete and editcontent permissions can be checked. Other tables will be checked for "editcontent" only (and $type will be ignored)
626 * @param string For $tableName='pages' this can be 'edit' (2), 'new' (8 or 16), 'delete' (4), 'editcontent' (16). For all other tables this is ignored. (16 is used)
627 * @return boolean
628 * @access public (used by typo3/alt_clickmenu.php)
629 */
630 public function isPSet($compiledPermissions, $tableName, $actionType = '') {
631 if ($this->isAdmin()) {
632 $result = TRUE;
633 }
634 elseif ($tableName == 'pages') {
635 switch ($actionType) {
636 case 'edit':
637 $result = ($compiledPermissions & 2) !== 0;
638 break;
639 case 'new':
640 // Create new page OR page content
641 $result = ($compiledPermissions & (8 + 16)) !== 0;
642 break;
643 case 'delete':
644 $result = ($compiledPermissions & 4) !== 0;
645 break;
646 case 'editcontent':
647 $result = ($compiledPermissions & 16) !== 0;
648 break;
649 default:
650 $result = FALSE;
651 }
652 } else {
653 $result = ($compiledPermissions & 16) !== 0;
654 }
655 return $result;
656 }
657
658 /**
659 * Returns TRUE if the BE_USER is allowed to *create* shortcuts in the backend modules
660 *
661 * @return boolean
662 */
663 function mayMakeShortcut() {
664 return $this->getTSConfigVal('options.enableBookmarks') && !$this->getTSConfigVal('options.mayNotCreateEditBookmarks');
665 }
666
667 /**
668 * Checking if editing of an existing record is allowed in current workspace if that is offline.
669 * Rules for editing in offline mode:
670 * - record supports versioning and is an offline version from workspace and has the corrent stage
671 * - or record (any) is in a branch where there is a page which is a version from the workspace and where the stage is not preventing records
672 *
673 * @param string Table of record
674 * @param array Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
675 * @return string String error code, telling the failure state. FALSE=All ok
676 */
677 function workspaceCannotEditRecord($table, $recData) {
678
679 if ($this->workspace !== 0) { // Only test offline spaces:
680
681 if (!is_array($recData)) {
682 $recData = t3lib_BEfunc::getRecord($table, $recData, 'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : ''));
683 }
684
685 if (is_array($recData)) {
686 if ((int) $recData['pid'] === -1) { // We are testing a "version" (identified by a pid of -1): it can be edited provided that workspace matches and versioning is enabled for the table.
687 if (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) { // No versioning, basic error, inconsistency even! Such records should not have a pid of -1!
688 return 'Versioning disabled for table';
689 } elseif ((int) $recData['t3ver_wsid'] !== $this->workspace) { // So does workspace match?
690 return 'Workspace ID of record didn\'t match current workspace';
691 } else {
692 // So is the user allowed to "use" the edit stage within the workspace?
693 return $this->workspaceCheckStageForCurrent(0) ? FALSE : 'User\'s access level did not allow for editing';
694 }
695 } else { // We are testing a "live" record:
696 if ($res = $this->workspaceAllowLiveRecordsInPID($recData['pid'], $table)) { // For "Live" records, check that PID for table allows editing
697 // Live records are OK in this branch, but what about the stage of branch point, if any:
698 return $res > 0 ? FALSE : 'Stage for versioning root point and users access level did not allow for editing'; // OK
699 } else { // If not offline and not in versionized branch, output error:
700 return 'Online record was not in versionized branch!';
701 }
702 }
703 } else {
704 return 'No record';
705 }
706 } else {
707 return FALSE; // OK because workspace is 0
708 }
709 }
710
711 /**
712 * Evaluates if a user is allowed to edit the offline version
713 *
714 * @param string Table of record
715 * @param array Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
716 * @return string String error code, telling the failure state. FALSE=All ok
717 * @see workspaceCannotEditRecord()
718 */
719 function workspaceCannotEditOfflineVersion($table, $recData) {
720 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
721
722 if (!is_array($recData)) {
723 $recData = t3lib_BEfunc::getRecord($table, $recData, 'uid,pid,t3ver_wsid,t3ver_stage');
724 }
725 if (is_array($recData)) {
726 if ((int) $recData['pid'] === -1) {
727 return $this->workspaceCannotEditRecord($table, $recData);
728 } else {
729 return 'Not an offline version';
730 }
731 } else {
732 return 'No record';
733 }
734 } else {
735 return 'Table does not support versioning.';
736 }
737 }
738
739 /**
740 * Check if "live" records from $table may be created or edited in this PID.
741 * If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
742 * If the answer is 1 or 2 it means it is OK to create a record, if -1 it means that it is OK in terms of versioning because the element was within a versionized branch but NOT ok in terms of the state the root point had!
743 *
744 * @param integer PID value to check for.
745 * @param string Table name
746 * @return mixed Returns FALSE if a live record cannot be created and must be versionized in order to do so. 2 means a) Workspace is "Live" or workspace allows "live edit" of records from non-versionized tables (and the $table is not versionizable). 1 and -1 means the pid is inside a versionized branch where -1 means that the branch-point did NOT allow a new record according to its state.
747 */
748 function workspaceAllowLiveRecordsInPID($pid, $table) {
749
750 // Always for Live workspace AND if live-edit is enabled and tables are completely without versioning it is ok as well.
751 if ($this->workspace === 0 || ($this->workspaceRec['live_edit'] && !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) || $GLOBALS['TCA'][$table]['ctrl']['versioningWS_alwaysAllowLiveEdit']) {
752 return 2; // OK to create for this table.
753 } else {
754 return FALSE; // If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
755 }
756 }
757
758 /**
759 * Evaluates if a record from $table can be created in $pid
760 *
761 * @param integer Page id. This value must be the _ORIG_uid if available: So when you have pages versionized as "page" or "element" you must supply the id of the page version in the workspace!
762 * @param string Table name
763 * @return boolean TRUE if OK.
764 */
765 function workspaceCreateNewRecord($pid, $table) {
766 if ($res = $this->workspaceAllowLiveRecordsInPID($pid, $table)) {
767 // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
768 if ($res < 0) {
769 // Stage for versioning root point and users access level did not allow for editing
770 return FALSE;
771 }
772 } elseif (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
773 // So, if no live records were allowed, we have to create a new version of this record:
774 return FALSE;
775 }
776 return TRUE;
777 }
778
779 /**
780 * Evaluates if auto creation of a version of a record is allowed.
781 *
782 * @param string Table of the record
783 * @param integer UID of record
784 * @param integer PID of record
785 * @return boolean TRUE if ok.
786 */
787 function workspaceAllowAutoCreation($table, $id, $recpid) {
788 // Auto-creation of version: In offline workspace, test if versioning is
789 // enabled and look for workspace version of input record.
790 // If there is no versionized record found we will create one and save to that.
791 if ($this->workspace !== 0 // Only in draft workspaces
792 && !$this->workspaceRec['disable_autocreate'] // Auto-creation must not be disabled.
793 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] // Table must be versionizable
794 && $recpid >= 0 // The PID of the record must NOT be -1 or less (would indicate that it already was a version!)
795 && !t3lib_BEfunc::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid') // There must be no existing version of this record in workspace.
796 && !t3lib_BEfunc::isPidInVersionizedBranch($recpid, $table)) { // PID must NOT be in a versionized branch either
797 return TRUE;
798 } elseif ($this->workspaceRec['disable_autocreate']) {
799 t3lib_div::deprecationLog('Usage of disable_autocreate feature is deprecated since 4.5.');
800 }
801 }
802
803 /**
804 * Checks if an element stage allows access for the user in the current workspace
805 * In live workspace (= 0) access is always granted for any stage.
806 * Admins are always allowed.
807 * An option for custom workspaces allows members to also edit when the stage is "Review"
808 *
809 * @param integer Stage id from an element: -1,0 = editing, 1 = reviewer, >1 = owner
810 * @return boolean TRUE if user is allowed access
811 */
812 function workspaceCheckStageForCurrent($stage) {
813 // always allow for admins
814 if ($this->isAdmin()) {
815 return TRUE;
816 }
817
818 if ($this->workspace !== 0 && t3lib_extMgm::isLoaded('workspaces')) {
819 $stage = intval($stage);
820 $stat = $this->checkWorkspaceCurrent();
821
822 // Check if custom staging is activated
823 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']);
824 if ($workspaceRec['custom_stages'] > 0 && $stage !== 0 && $stage !== -10) {
825
826 // Get custom stage record
827 $workspaceStageRec = t3lib_BEfunc::getRecord('sys_workspace_stage', $stage);
828 // Check if the user is responsible for the current stage
829 if ((t3lib_div::inList($workspaceStageRec['responsible_persons'], 'be_users_' . $this->user['uid'])
830 && $stat['_ACCESS'] === 'member')
831 || $stat['_ACCESS'] === 'owner') {
832 return TRUE;
833 }
834
835 // Check if the user is in a group which is responsible for the current stage
836 foreach ($this->userGroupsUID as $groupUid) {
837 if ((t3lib_div::inList($workspaceStageRec['responsible_persons'], 'be_groups_' . $groupUid)
838 && $stat['_ACCESS'] === 'member')
839 || $stat['_ACCESS'] === 'owner') {
840 return TRUE;
841 }
842 }
843 // only owner is allowed to change records which are "ready to publish"
844 } elseif ($stage == -10 || $stage == -20) {
845 if ($stat['_ACCESS'] === 'owner') {
846 return TRUE;
847 } else {
848 return FALSE;
849 }
850 } else {
851 $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
852 if (($stage <= $memberStageLimit && $stat['_ACCESS'] === 'member')
853 || ($stage <= 1 && $stat['_ACCESS'] === 'reviewer')
854 || $stat['_ACCESS'] === 'owner') {
855 return TRUE;
856 }
857 }
858 } else {
859 // Always OK for live workspace.
860 return TRUE;
861 }
862 }
863
864 /**
865 * Returns TRUE if the user has access to publish content from the workspace ID given.
866 * Admin-users are always granted access to do this
867 * If the workspace ID is 0 (live) all users have access also
868 * For custom workspaces it depends on whether the user is owner OR like with draft workspace if the user has access to Live workspace.
869 *
870 * @param integer Workspace UID; 0,1+
871 * @return boolean Returns TRUE if the user has access to publish content from the workspace ID given.
872 */
873 function workspacePublishAccess($wsid) {
874 if ($this->isAdmin()) {
875 return TRUE;
876 }
877
878 // If no access to workspace, of course you cannot publish!
879 $retVal = FALSE;
880
881 $wsAccess = $this->checkWorkspace($wsid);
882 if ($wsAccess) {
883 switch ($wsAccess['uid']) {
884 case 0: // Live workspace
885 $retVal = TRUE; // If access to Live workspace, no problem.
886 break;
887 default: // Custom workspace
888 $retVal = $wsAccess['_ACCESS'] === 'owner' || ($this->checkWorkspace(0) && !($wsAccess['publish_access'] & 2)); // Either be an adminuser OR have access to online workspace which is OK as well as long as publishing access is not limited by workspace option.
889 break;
890 }
891 }
892 return $retVal;
893 }
894
895 /**
896 * Workspace swap-mode access?
897 *
898 * @return boolean Returns TRUE if records can be swapped in the current workspace, otherwise FALSE
899 */
900 function workspaceSwapAccess() {
901 if ($this->workspace > 0 && (int) $this->workspaceRec['swap_modes'] === 2) {
902 return FALSE;
903 } else {
904 return TRUE;
905 }
906 }
907
908 /**
909 * Workspace Versioning type access. Check wether the requsted type of versioning (element/page/branch) is allowd in current workspace
910 * (element/pages/branches type of versioning can/could be set on custom workspaces on filed "vtype")
911 *
912 * @todo workspacecleanup: this seems mostly obsolete and should be removed
913 * @param integer Versioning type to evaluation: -1, 0, >1
914 * 0 = page (deprecated)
915 * -1 = element
916 * >1 = branch (deprecated), indicating the "nesting" level
917 * @return boolean TRUE if OK
918 * @deprecated since TYPO3 4.4, will be removed in TYPO3 4.8 as only element versioning is supported now
919 */
920 function workspaceVersioningTypeAccess($type) {
921 t3lib_div::logDeprecatedFunction();
922
923 $type = t3lib_utility_Math::forceIntegerInRange($type, -1);
924
925 // only element versioning is allowed now
926 return $type == -1;
927 }
928
929 /**
930 * Finding "closest" versioning type, used for creation of new records.
931 *
932 * @see workspaceVersioningTypeAccess() for hints on $type
933 * @param integer Versioning type to evaluation: -1, 0, >1
934 * @return integer Returning versioning type
935 * @deprecated since TYPO3 4.4, will be removed in TYPO3 4.8 as only element versioning is supported now
936 */
937 function workspaceVersioningTypeGetClosest($type) {
938 t3lib_div::logDeprecatedFunction();
939
940 $type = t3lib_utility_Math::forceIntegerInRange($type, -1);
941
942 if ($this->workspace > 0) {
943 switch ((int) $type) {
944 case -1:
945 $type = -1;
946 break;
947 case 0:
948 $type = $this->workspaceVersioningTypeAccess($type) ? $type : -1;
949 break;
950 default:
951 $type = $this->workspaceVersioningTypeAccess($type) ? $type : ($this->workspaceVersioningTypeAccess(0) ? 0 : -1);
952 break;
953 }
954 }
955 return $type;
956 }
957
958
959 /*************************************
960 *
961 * Miscellaneous functions
962 *
963 *************************************/
964
965 /**
966 * Returns the value/properties of a TS-object as given by $objectString, eg. 'options.dontMountAdminMounts'
967 * Nice (general!) function for returning a part of a TypoScript array!
968 *
969 * @param string Pointer to an "object" in the TypoScript array, fx. 'options.dontMountAdminMounts'
970 * @param array Optional TSconfig array: If array, then this is used and not $this->userTS. If not array, $this->userTS is used.
971 * @return array An array with two keys, "value" and "properties" where "value" is a string with the value of the objectsting and "properties" is an array with the properties of the objectstring.
972 * @params array An array with the TypoScript where the $objectString is located. If this argument is not an array, then internal ->userTS (User TSconfig for the current BE_USER) will be used instead.
973 */
974 function getTSConfig($objectString, $config = '') {
975 if (!is_array($config)) {
976 $config = $this->userTS; // Getting Root-ts if not sent
977 }
978 $TSConf = array();
979 $parts = explode('.', $objectString, 2);
980 $key = $parts[0];
981 if (trim($key)) {
982 if (count($parts) > 1 && trim($parts[1])) {
983 // Go on, get the next level
984 if (is_array($config[$key . '.'])) {
985 $TSConf = $this->getTSConfig($parts[1], $config[$key . '.']);
986 }
987 } else {
988 $TSConf['value'] = $config[$key];
989 $TSConf['properties'] = $config[$key . '.'];
990 }
991 }
992 return $TSConf;
993 }
994
995 /**
996 * Returns the "value" of the $objectString from the BE_USERS "User TSconfig" array
997 *
998 * @param string Object string, eg. "somestring.someproperty.somesubproperty"
999 * @return string The value for that object string (object path)
1000 * @see getTSConfig()
1001 */
1002 function getTSConfigVal($objectString) {
1003 $TSConf = $this->getTSConfig($objectString);
1004 return $TSConf['value'];
1005 }
1006
1007 /**
1008 * Returns the "properties" of the $objectString from the BE_USERS "User TSconfig" array
1009 *
1010 * @param string Object string, eg. "somestring.someproperty.somesubproperty"
1011 * @return array The properties for that object string (object path) - if any
1012 * @see getTSConfig()
1013 */
1014 function getTSConfigProp($objectString) {
1015 $TSConf = $this->getTSConfig($objectString);
1016 return $TSConf['properties'];
1017 }
1018
1019 /**
1020 * Returns TRUE if $item is in $in_list
1021 *
1022 * @param string Comma list with items, no spaces between items!
1023 * @param string The string to find in the list of items
1024 * @return string Boolean
1025 * @deprecated since TYPO3 4.7, should be removed in TYPO3 4.9, use equivalent function t3lib_div::inList()
1026 */
1027 function inList($in_list, $item) {
1028 t3lib_div::logDeprecatedFunction();
1029 return t3lib_div::inList($in_list, $item);
1030 }
1031
1032 /**
1033 * Returns an array with the webmounts.
1034 * If no webmounts, and empty array is returned.
1035 * NOTICE: Deleted pages WILL NOT be filtered out! So if a mounted page has been deleted it is STILL coming out as a webmount. This is not checked due to performance.
1036 *
1037 * @return array
1038 */
1039 function returnWebmounts() {
1040 return (string) ($this->groupData['webmounts']) != '' ? explode(',', $this->groupData['webmounts']) : array();
1041 }
1042
1043
1044 /**
1045 * Returns TRUE or FALSE, depending if an alert popup (a javascript confirmation) should be shown
1046 * call like $GLOBALS['BE_USER']->jsConfirmation($BITMASK)
1047 *
1048 * 1 - typeChange
1049 * 2 - copy/move/paste
1050 * 4 - delete
1051 * 8 - frontend editing
1052 * 128 - other (not used yet)
1053 *
1054 * @param integer Bitmask
1055 * @return boolean TRUE if the confirmation should be shown
1056 */
1057 function jsConfirmation($bitmask) {
1058 $alertPopup = $GLOBALS['BE_USER']->getTSConfig('options.alertPopups');
1059 if (empty($alertPopup['value'])) {
1060 $alertPopup = 255; // default: show all warnings
1061 } else {
1062 $alertPopup = (int) $alertPopup['value'];
1063 }
1064 if (($alertPopup & $bitmask) == $bitmask) { // show confirmation
1065 return 1;
1066 } else { // don't show confirmation
1067 return 0;
1068 }
1069 }
1070
1071
1072 /*************************************
1073 *
1074 * Authentication methods
1075 *
1076 *************************************/
1077
1078
1079 /**
1080 * Initializes a lot of stuff like the access-lists, database-mountpoints and filemountpoints
1081 * This method is called by ->backendCheckLogin() (from extending class t3lib_beuserauth) if the backend user login has verified OK.
1082 * Generally this is required initialization of a backend user.
1083 *
1084 * @return void
1085 * @access private
1086 * @see t3lib_TSparser
1087 */
1088 function fetchGroupData() {
1089 if ($this->user['uid']) {
1090
1091 // Get lists for the be_user record and set them as default/primary values.
1092 $this->dataLists['modList'] = $this->user['userMods']; // Enabled Backend Modules
1093 $this->dataLists['allowed_languages'] = $this->user['allowed_languages']; // Add Allowed Languages
1094 $this->dataLists['workspace_perms'] = $this->user['workspace_perms']; // Set user value for workspace permissions.
1095 $this->dataLists['webmount_list'] = $this->user['db_mountpoints']; // Database mountpoints
1096 $this->dataLists['filemount_list'] = $this->user['file_mountpoints']; // File mountpoints
1097 $this->dataLists['fileoper_perms'] = (int) $this->user['fileoper_perms']; // Fileoperation permissions
1098
1099 // Setting default User TSconfig:
1100 $this->TSdataArray[] = $this->addTScomment('From $GLOBALS["TYPO3_CONF_VARS"]["BE"]["defaultUserTSconfig"]:') .
1101 $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'];
1102
1103 // Default TSconfig for admin-users
1104 if ($this->isAdmin()) {
1105 $this->TSdataArray[] = $this->addTScomment('"admin" user presets:') . '
1106 admPanel.enable.all = 1
1107 ';
1108 if (t3lib_extMgm::isLoaded('sys_note')) {
1109 $this->TSdataArray[] = '
1110 // Setting defaults for sys_note author / email...
1111 TCAdefaults.sys_note.author = ' . $this->user['realName'] . '
1112 TCAdefaults.sys_note.email = ' . $this->user['email'] . '
1113 ';
1114 }
1115 }
1116
1117 // BE_GROUPS:
1118 // Get the groups...
1119 # $grList = t3lib_BEfunc::getSQLselectableList($this->user[$this->usergroup_column],$this->usergroup_table,$this->usergroup_table);
1120 $grList = $GLOBALS['TYPO3_DB']->cleanIntList($this->user[$this->usergroup_column]); // 240203: Since the group-field never contains any references to groups with a prepended table name we think it's safe to just intExplode and re-implode - which should be much faster than the other function call.
1121 if ($grList) {
1122 // Fetch groups will add a lot of information to the internal arrays: modules, accesslists, TSconfig etc. Refer to fetchGroups() function.
1123 $this->fetchGroups($grList);
1124 }
1125
1126 // Add the TSconfig for this specific user:
1127 $this->TSdataArray[] = $this->addTScomment('USER TSconfig field') . $this->user['TSconfig'];
1128 // Check include lines.
1129 $this->TSdataArray = t3lib_TSparser::checkIncludeLines_array($this->TSdataArray);
1130
1131 $this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray); // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
1132
1133 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['TSconfigConditions'] && !$this->userTS_dontGetCached) {
1134 // Perform TS-Config parsing with condition matching
1135 $parseObj = t3lib_div::makeInstance('t3lib_TSparser_TSconfig');
1136 $res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
1137 if ($res) {
1138 $this->userTS = $res['TSconfig'];
1139 $this->userTSUpdated = ($res['cached'] ? 0 : 1);
1140 }
1141 } else {
1142 // Parsing the user TSconfig (or getting from cache)
1143 $hash = md5('userTS:' . $this->userTS_text);
1144 $cachedContent = t3lib_BEfunc::getHash($hash);
1145 if (isset($cachedContent) && !$this->userTS_dontGetCached) {
1146 $this->userTS = unserialize($cachedContent);
1147 } else {
1148 $parseObj = t3lib_div::makeInstance('t3lib_TSparser');
1149 $parseObj->parse($this->userTS_text);
1150 $this->userTS = $parseObj->setup;
1151 t3lib_BEfunc::storeHash($hash, serialize($this->userTS), 'BE_USER_TSconfig');
1152 // Update UC:
1153 $this->userTSUpdated = 1;
1154 }
1155 }
1156
1157 // Processing webmounts
1158 if ($this->isAdmin() && !$this->getTSConfigVal('options.dontMountAdminMounts')) { // Admin's always have the root mounted
1159 $this->dataLists['webmount_list'] = '0,' . $this->dataLists['webmount_list'];
1160 }
1161
1162 // The lists are cleaned for duplicates
1163 $this->groupData['webmounts'] = t3lib_div::uniqueList($this->dataLists['webmount_list']);
1164 $this->groupData['pagetypes_select'] = t3lib_div::uniqueList($this->dataLists['pagetypes_select']);
1165 $this->groupData['tables_select'] = t3lib_div::uniqueList($this->dataLists['tables_modify'] . ',' . $this->dataLists['tables_select']);
1166 $this->groupData['tables_modify'] = t3lib_div::uniqueList($this->dataLists['tables_modify']);
1167 $this->groupData['non_exclude_fields'] = t3lib_div::uniqueList($this->dataLists['non_exclude_fields']);
1168 $this->groupData['explicit_allowdeny'] = t3lib_div::uniqueList($this->dataLists['explicit_allowdeny']);
1169 $this->groupData['allowed_languages'] = t3lib_div::uniqueList($this->dataLists['allowed_languages']);
1170 $this->groupData['custom_options'] = t3lib_div::uniqueList($this->dataLists['custom_options']);
1171 $this->groupData['modules'] = t3lib_div::uniqueList($this->dataLists['modList']);
1172 $this->groupData['fileoper_perms'] = $this->dataLists['fileoper_perms'];
1173 $this->groupData['workspace_perms'] = $this->dataLists['workspace_perms'];
1174
1175 // populating the $this->userGroupsUID -array with the groups in the order in which they were LAST included.!!
1176 $this->userGroupsUID = array_reverse(array_unique(array_reverse($this->includeGroupArray)));
1177
1178 // Finally this is the list of group_uid's in the order they are parsed (including subgroups!) and without duplicates (duplicates are presented with their last entrance in the list, which thus reflects the order of the TypoScript in TSconfig)
1179 $this->groupList = implode(',', $this->userGroupsUID);
1180 $this->setCachedList($this->groupList);
1181
1182 // Checking read access to webmounts:
1183 if (trim($this->groupData['webmounts']) !== '') {
1184 $webmounts = explode(',', $this->groupData['webmounts']); // Explode mounts
1185 $MProws = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'deleted=0 AND uid IN (' . $this->groupData['webmounts'] . ') AND ' . $this->getPagePermsClause(1), '', '', '', 'uid'); // Selecting all webmounts with permission clause for reading
1186 foreach ($webmounts as $idx => $mountPointUid) {
1187 if ($mountPointUid > 0 && !isset($MProws[$mountPointUid])) { // If the mount ID is NOT found among selected pages, unset it:
1188 unset($webmounts[$idx]);
1189 }
1190 }
1191 $this->groupData['webmounts'] = implode(',', $webmounts); // Implode mounts in the end.
1192 }
1193
1194 // Setting up workspace situation (after webmounts are processed!):
1195 $this->workspaceInit();
1196 }
1197 }
1198
1199 /**
1200 * Fetches the group records, subgroups and fills internal arrays.
1201 * Function is called recursively to fetch subgroups
1202 *
1203 * @param string Commalist of be_groups uid numbers
1204 * @param string List of already processed be_groups-uids so the function will not fall into a eternal recursion.
1205 * @return void
1206 * @access private
1207 */
1208 function fetchGroups($grList, $idList = '') {
1209 // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
1210 $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=\'' . t3lib_div::getIndpEnv('HTTP_HOST') . '\')';
1211 $whereSQL = 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $grList . ')' . $lockToDomain_SQL;
1212
1213 // Hook for manipulation of the WHERE sql sentence which controls which BE-groups are included
1214 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'])) {
1215 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'] as $classRef) {
1216 $hookObj = t3lib_div::getUserObj($classRef);
1217 if (method_exists($hookObj, 'fetchGroupQuery_processQuery')) {
1218 $whereSQL = $hookObj->fetchGroupQuery_processQuery($this, $grList, $idList, $whereSQL);
1219 }
1220 }
1221 }
1222
1223 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->usergroup_table, $whereSQL);
1224
1225 // The userGroups array is filled
1226 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1227 $this->userGroups[$row['uid']] = $row;
1228 }
1229
1230 // Traversing records in the correct order
1231 $include_staticArr = t3lib_div::intExplode(',', $grList);
1232 // traversing list
1233 foreach ($include_staticArr as $key => $uid) {
1234
1235 // Get row:
1236 $row = $this->userGroups[$uid];
1237 if (is_array($row) && !t3lib_div::inList($idList, $uid)) { // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
1238
1239 // Include sub groups
1240 if (trim($row['subgroup'])) {
1241 $theList = implode(',', t3lib_div::intExplode(',', $row['subgroup'])); // Make integer list
1242 $this->fetchGroups($theList, $idList . ',' . $uid); // Call recursively, pass along list of already processed groups so they are not recursed again.
1243 }
1244 // Add the group uid, current list, TSconfig to the internal arrays.
1245 $this->includeGroupArray[] = $uid;
1246 $this->includeHierarchy[] = $idList;
1247 $this->TSdataArray[] = $this->addTScomment('Group "' . $row['title'] . '" [' . $row['uid'] . '] TSconfig field:') . $row['TSconfig'];
1248
1249 // Mount group database-mounts
1250 if (($this->user['options'] & 1) == 1) {
1251 $this->dataLists['webmount_list'] .= ',' . $row['db_mountpoints'];
1252 }
1253
1254 // Mount group file-mounts
1255 if (($this->user['options'] & 2) == 2) {
1256 $this->dataLists['filemount_list'] .= ',' . $row['file_mountpoints'];
1257 }
1258
1259 // The lists are made: groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny, allowed_languages, custom_options
1260 if ($row['inc_access_lists'] == 1) {
1261 $this->dataLists['modList'] .= ',' . $row['groupMods'];
1262 $this->dataLists['tables_select'] .= ',' . $row['tables_select'];
1263 $this->dataLists['tables_modify'] .= ',' . $row['tables_modify'];
1264 $this->dataLists['pagetypes_select'] .= ',' . $row['pagetypes_select'];
1265 $this->dataLists['non_exclude_fields'] .= ',' . $row['non_exclude_fields'];
1266 $this->dataLists['explicit_allowdeny'] .= ',' . $row['explicit_allowdeny'];
1267 $this->dataLists['allowed_languages'] .= ',' . $row['allowed_languages'];
1268 $this->dataLists['custom_options'] .= ',' . $row['custom_options'];
1269 }
1270
1271 // Setting fileoperation permissions
1272 $this->dataLists['fileoper_perms'] |= (int) $row['fileoper_perms'];
1273
1274 // Setting workspace permissions:
1275 $this->dataLists['workspace_perms'] |= $row['workspace_perms'];
1276
1277 // If this function is processing the users OWN group-list (not subgroups) AND if the ->firstMainGroup is not set, then the ->firstMainGroup will be set.
1278 if (!strcmp($idList, '') && !$this->firstMainGroup) {
1279 $this->firstMainGroup = $uid;
1280 }
1281 }
1282 }
1283
1284 // ****************
1285 // HOOK: fetchGroups_postProcessing
1286 // ****************
1287 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'])) {
1288 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'] as $_funcRef) {
1289 $_params = array();
1290 t3lib_div::callUserFunction($_funcRef, $_params, $this);
1291 }
1292 }
1293 }
1294
1295 /**
1296 * Updates the field be_users.usergroup_cached_list if the groupList of the user has changed/is different from the current list.
1297 * The field "usergroup_cached_list" contains the list of groups which the user is a member of. After authentication (where these functions are called...) one can depend on this list being a representation of the exact groups/subgroups which the BE_USER has membership with.
1298 *
1299 * @param string The newly compiled group-list which must be compared with the current list in the user record and possibly stored if a difference is detected.
1300 * @return void
1301 * @access private
1302 */
1303 function setCachedList($cList) {
1304 if ((string) $cList != (string) $this->user['usergroup_cached_list']) {
1305 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('usergroup_cached_list' => $cList));
1306 }
1307 }
1308
1309
1310
1311 /***********************
1312 * FILE MOUNT FUNCTIONS
1313 **********************/
1314
1315 /**
1316 * Sets up all file storages for a user.
1317 * Needs to be called AFTER the groups have been loaded.
1318 *
1319 * @return void
1320 */
1321 protected function initializeFileStorages() {
1322 $this->fileStorages = array();
1323 /** @var $storageRepository t3lib_file_Repository_StorageRepository */
1324 $storageRepository = t3lib_div::makeInstance('t3lib_file_Repository_StorageRepository');
1325
1326 // Admin users have all file storages visible, without any filters
1327 if ($this->isAdmin()) {
1328 $storageObjects = $storageRepository->findAll();
1329 foreach ($storageObjects as $storageObject) {
1330 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1331 }
1332 } else {
1333 // If userHomePath is set, we attempt to mount it
1334 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) {
1335 list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2);
1336 $userHomeStorageUid = intval($userHomeStorageUid);
1337 if ($userHomeStorageUid > 0) {
1338 $storageObject = $storageRepository->findByUid($userHomeStorageUid);
1339 // First try and mount with [uid]_[username]
1340 $userHomeFilterIdentifier = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1341 $didMount = $storageObject->injectFileMount($userHomeFilterIdentifier);
1342 // If that failed, try and mount with only [uid]
1343 if (!$didMount) {
1344 $userHomeFilterIdentifier = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1345 $storageObject->injectFileMount($userHomeFilterIdentifier);
1346 }
1347 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1348 }
1349 }
1350
1351 // Mount group home-dirs
1352 if (($this->user['options'] & 2) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') {
1353 // If groupHomePath is set, we attempt to mount it
1354 list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2);
1355 $groupHomeStorageUid = intval($groupHomeStorageUid);
1356 if ($groupHomeStorageUid > 0) {
1357 $storageObject = $storageRepository->findByUid($groupHomeStorageUid);
1358 foreach ($this->userGroups as $groupUid => $groupData) {
1359 $groupHomeFilterIdentifier = $groupHomeFilter . $groupData['uid'];
1360 $storageObject->injectFileMount($groupHomeFilterIdentifier);
1361 }
1362 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1363 }
1364 }
1365
1366 // Processing filemounts (both from the user and the groups)
1367 $this->dataLists['filemount_list'] = t3lib_div::uniqueList($this->dataLists['filemount_list']);
1368 if ($this->dataLists['filemount_list']) {
1369 $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ? $GLOBALS['TYPO3_DB']->stripOrderBy($GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby']) : 'sorting';
1370 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
1371 '*',
1372 'sys_filemounts',
1373 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $this->dataLists['filemount_list'] . ')',
1374 '',
1375 $orderBy
1376 );
1377
1378 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1379 $storageObject = $storageRepository->findByUid($row['base']);
1380 $storageObject->injectFileMount($row['path'], $row);
1381 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1382 }
1383 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1384 }
1385 }
1386
1387 // Injects the users' permissions to each storage
1388 foreach ($this->fileStorages as $storageObject) {
1389 $storagePermissions = $this->getFilePermissionsForStorage($storageObject);
1390 $storageObject->injectUserPermissions($storagePermissions);
1391 }
1392
1393 // more narrowing down through the workspace
1394 $this->initializeFileStoragesForWorkspace();
1395
1396 // this has to be called always in order to set certain filters
1397 // @todo Should be in BE_USER object then
1398 $GLOBALS['BE_USER']->evaluateUserSpecificFileFilterSettings();
1399 }
1400
1401 /**
1402 * Returns an array with the filemounts for the user.
1403 * Each filemount is represented with an array of a "name", "path" and "type".
1404 * If no filemounts an empty array is returned.
1405 *
1406 * @api
1407 * @return t3lib_file_Storage[]
1408 */
1409 public function getFileStorages() {
1410 // initializing file mounts after the groups are fetched
1411 if ($this->fileStorages === NULL) {
1412 $this->initializeFileStorages();
1413 }
1414
1415 return $this->fileStorages;
1416 }
1417
1418 /**
1419 * Adds filters based on what the user has set
1420 * this should be done in this place, and called whenever needed,
1421 * but only when needed
1422 *
1423 * @return void
1424 * @todo Should be in BE_USER object then
1425 */
1426 public function evaluateUserSpecificFileFilterSettings() {
1427 // add the option for also displaying the non-hidden files
1428 if ($this->uc['showHiddenFilesAndFolders']) {
1429 t3lib_file_Utility_FilenameFilters::setShowHiddenFilesAndFolders(TRUE);
1430 }
1431 }
1432
1433 /**
1434 * Returns the information about file permissions
1435 * previously, this was stored in the DB fields (fileoper_perms)
1436 * but is now handled via userTSconfig
1437 *
1438 * permissions.file.default {
1439 * addFile = 1
1440 * readFile = 1
1441 * editFile = 1
1442 * writeFile = 1
1443 * uploadFile = 1
1444 * copyFile = 1
1445 * moveFile = 1
1446 * renameFile = 1
1447 * unzipFile = 1
1448 * removeFile = 1
1449 *
1450 * addFolder = 1
1451 * browseFolder = 1
1452 * moveFolder = 1
1453 * writeFolder = 1
1454 * renameFolder = 1
1455 * removeFolder = 1
1456 * removeSubfolders = 1
1457 * }
1458 *
1459 * # overwrite settings for a specific storageObject
1460 * permissions.file.storage.StorageUid {
1461 * readFile = 0
1462 * removeSubfolders = 1
1463 * }
1464 *
1465 * Please note that these permissions only apply, if the storage has the
1466 * capabilities (browseable, writable), and if the driver allows for writing etc
1467 *
1468 * @api
1469 * @return array
1470 */
1471 public function getFilePermissions() {
1472 if (!isset($this->filePermissions)) {
1473 $defaultOptions = array(
1474 'addFile' => TRUE, // new option
1475 'readFile' => TRUE, // new option, generic check of the user rights
1476 'editFile' => TRUE, // new option
1477 'writeFile' => TRUE, // new option, generic check of the user rights
1478 'uploadFile' => TRUE,
1479 'copyFile' => TRUE,
1480 'moveFile' => TRUE,
1481 'renameFile' => TRUE,
1482 'unzipFile' => TRUE,
1483 'removeFile' => TRUE,
1484 'addFolder' => TRUE,
1485 'browseFolder' => TRUE, // new option,, generic check of the user rights
1486 'moveFolder' => TRUE,
1487 'renameFolder' => TRUE,
1488 'writeFolder' => TRUE, // new option, generic check of the user rights
1489 'removeFolder' => TRUE,
1490 'removeSubfolders' => TRUE // was "delete recursively" previously
1491 );
1492
1493 if (!$this->isAdmin()) {
1494 $this->filePermissions = $this->getTSConfig('permissions.file.default');
1495 if (!is_array($this->filePermissions)) {
1496 $oldFileOperationPermissions = $this->getFileoperationPermissions();
1497
1498 // lower permissions if the old file operation permissions are not set
1499 if ($oldFileOperationPermissions ^ 1) {
1500 $defaultOptions['addFile'] = FALSE;
1501 $defaultOptions['uploadFile'] = FALSE;
1502 $defaultOptions['copyFile'] = FALSE;
1503 $defaultOptions['moveFile'] = FALSE;
1504 $defaultOptions['renameFile'] = FALSE;
1505 $defaultOptions['removeFile'] = FALSE;
1506 }
1507 if ($oldFileOperationPermissions ^ 2) {
1508 $defaultOptions['unzipFile'] = FALSE;
1509 }
1510 if ($oldFileOperationPermissions ^ 4) {
1511 $defaultOptions['addFolder'] = FALSE;
1512 $defaultOptions['moveFolder'] = FALSE;
1513 $defaultOptions['renameFolder'] = FALSE;
1514 $defaultOptions['removeFolder'] = FALSE;
1515 }
1516 if ($oldFileOperationPermissions ^ 8) {
1517 $defaultOptions['copyFolder'] = FALSE;
1518 }
1519 if ($oldFileOperationPermissions ^ 16) {
1520 $defaultOptions['removeSubfolders'] = FALSE;
1521 }
1522 }
1523 }
1524
1525 $this->filePermissions = $defaultOptions;
1526 }
1527
1528 return $this->filePermissions;
1529 }
1530
1531 /**
1532 * Gets the file permissions for a storage
1533 * by merging any storage-specific permissions for a
1534 * storage with the default settings
1535 *
1536 * @api
1537 * @param t3lib_file_Storage $storageObject
1538 * @return array
1539 */
1540 public function getFilePermissionsForStorage(t3lib_file_Storage $storageObject) {
1541 $defaultFilePermissions = $this->getFilePermissions();
1542 $storageFilePermissions = $this->getTSConfig('permissions.file.storage.' . $storageObject->getUid());
1543
1544 if (is_array($storageFilePermissions) && count($storageFilePermissions)) {
1545 return array_merge($storageFilePermissions, $defaultFilePermissions);
1546 } else {
1547 return $defaultFilePermissions;
1548 }
1549 }
1550
1551 /**
1552 * Returns a t3lib_file_Folder object that is used for uploading
1553 * files by default.
1554 * This is used for RTE and its magic images, as well as uploads
1555 * in the TCEforms fields, unless otherwise configured (will be added in the future)
1556 *
1557 * the default upload folder for a user is the defaultFolder on the first
1558 * filestorage/filemount that the user can access
1559 * however, you can set the users' upload folder like this:
1560 *
1561 * options.defaultUploadFolder = 3:myfolder/yourfolder/
1562 *
1563 * @return t3lib_file_Folder|boolean The default upload folder for this user
1564 */
1565 public function getDefaultUploadFolder() {
1566 $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1567 if ($uploadFolder) {
1568 $uploadFolder = t3lib_file_Factory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1569 } else {
1570 $storages = $this->getFileStorages();
1571 if (count($storages) > 0) {
1572 /** @var $firstStorage t3lib_file_Storage */
1573 $firstStorage = reset($storages);
1574 $uploadFolder = $firstStorage->getDefaultFolder();
1575 }
1576 }
1577
1578 if ($uploadFolder instanceof t3lib_file_Folder) {
1579 return $uploadFolder;
1580 } else {
1581 return FALSE;
1582 }
1583 }
1584
1585 /**
1586 * Returns an integer bitmask that represents the permissions for file operations.
1587 * Permissions of the user and groups the user is a member of were combined by a logical OR.
1588 *
1589 * Meaning of each bit:
1590 * 1 - Files: Upload,Copy,Move,Delete,Rename
1591 * 2 - Files: Unzip
1592 * 4 - Directory: Move,Delete,Rename,New
1593 * 8 - Directory: Copy
1594 * 16 - Directory: Delete recursively (rm -Rf)
1595 *
1596 * @return integer File operation permission bitmask
1597 * @deprecated since TYPO3 6.0, use the TSconfig settings instead
1598 */
1599 public function getFileoperationPermissions() {
1600 t3lib_div::logDeprecatedFunction();
1601
1602 if ($this->isAdmin()) {
1603 return 31;
1604 } else {
1605 return $this->groupData['fileoper_perms'];
1606 }
1607 }
1608
1609 /**
1610 * Adds a filemount to the users array of filemounts, $this->groupData['filemounts'][hash_key] = Array ('name'=>$name, 'path'=>$path, 'type'=>$type);
1611 * Is a part of the authentication proces of the user.
1612 * A final requirement for a path being mounted is that a) it MUST return TRUE on is_dir(), b) must contain either PATH_site+'fileadminDir' OR 'lockRootPath' - if lockRootPath is set - as first part of string!
1613 * Paths in the mounted information will always be absolute and have a trailing slash.
1614 *
1615 * @param string $title will be the (root)name of the filemount in the folder tree
1616 * @param string $altTitle will be the (root)name of the filemount IF $title is not TRUE (blank or zero)
1617 * @param string $path is the path which should be mounted. Will accept backslash in paths on windows servers (will substituted with forward slash). The path should be 1) relative to TYPO3_CONF_VARS[BE][fileadminDir] if $webspace is set, otherwise absolute.
1618 * @param boolean If $webspace is set, the $path is relative to 'fileadminDir' in TYPO3_CONF_VARS, otherwise $path is absolute. 'fileadminDir' must be set to allow mounting of relative paths.
1619 * @param string Type of filemount; Can be blank (regular) or "user" / "group" (for user and group filemounts presumably). Probably sets the icon first and foremost.
1620 * @return boolean Returns "1" if the requested filemount was mounted, otherwise no return value.
1621 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1, all data is stored in $this->fileStorages now, see initializeFileStorages()
1622 * @access private
1623 */
1624 function addFileMount($title, $altTitle, $path, $webspace, $type) {
1625 t3lib_div::logDeprecatedFunction();
1626
1627 // Return FALSE if fileadminDir is not set and we try to mount a relative path
1628 if ($webspace && !$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) {
1629 return FALSE;
1630 }
1631
1632 // Trimming and pre-processing
1633 $path = trim($path);
1634 if ($this->OS == 'WIN') { // with WINDOWS convert backslash to slash!!
1635 $path = str_replace('\\', '/', $path);
1636 }
1637 // If the path is TRUE and validates as a valid path string:
1638 if ($path && t3lib_div::validPathStr($path)) {
1639 // normalize path: remove leading '/' and './', and trailing '/' and '/.'
1640 $path = trim($path);
1641 $path = preg_replace('#^\.?/|/\.?$#', '', $path);
1642
1643 if ($path) { // there must be some chars in the path
1644 $fdir = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']; // fileadmin dir, absolute
1645 if ($webspace) {
1646 $path = $fdir . $path; // PATH_site + fileadmin dir is prepended
1647 } else {
1648 if ($this->OS != 'WIN') { // with WINDOWS no prepending!!
1649 $path = '/' . $path; // root-level is the start...
1650 }
1651 }
1652 $path .= '/';
1653
1654 // We now have a path with slash after and slash before (if unix)
1655 if (@is_dir($path) &&
1656 (($GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && t3lib_div::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'])) || t3lib_div::isFirstPartOfStr($path, $fdir))) {
1657 // Alternative title?
1658 $name = $title ? $title : $altTitle;
1659 // Adds the filemount. The same filemount with same name, type and path cannot be set up twice because of the hash string used as key.
1660 $this->groupData['filemounts'][md5($name . '|' . $path . '|' . $type)] = array('name' => $name, 'path' => $path, 'type' => $type);
1661 // Return TRUE - went well, success!
1662 return 1;
1663 }
1664 }
1665 }
1666 }
1667
1668
1669 /**
1670 * Returns an array with the filemounts for the user. Each filemount is represented with an array of a "name", "path" and "type".
1671 * If no filemounts an empty array is returned.
1672 *
1673 * @return array
1674 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1 as getFileStorages() should be the one to be used
1675 */
1676 function returnFilemounts() {
1677 t3lib_div::logDeprecatedFunction();
1678
1679 // initialize the file storages in order to set some default settings in any time
1680 $this->getFileStorages();
1681
1682 return $this->groupData['filemounts'];
1683 }
1684
1685 /**
1686 * Creates a TypoScript comment with the string text inside.
1687 *
1688 * @param string The text to wrap in comment prefixes and delimiters.
1689 * @return string TypoScript comment with the string text inside.
1690 */
1691 function addTScomment($str) {
1692 $delimiter = '# ***********************************************';
1693
1694 $out = $delimiter . LF;
1695 $lines = t3lib_div::trimExplode(LF, $str);
1696 foreach ($lines as $v) {
1697 $out .= '# ' . $v . LF;
1698 }
1699 $out .= $delimiter . LF;
1700 return $out;
1701 }
1702
1703
1704 /************************************
1705 *
1706 * Workspaces
1707 *
1708 ************************************/
1709
1710 /**
1711 * Initializing workspace.
1712 * Called from within this function, see fetchGroupData()
1713 *
1714 * @return void
1715 * @see fetchGroupData()
1716 */
1717 function workspaceInit() {
1718
1719 // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
1720 $this->setWorkspace($this->user['workspace_id']);
1721
1722 // Limiting the DB mountpoints if there any selected in the workspace record
1723 $this->initializeDbMountpointsInWorkspace();
1724
1725 if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
1726 $this->groupData['allowed_languages'] = $allowed_languages;
1727 $this->groupData['allowed_languages'] = t3lib_div::uniqueList($this->groupData['allowed_languages']);
1728 }
1729 }
1730
1731 /**
1732 * Limiting the DB mountpoints if there any selected in the workspace record
1733 *
1734 * @return void
1735 */
1736 protected function initializeDbMountpointsInWorkspace() {
1737 $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
1738 if ($this->workspace > 0 && $dbMountpoints != '') {
1739 $filteredDbMountpoints = array();
1740 $readPerms = '1=1'; // Notice: We cannot call $this->getPagePermsClause(1); as usual because the group-list is not available at this point. But bypassing is fine because all we want here is check if the workspace mounts are inside the current webmounts rootline. The actual permission checking on page level is done elsewhere as usual anyway before the page tree is rendered.
1741
1742 // Traverse mount points of the
1743 $dbMountpoints = t3lib_div::intExplode(',', $dbMountpoints);
1744 foreach ($dbMountpoints as $mpId) {
1745 if ($this->isInWebMount($mpId, $readPerms)) {
1746 $filteredDbMountpoints[] = $mpId;
1747 }
1748 }
1749
1750 // Re-insert webmounts:
1751 $filteredDbMountpoints = array_unique($filteredDbMountpoints);
1752 $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
1753 }
1754 }
1755
1756 /**
1757 * Adds more limitations for users who are no admins
1758 * this was previously in workspaceInit but has now been moved to "
1759 *
1760 * @return void
1761 */
1762 protected function initializeFileStoragesForWorkspace() {
1763 // Filtering the file mountpoints
1764 // if there some selected in the workspace record
1765 if ($this->workspace > 0) {
1766 $storageFiltersInWorkspace = trim($this->workspaceRec['file_mountpoints']);
1767
1768 // no custom filemounts that should serve as filter or user is admin
1769 // so all user mountpoints are re-applied
1770 if (!$this->isAdmin() && $storageFiltersInWorkspace !== '') {
1771 // empty the fileStorages (will be re-applied later)
1772 $existingFileStoragesOfUser = $this->fileStorages;
1773 $this->fileStorages = array();
1774
1775 $storageRepository = t3lib_div::makeInstance('t3lib_file_Repository_StorageRepository');
1776
1777 // Fetching all filemounts from the workspace
1778 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
1779 '*',
1780 'sys_filemounts',
1781 'deleted = 0 AND hidden = 0 AND pid = 0 AND uid IN (' . $GLOBALS['TYPO3_DB']->cleanIntList($storageFiltersInWorkspace) . ')'
1782 );
1783
1784 // add every filemount of this workspace record
1785 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1786 // get the added entry, and check if it was in the users' original filemounts
1787 // if not, remove it from the new filemount list again
1788 // see self::addFileMount
1789
1790 // TODO: check if the filter is narrowing down the existing user
1791 $storageObject = $storageRepository->findByUid($row['base']);
1792 if (isset($existingFileStoragesOfUser[$storageObject->getUid()])) {
1793 $storageObject->injectFileMount($row['path']);
1794 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1795 }
1796 }
1797 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1798 }
1799 }
1800 }
1801
1802 /**
1803 * Checking if a workspace is allowed for backend user
1804 *
1805 * @param mixed If integer, workspace record is looked up, if array it is seen as a Workspace record with at least uid, title, members and adminusers columns. Can be faked for workspaces uid 0 and -1 (online and offline)
1806 * @param string List of fields to select. Default fields are: uid,title,adminusers,members,reviewers,publish_access,stagechg_notification
1807 * @return array TRUE if access. Output will also show how access was granted. Admin users will have a true output regardless of input.
1808 */
1809 function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification') {
1810 $retVal = FALSE;
1811
1812 // If not array, look up workspace record:
1813 if (!is_array($wsRec)) {
1814 switch ((string) $wsRec) {
1815 case '0':
1816 $wsRec = array('uid' => $wsRec);
1817 break;
1818 default:
1819 if (t3lib_extMgm::isLoaded('workspaces')) {
1820 $wsRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
1821 $fields,
1822 'sys_workspace',
1823 'pid=0 AND uid=' . intval($wsRec) .
1824 t3lib_BEfunc::deleteClause('sys_workspace'),
1825 '',
1826 'title'
1827 );
1828 }
1829 break;
1830 }
1831 }
1832
1833 // If wsRec is set to an array, evaluate it:
1834 if (is_array($wsRec)) {
1835 if ($this->isAdmin()) {
1836 return array_merge($wsRec, array('_ACCESS' => 'admin'));
1837 } else {
1838
1839 switch ((string) $wsRec['uid']) {
1840 case '0':
1841 $retVal = ($this->groupData['workspace_perms'] & 1) ? array_merge($wsRec, array('_ACCESS' => 'online')) : FALSE;
1842 break;
1843 default:
1844 // Checking if the guy is admin:
1845 if (t3lib_div::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
1846 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1847 }
1848 // Checking if he is owner through a user group of his:
1849 foreach ($this->userGroupsUID as $groupUid) {
1850 if (t3lib_div::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
1851 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1852 }
1853 }
1854 // Checking if he is reviewer user:
1855 if (t3lib_div::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
1856 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1857 }
1858 // Checking if he is reviewer through a user group of his:
1859 foreach ($this->userGroupsUID as $groupUid) {
1860 if (t3lib_div::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
1861 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1862 }
1863 }
1864 // Checking if he is member as user:
1865 if (t3lib_div::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
1866 return array_merge($wsRec, array('_ACCESS' => 'member'));
1867 }
1868 // Checking if he is member through a user group of his:
1869 foreach ($this->userGroupsUID as $groupUid) {
1870 if (t3lib_div::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
1871 return array_merge($wsRec, array('_ACCESS' => 'member'));
1872 }
1873 }
1874 break;
1875 }
1876 }
1877 }
1878
1879 return $retVal;
1880 }
1881
1882 /**
1883 * Uses checkWorkspace() to check if current workspace is available for user. This function caches the result and so can be called many times with no performance loss.
1884 *
1885 * @return array See checkWorkspace()
1886 * @see checkWorkspace()
1887 */
1888 function checkWorkspaceCurrent() {
1889 if (!isset($this->checkWorkspaceCurrent_cache)) {
1890 $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
1891 }
1892 return $this->checkWorkspaceCurrent_cache;
1893 }
1894
1895 /**
1896 * Setting workspace ID
1897 *
1898 * @param integer ID of workspace to set for backend user. If not valid the default workspace for BE user is found and set.
1899 * @return void
1900 */
1901 function setWorkspace($workspaceId) {
1902
1903 // Check workspace validity and if not found, revert to default workspace.
1904 if ($this->workspaceRec = $this->checkWorkspace($workspaceId, '*')) {
1905 // Set workspace ID internally
1906 $this->workspace = (int) $workspaceId;
1907 } else {
1908 $this->workspace = (int) $this->getDefaultWorkspace();
1909 $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
1910 }
1911
1912 // Unset access cache:
1913 unset($this->checkWorkspaceCurrent_cache);
1914
1915 // If ID is different from the stored one, change it:
1916 if (strcmp($this->workspace, $this->user['workspace_id'])) {
1917 $this->user['workspace_id'] = $this->workspace;
1918 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_id' => $this->user['workspace_id']));
1919 $this->simplelog('User changed workspace to "' . $this->workspace . '"');
1920 }
1921 }
1922
1923 /**
1924 * Setting workspace preview state for user:
1925 *
1926 * @param boolean State of user preview.
1927 * @return void
1928 */
1929 function setWorkspacePreview($previewState) {
1930 $this->user['workspace_preview'] = $previewState;
1931 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_preview' => $this->user['workspace_preview']));
1932 }
1933
1934 /**
1935 * Return default workspace ID for user,
1936 * If EXT:workspaces is not installed the user will be pushed the the
1937 * Live workspace
1938 *
1939 * @return integer Default workspace id. If no workspace is available it will be "-99"
1940 */
1941 function getDefaultWorkspace() {
1942
1943 $defaultWorkspace = -99;
1944
1945 if (!t3lib_extMgm::isLoaded('workspaces') || $this->checkWorkspace(0)) { // Check online
1946 $defaultWorkspace = 0;
1947 } elseif ($this->checkWorkspace(-1)) { // Check offline
1948 $defaultWorkspace = -1;
1949 } elseif (t3lib_extMgm::isLoaded('workspaces')) { // Traverse custom workspaces:
1950 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers', 'sys_workspace', 'pid=0' . t3lib_BEfunc::deleteClause('sys_workspace'), '', 'title');
1951 foreach ($workspaces as $rec) {
1952 if ($this->checkWorkspace($rec)) {
1953 $defaultWorkspace = $rec['uid'];
1954 break;
1955 }
1956 }
1957 }
1958 return $defaultWorkspace;
1959 }
1960
1961
1962 /************************************
1963 *
1964 * Logging
1965 *
1966 ************************************/
1967
1968 /**
1969 * Writes an entry in the logfile/table
1970 * Documentation in "TYPO3 Core API"
1971 *
1972 * @param integer Denotes which module that has submitted the entry. See "TYPO3 Core API". Use "4" for extensions.
1973 * @param integer Denotes which specific operation that wrote the entry. Use "0" when no sub-categorizing applies
1974 * @param integer Flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
1975 * @param integer The message number. Specific for each $type and $action. This will make it possible to translate errormessages to other languages
1976 * @param string Default text that follows the message (in english!). Possibly translated by identification through type/action/details_nr
1977 * @param array Data that follows the log. Might be used to carry special information. If an array the first 5 entries (0-4) will be sprintf'ed with the details-text
1978 * @param string Table name. Special field used by tce_main.php.
1979 * @param integer Record UID. Special field used by tce_main.php.
1980 * @param integer Record PID. Special field used by tce_main.php. OBSOLETE
1981 * @param integer The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
1982 * @param string Special field used by tce_main.php. NEWid string of newly created records.
1983 * @param integer Alternative Backend User ID (used for logging login actions where this is not yet known).
1984 * @return integer Log entry ID.
1985 */
1986 function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0) {
1987
1988 $fields_values = array(
1989 'userid' => $userId ? $userId : intval($this->user['uid']),
1990 'type' => intval($type),
1991 'action' => intval($action),
1992 'error' => intval($error),
1993 'details_nr' => intval($details_nr),
1994 'details' => $details,
1995 'log_data' => serialize($data),
1996 'tablename' => $tablename,
1997 'recuid' => intval($recuid),
1998 # 'recpid' => intval($recpid),
1999 'IP' => t3lib_div::getIndpEnv('REMOTE_ADDR'),
2000 'tstamp' => $GLOBALS['EXEC_TIME'],
2001 'event_pid' => intval($event_pid),
2002 'NEWid' => $NEWid,
2003 'workspace' => $this->workspace
2004 );
2005
2006 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_log', $fields_values);
2007 return $GLOBALS['TYPO3_DB']->sql_insert_id();
2008 }
2009
2010 /**
2011 * Simple logging function
2012 *
2013 * @param string Log message
2014 * @param string Option extension key / module name
2015 * @param integer Error level. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2016 * @return integer Log entry UID
2017 */
2018 function simplelog($message, $extKey = '', $error = 0) {
2019 return $this->writelog(
2020 4,
2021 0,
2022 $error,
2023 0,
2024 ($extKey ? '[' . $extKey . '] ' : '') . $message,
2025 array()
2026 );
2027 }
2028
2029 /**
2030 * Sends a warning to $email if there has been a certain amount of failed logins during a period.
2031 * If a login fails, this function is called. It will look up the sys_log to see if there has been more than $max failed logins the last $secondsBack seconds (default 3600). If so, an email with a warning is sent to $email.
2032 *
2033 * @param string Email address
2034 * @param integer Number of sections back in time to check. This is a kind of limit for how many failures an hour for instance.
2035 * @param integer Max allowed failures before a warning mail is sent
2036 * @return void
2037 * @access private
2038 */
2039 function checkLogFailures($email, $secondsBack = 3600, $max = 3) {
2040
2041 if ($email) {
2042
2043 // get last flag set in the log for sending
2044 $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2045 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2046 'tstamp',
2047 'sys_log',
2048 'type=255 AND action=4 AND tstamp>' . intval($theTimeBack),
2049 '',
2050 'tstamp DESC',
2051 '1'
2052 );
2053 if ($testRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2054 $theTimeBack = $testRow['tstamp'];
2055 }
2056 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2057
2058 // Check for more than $max number of error failures with the last period.
2059 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2060 '*',
2061 'sys_log',
2062 'type=255 AND action=3 AND error<>0 AND tstamp>' . intval($theTimeBack),
2063 '',
2064 'tstamp'
2065 );
2066 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) > $max) {
2067 // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2068 $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2069 $email_body = 'There have been some attempts (' . $GLOBALS['TYPO3_DB']->sql_num_rows($res) . ') to login at the TYPO3
2070 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . t3lib_div::getIndpEnv('HTTP_HOST') . ').
2071
2072 This is a dump of the failures:
2073
2074 ';
2075 while ($testRows = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2076 $theData = unserialize($testRows['log_data']);
2077 $email_body .= date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $testRows['tstamp']) . ': ' . @sprintf($testRows['details'], '' . $theData[0], '' . $theData[1], '' . $theData[2]);
2078 $email_body .= LF;
2079 }
2080 $from = t3lib_utility_Mail::getSystemFrom();
2081 /** @var $mail t3lib_mail_Message */
2082 $mail = t3lib_div::makeInstance('t3lib_mail_Message');
2083 $mail->setTo($email)
2084 ->setFrom($from)
2085 ->setSubject($subject)
2086 ->setBody($email_body);
2087 $mail->send();
2088 $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', array($GLOBALS['TYPO3_DB']->sql_num_rows($res), $secondsBack, $email)); // Logout written to log
2089 }
2090 }
2091 }
2092 }
2093
2094 ?>