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