2008-08-07 23:12:44 +02:00
< ? php
/**
2012-08-07 10:55:41 +02:00
* EGroupware API : session handling
2008-08-07 23:12:44 +02:00
*
2008-08-08 08:02:45 +02:00
* This class is based on the old phpgwapi / inc / class . sessions ( _php4 ) . inc . php :
* ( c ) 1998 - 2000 NetUSE AG Boris Erdmann , Kristian Koehntopp
* ( c ) 2003 FreeSoftware Foundation
* Not sure how much the current code still has to do with it .
*
* Former authers were :
* - NetUSE AG Boris Erdmann , Kristian Koehntopp
* - Dan Kuykendall < seek3r @ phpgroupware . org >
* - Joseph Engo < jengo @ phpgroupware . org >
*
2008-08-07 23:12:44 +02:00
* @ link http :// www . egroupware . org
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package api
* @ subpackage session
2008-08-08 08:02:45 +02:00
* @ author Ralf Becker < ralfbecker @ outdoor - training . de > since 2003 on
2008-08-07 23:12:44 +02:00
* @ version $Id $
*/
/**
2012-08-07 10:55:41 +02:00
* session handling
2008-08-07 23:12:44 +02:00
*
2012-08-07 10:55:41 +02:00
* Create , verifies or destroys an EGroupware session
2008-08-07 23:12:44 +02:00
*
* There are separate session - handler classes : egw_session_ ( files | memcache ),
* which implement custom session handler or certain extra functionality , like eg . listing sessions ,
* not available in php ' s session extension .
2008-11-13 17:57:16 +01:00
*
* If you want to analyse the memory usage in the session , you can uncomment the following call :
*
* static function encrypt ( $kp3 )
* {
* // switch that on to analyse memory usage in the session
* //self::log_session_usage($_SESSION[self::EGW_APPSESSION_VAR],'_SESSION['.self::EGW_APPSESSION_VAR.']',true,5000);
2008-08-07 23:12:44 +02:00
*/
2008-10-08 20:38:30 +02:00
class egw_session
2008-08-07 23:12:44 +02:00
{
2009-04-04 10:38:56 +02:00
/**
* Write debug messages about session verification and creation to the error_log
2015-08-19 12:41:06 +02:00
*
* This will contain passwords ! Don ' t leave it permanently switched on !
2009-04-04 10:38:56 +02:00
*/
2009-12-03 08:56:34 +01:00
const ERROR_LOG_DEBUG = false ;
2009-04-04 10:38:56 +02:00
2008-08-07 23:12:44 +02:00
/**
* key of eGW ' s session - data in $_SESSION
*/
const EGW_SESSION_VAR = 'egw_session' ;
/**
* key of eGW ' s application session - data in $_SESSION
*/
const EGW_APPSESSION_VAR = 'egw_app_session' ;
2008-10-08 20:38:30 +02:00
/**
* key of eGW ' s required files in $_SESSION
*
* These files get set by egw_db and egw class , for classes which get not autoloaded ( eg . ADOdb , idots_framework )
*/
const EGW_REQUIRED_FILES = 'egw_required_files' ;
/**
* key of eGW ' s egw_info cached in $_SESSION
*/
const EGW_INFO_CACHE = 'egw_info_cache' ;
/**
* key of eGW ' s egw object cached in $_SESSION
*/
const EGW_OBJECT_CACHE = 'egw_object_cache' ;
2008-08-07 23:12:44 +02:00
/**
* Name of cookie or get - parameter with session - id
*/
const EGW_SESSION_NAME = 'sessionid' ;
2013-09-11 15:06:00 +02:00
/**
* Used mcrypt algorithm and mode
*/
2013-10-07 09:22:50 +02:00
const MCRYPT_ALGO = MCRYPT_TRIPLEDES ;
const MCRYPT_MODE = MCRYPT_MODE_ECB ;
2013-09-11 15:06:00 +02:00
2008-08-07 23:12:44 +02:00
/**
* current user login ( account_lid @ domain )
*
* @ var string
*/
var $login ;
/**
* current user password
*
* @ var string
*/
var $passwd ;
/**
* current user db / ldap account id
*
* @ var int
*/
var $account_id ;
/**
* current user account login id ( without the eGW - domain /- instance part
*
* @ var string
*/
var $account_lid ;
/**
* domain for current user
*
* @ var string
*/
var $account_domain ;
/**
* type flag , A - anonymous session , N - None , normal session
*
* @ var string
*/
var $session_flags ;
/**
* current user session id
*
* @ var string
*/
var $sessionid ;
/**
* an other session specific id ( md5 from a random string ),
* used together with the sessionid for xmlrpc basic auth and the encryption of session - data ( if that ' s enabled )
*
* @ var string
*/
var $kp3 ;
2011-04-13 16:11:09 +02:00
/**
* Primary key of egw_access_log row for updates
*
* @ var int
*/
var $sessionid_access_log ;
2008-08-07 23:12:44 +02:00
/**
* name of XML - RPC / SOAP method called
*
* @ var string
*/
var $xmlrpc_method_called ;
/**
* Array with the name of the system domains
*
* @ var array
*/
private $egw_domains ;
/**
* $_SESSION at the time the constructor was called
*
* @ var array
*/
2008-10-08 20:38:30 +02:00
var $required_files ;
2008-08-07 23:12:44 +02:00
2015-07-01 14:44:34 +02:00
/**
* Nummeric code why session creation failed
*
* @ var int
*/
var $cd_reason ;
const CD_BAD_LOGIN_OR_PASSWORD = 5 ;
const CD_FORCE_PASSWORD_CHANGE = 97 ;
const CD_ACCOUNT_EXPIRED = 98 ;
const CD_BLOCKED = 99 ; // to many failed attempts to loing
/**
* Verbose reason why session creation failed
*
* @ var string
*/
var $reason ;
2008-08-07 23:12:44 +02:00
/**
* Constructor just loads up some defaults from cookies
*
2014-11-18 13:55:32 +01:00
* @ param array $domain_names = null domain - names used in this install
2008-08-07 23:12:44 +02:00
*/
function __construct ( array $domain_names = null )
{
2008-10-08 20:38:30 +02:00
$this -> required_files = $_SESSION [ self :: EGW_REQUIRED_FILES ];
2008-08-07 23:12:44 +02:00
2009-04-04 10:38:56 +02:00
$this -> sessionid = self :: get_sessionid ();
2009-08-22 21:32:28 +02:00
$this -> kp3 = self :: get_request ( 'kp3' );
2008-08-07 23:12:44 +02:00
$this -> egw_domains = $domain_names ;
2010-04-06 17:30:36 +02:00
if ( ! isset ( $GLOBALS [ 'egw_setup' ]))
2008-08-07 23:12:44 +02:00
{
2010-04-06 17:30:36 +02:00
// verfiy and if necessary create and save our config settings
//
$save_rep = false ;
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_access_log_age' ]))
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'max_access_log_age' ] = 90 ; // default 90 days
$save_rep = true ;
}
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'block_time' ]))
{
2012-11-29 12:13:58 +01:00
$GLOBALS [ 'egw_info' ][ 'server' ][ 'block_time' ] = 1 ; // default 1min, its enough to slow down brute force attacks
2010-04-06 17:30:36 +02:00
$save_rep = true ;
}
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_id' ]))
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_id' ] = 3 ; // default 3 trys per id
$save_rep = true ;
}
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_ip' ]))
{
2012-11-29 12:13:58 +01:00
$GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_ip' ] = $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_id' ] * 5 ; // default is 5 times as high as the id default; since accessing via proxy is quite common
2010-04-06 17:30:36 +02:00
$save_rep = true ;
}
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'install_id' ]))
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'install_id' ] = md5 ( common :: randomstring ( 15 ));
}
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_history' ]))
{
$GLOBALS [ 'egw_info' ][ 'server' ][ 'max_history' ] = 20 ;
$save_rep = true ;
}
2010-05-05 11:19:37 +02:00
2010-04-06 17:30:36 +02:00
if ( $save_rep )
{
$config = new config ( 'phpgwapi' );
$config -> read_repository ();
$config -> value ( 'max_access_log_age' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_access_log_age' ]);
$config -> value ( 'block_time' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'block_time' ]);
$config -> value ( 'num_unsuccessful_id' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_id' ]);
$config -> value ( 'num_unsuccessful_ip' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_ip' ]);
$config -> value ( 'install_id' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'install_id' ]);
$config -> value ( 'max_history' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_history' ]);
$config -> save_repository ();
}
2008-08-07 23:12:44 +02:00
}
self :: set_cookiedomain ();
2012-05-29 16:43:33 +02:00
// set session_timeout from global php.ini and default to 14400=4h, if not set
if ( ! ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ] = ini_get ( 'session.gc_maxlifetime' )))
{
ini_set ( 'session.gc_maxlifetime' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ] = 14400 );
}
2008-08-07 23:12:44 +02:00
}
2008-10-09 13:55:09 +02:00
/**
* Magic function called when this class get ' s restored from the session
*
*/
2008-08-07 23:12:44 +02:00
function __wakeup ()
{
2008-10-08 20:38:30 +02:00
ini_set ( 'session.gc_maxlifetime' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ]);
}
2008-10-09 13:55:09 +02:00
/**
* Destructor
*
*/
2008-10-08 20:38:30 +02:00
function __destruct ()
{
2012-11-29 12:13:58 +01:00
//if (empty($GLOBALS['egw_info']['user']['passwd']) )//|| empty($this->appsession('password','phpgwapi'))
//{
// error_log('__destruct'.'~252'.'->'." REQUEST_URI".$_SERVER['REQUEST_URI']);
//}
2008-10-08 20:38:30 +02:00
self :: encrypt ( $this -> kp3 );
}
2008-10-09 13:55:09 +02:00
/**
* commit the sessiondata to storage
*
* It ' s necessary to use this function instead of session_write_close () direct , as otherwise the session is not encrypted !
*/
function commit_session ()
{
2012-11-12 10:48:31 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " () sessionid= $this->sessionid , _SESSION[ " . self :: EGW_SESSION_VAR . ']=' . array2string ( $_SESSION [ self :: EGW_SESSION_VAR ]) . ' ' . function_backtrace ());
2008-10-09 13:55:09 +02:00
self :: encrypt ( $this -> kp3 );
session_write_close ();
}
2008-10-08 20:38:30 +02:00
/**
* Keys of session variables which get encrypted
*
* @ var array
*/
static $egw_session_vars = array (
//self::EGW_SESSION_VAR, no need to encrypt and required by the session list
self :: EGW_APPSESSION_VAR ,
self :: EGW_INFO_CACHE ,
self :: EGW_OBJECT_CACHE ,
);
static $mcrypt ;
2008-10-09 13:55:09 +02:00
/**
* Name of flag in session to signal it is encrypted or not
*/
const EGW_SESSION_ENCRYPTED = 'egw_session_encrypted' ;
2008-10-08 20:38:30 +02:00
/**
* Encrypt the variables in the session
*
* Is called by self :: __destruct () .
*/
static function encrypt ( $kp3 )
{
2008-11-13 17:57:16 +01:00
// switch that on to analyse memory usage in the session
//self::log_session_usage($_SESSION[self::EGW_APPSESSION_VAR],'_SESSION['.self::EGW_APPSESSION_VAR.']',true,5000);
2008-10-09 13:55:09 +02:00
if ( ! isset ( $_SESSION [ self :: EGW_SESSION_ENCRYPTED ]) && self :: init_crypt ( $kp3 ))
2008-10-08 20:38:30 +02:00
{
foreach ( self :: $egw_session_vars as $name )
{
if ( isset ( $_SESSION [ $name ]))
{
$_SESSION [ $name ] = mcrypt_generic ( self :: $mcrypt , serialize ( $_SESSION [ $name ]));
//error_log(__METHOD__."() 'encrypting' session var: $name, len=".strlen($_SESSION[$name]));
}
}
2008-10-09 13:55:09 +02:00
$_SESSION [ self :: EGW_SESSION_ENCRYPTED ] = true ; // flag session as encrypted
2008-10-08 20:38:30 +02:00
mcrypt_generic_deinit ( self :: $mcrypt );
self :: $mcrypt = null ;
}
}
2008-11-13 17:57:16 +01:00
/**
* Log the usage of session - vars
*
* @ param array & $arr
* @ param string $label
2014-11-18 13:55:32 +01:00
* @ param boolean $recursion = true if true call itself for every item > $limit
* @ param int $limit = 1000 log only differences > $limit
2008-11-13 17:57:16 +01:00
*/
static function log_session_usage ( & $arr , $label , $recursion = true , $limit = 1000 )
{
if ( ! is_array ( $arr )) return ;
$sizes = array ();
foreach ( $arr as $key => & $data )
{
$sizes [ $key ] = strlen ( serialize ( $data ));
}
arsort ( $sizes , SORT_NUMERIC );
foreach ( $sizes as $key => $size )
{
$diff = $size - ( int ) $_SESSION [ $label . '-sizes' ][ $key ];
$_SESSION [ $label . '-sizes' ][ $key ] = $size ;
if ( $diff > $limit )
{
error_log ( " strlen( { $label } [ $key ])= " . egw_vfs :: hsize ( $size ) . " , diff= " . egw_vfs :: hsize ( $diff ));
if ( $recursion ) self :: log_session_usage ( $arr [ $key ], $label . '[' . $key . ']' , $recursion , $limit );
}
}
}
2008-10-08 20:38:30 +02:00
/**
* Decrypt the variables in the session
*
* Is called by self :: init_handler from phpgwapi / inc / functions . inc . php ( called from the header . inc . php )
* before the restore of the eGW enviroment takes place , so that the whole thing can be encrypted
*/
static function decrypt ()
{
2009-08-22 21:32:28 +02:00
if ( $_SESSION [ self :: EGW_SESSION_ENCRYPTED ] && self :: init_crypt ( self :: get_request ( 'kp3' )))
2008-10-08 20:38:30 +02:00
{
foreach ( self :: $egw_session_vars as $name )
{
2008-10-09 13:55:09 +02:00
if ( isset ( $_SESSION [ $name ]))
2008-10-08 20:38:30 +02:00
{
2008-10-09 13:55:09 +02:00
$_SESSION [ $name ] = unserialize ( trim ( mdecrypt_generic ( self :: $mcrypt , $_SESSION [ $name ])));
//error_log(__METHOD__."() 'decrypting' session var $name: gettype($name) = ".gettype($_SESSION[$name]));
2008-10-08 20:38:30 +02:00
}
}
2008-10-09 13:55:09 +02:00
unset ( $_SESSION [ self :: EGW_SESSION_ENCRYPTED ]); // delete encryption flag
2008-10-08 20:38:30 +02:00
}
}
/**
* Check if session encryption is configured , possible and initialise it
*
* @ param string $kp3 mcrypt key transported via cookie or get parameter like the session id ,
* unlike the session id it ' s not know on the server , so only the client - request can decrypt the session !
2014-11-18 13:55:32 +01:00
* @ param string $algo = self :: MCRYPT_ALGO
* @ param string $mode = self :: MCRYPT_MODE
2008-10-08 20:38:30 +02:00
* @ return boolean true if encryption is used , false otherwise
*/
2013-09-11 15:06:00 +02:00
static private function init_crypt ( $kp3 , $algo = self :: MCRYPT_ALGO , $mode = self :: MCRYPT_MODE )
2008-10-08 20:38:30 +02:00
{
if ( ! $GLOBALS [ 'egw_info' ][ 'server' ][ 'mcrypt_enabled' ])
{
return false ; // session encryption is switched off
}
if ( $GLOBALS [ 'egw_info' ][ 'currentapp' ] == 'syncml' || ! $kp3 )
{
$kp3 = 'staticsyncmlkp3' ; // syncml has no kp3!
}
if ( is_null ( self :: $mcrypt ))
{
2009-04-20 14:43:44 +02:00
if ( ! check_load_extension ( 'mcrypt' ))
2008-10-08 20:38:30 +02:00
{
error_log ( __METHOD__ . " () required PHP extension mcrypt not loaded and can not be loaded, sessions get NOT encrypted! " );
return false ;
}
if ( ! ( self :: $mcrypt = mcrypt_module_open ( $algo , '' , $mode , '' )))
{
error_log ( __METHOD__ . " () could not mcrypt_module_open(algo=' $algo ','',mode=' $mode ',''), sessions get NOT encrypted! " );
return false ;
}
$iv_size = mcrypt_enc_get_iv_size ( self :: $mcrypt );
$iv = ! isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'mcrypt_iv' ]) || strlen ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'mcrypt_iv' ]) < $iv_size ?
mcrypt_create_iv ( $iv_size , MCRYPT_RAND ) : substr ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'mcrypt_iv' ], 0 , $iv_size );
if ( mcrypt_generic_init ( self :: $mcrypt , $kp3 , $iv ) < 0 )
{
error_log ( __METHOD__ . " () could not initialise mcrypt, sessions get NOT encrypted! " );
return self :: $mcrypt = false ;
}
}
return is_resource ( self :: $mcrypt );
2008-08-07 23:12:44 +02:00
}
/**
* Create a new eGW session
*
* @ param string $login user login
* @ param string $passwd user password
* @ param string $passwd_type type of password being used , ie plaintext , md5 , sha1
2014-11-18 13:55:32 +01:00
* @ param boolean $no_session = false dont create a real session , eg . for GroupDAV clients using only basic auth , no cookie support
* @ param boolean $auth_check = true if false , the user is loged in without checking his password ( eg . for single sign on ), default = true
2015-07-01 14:44:34 +02:00
* @ param boolean $fail_on_forced_password_change = false true : do NOT create session , if password change requested
2015-08-19 12:41:06 +02:00
* @ return string | boolean session id or false if session was not created , $this -> ( cd_ ) reason contains cause
2008-08-07 23:12:44 +02:00
*/
2015-07-01 14:44:34 +02:00
function create ( $login , $passwd = '' , $passwd_type = '' , $no_session = false , $auth_check = true , $fail_on_forced_password_change = false )
2008-08-07 23:12:44 +02:00
{
2015-08-19 12:41:06 +02:00
try {
if ( is_array ( $login ))
{
$this -> login = $login [ 'login' ];
$this -> passwd = $login [ 'passwd' ];
$this -> passwd_type = $login [ 'passwd_type' ];
$login = $this -> login ;
}
else
{
$this -> login = $login ;
$this -> passwd = $passwd ;
$this -> passwd_type = $passwd_type ;
}
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $this->login , $this->passwd , $this->passwd_type , $no_session , $auth_check ) starting ... " );
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
self :: split_login_domain ( $login , $this -> account_lid , $this -> account_domain );
// add domain to the login, if not already there
if ( substr ( $this -> login , - strlen ( $this -> account_domain ) - 1 ) != '@' . $this -> account_domain )
{
$this -> login .= '@' . $this -> account_domain ;
}
$now = time ();
//error_log(__METHOD__."($login,$passwd,$passwd_type,$no_session,$auth_check) account_lid=$this->account_lid, account_domain=$this->account_domain, default_domain={$GLOBALS['egw_info']['server']['default_domain']}, user/domain={$GLOBALS['egw_info']['user']['domain']}");
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
// This is to ensure that we authenticate to the correct domain (might not be default)
// if no domain is given we use the default domain, so we dont need to re-create everything
if ( ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ] && $this -> account_domain == $GLOBALS [ 'egw_info' ][ 'server' ][ 'default_domain' ])
{
$GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ] = $this -> account_domain ;
}
elseif ( ! $this -> account_domain && $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ])
{
$this -> account_domain = $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ];
}
elseif ( $this -> account_domain != $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ])
{
throw new Exception ( " Wrong domain! ' $this->account_domain ' != ' { $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ] } ' " );
}
unset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'default_domain' ]); // we kill this for security reasons
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
//echo "<p>session::create(login='$login'): lid='$this->account_lid', domain='$this->account_domain'</p>\n";
$user_ip = self :: getuser_ip ();
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
$this -> account_id = $GLOBALS [ 'egw' ] -> accounts -> name2id ( $this -> account_lid , 'account_lid' , 'u' );
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
if (( $blocked = $this -> login_blocked ( $login , $user_ip )) || // too many unsuccessful attempts
$GLOBALS [ 'egw_info' ][ 'server' ][ 'global_denied_users' ][ $this -> account_lid ] ||
$auth_check && ! $GLOBALS [ 'egw' ] -> auth -> authenticate ( $this -> account_lid , $this -> passwd , $this -> passwd_type ) ||
$this -> account_id && $GLOBALS [ 'egw' ] -> accounts -> get_type ( $this -> account_id ) == 'g' )
{
$this -> reason = $blocked ? 'blocked, too many attempts' : 'bad login or password' ;
$this -> cd_reason = $blocked ? self :: CD_BLOCKED : self :: CD_BAD_LOGIN_OR_PASSWORD ;
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
// we dont log anon users as it would block the website
if ( ! $GLOBALS [ 'egw' ] -> acl -> get_specific_rights_for_account ( $this -> account_id , 'anonymous' , 'phpgwapi' ))
{
$this -> log_access ( $this -> reason , $login , $user_ip , 0 ); // log unsuccessfull login
}
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $this->login , $this->passwd , $this->passwd_type , $no_session , $auth_check ) UNSUCCESSFULL ( $this->reason ) " );
return false ;
}
if ( $fail_on_forced_password_change && auth :: check_password_change ( $this -> reason ) === false )
2008-11-22 09:14:59 +01:00
{
2015-08-19 12:41:06 +02:00
$this -> cd_reason = self :: CD_FORCE_PASSWORD_CHANGE ;
return false ;
2008-11-22 09:14:59 +01:00
}
2015-08-19 12:41:06 +02:00
if ( ! $this -> account_id && $GLOBALS [ 'egw_info' ][ 'server' ][ 'auto_create_acct' ])
2008-08-07 23:12:44 +02:00
{
2015-08-19 12:41:06 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'auto_create_acct' ] == 'lowercase' )
{
$this -> account_lid = strtolower ( $this -> account_lid );
}
$this -> account_id = $GLOBALS [ 'egw' ] -> accounts -> auto_add ( $this -> account_lid , $passwd );
}
// fix maybe wrong case in username, it makes problems eg. in filemanager (name of homedir)
if ( $this -> account_lid != ( $lid = $GLOBALS [ 'egw' ] -> accounts -> id2name ( $this -> account_id )))
{
$this -> account_lid = $lid ;
$this -> login = $lid . substr ( $this -> login , strlen ( $lid ));
2008-08-07 23:12:44 +02:00
}
2015-08-19 12:41:06 +02:00
$GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] = $this -> account_id ;
$GLOBALS [ 'egw' ] -> accounts -> accounts ( $this -> account_id );
2009-04-04 10:38:56 +02:00
2015-08-19 12:41:06 +02:00
// for *DAV and eSync we use a pseudo sessionid created from md5(user:passwd)
// --> allows this stateless protocolls which use basic auth to use sessions!
if (( $this -> sessionid = self :: get_sessionid ( true )))
2009-04-04 10:38:56 +02:00
{
2015-08-19 12:41:06 +02:00
session_id ( $this -> sessionid );
2009-04-04 10:38:56 +02:00
}
2015-08-19 12:41:06 +02:00
else
{
self :: cache_control ();
session_start ();
// set a new session-id, if not syncml (already done in Horde code and can NOT be changed)
if ( ! $no_session && $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] != 'syncml' )
{
session_regenerate_id ( true );
}
$this -> sessionid = session_id ();
}
$this -> kp3 = common :: randomstring ( 24 );
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
$GLOBALS [ 'egw_info' ][ 'user' ] = $this -> read_repositories ();
if ( $GLOBALS [ 'egw' ] -> accounts -> is_expired ( $GLOBALS [ 'egw_info' ][ 'user' ]))
{
$this -> reason = 'account is expired' ;
$this -> cd_reason = self :: CD_ACCOUNT_EXPIRED ;
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $this->login , $this->passwd , $this->passwd_type , $no_session , $auth_check ) UNSUCCESSFULL ( $this->reason ) " );
return false ;
}
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
egw_cache :: setSession ( 'phpgwapi' , 'password' , base64_encode ( $this -> passwd ));
2008-10-09 11:54:24 +02:00
2015-08-19 12:41:06 +02:00
if ( $GLOBALS [ 'egw' ] -> acl -> check ( 'anonymous' , 1 , 'phpgwapi' ))
{
$this -> session_flags = 'A' ;
}
else
{
$this -> session_flags = 'N' ;
}
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
if (( $hook_result = $GLOBALS [ 'egw' ] -> hooks -> process ( array (
'location' => 'session_creation' ,
'sessionid' => $this -> sessionid ,
'session_flags' => $this -> session_flags ,
'account_id' => $this -> account_id ,
'account_lid' => $this -> account_lid ,
'passwd' => $this -> passwd ,
'account_domain' => $this -> account_domain ,
'user_ip' => $user_ip ,
), '' , true ))) // true = run hooks from all apps, not just the ones the current user has perms to run
2008-08-07 23:12:44 +02:00
{
2015-08-19 12:41:06 +02:00
foreach ( $hook_result as $reason )
2008-08-07 23:12:44 +02:00
{
2015-08-19 12:41:06 +02:00
if ( $reason ) // called hook requests to deny the session
{
$this -> reason = $this -> cd_reason = $reason ;
$this -> log_access ( $this -> reason , $login , $user_ip , 0 ); // log unsuccessfull login
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $this->login , $this->passwd , $this->passwd_type , $no_session , $auth_check ) UNSUCCESSFULL ( $this->reason ) " );
return false ;
}
2008-08-07 23:12:44 +02:00
}
}
2015-08-19 12:41:06 +02:00
$GLOBALS [ 'egw' ] -> db -> transaction_begin ();
$this -> register_session ( $this -> login , $user_ip , $now , $this -> session_flags );
if ( $this -> session_flags != 'A' ) // dont log anonymous sessions
{
$this -> sessionid_access_log = $this -> log_access ( $this -> sessionid , $login , $user_ip , $this -> account_id );
}
self :: appsession ( 'account_previous_login' , 'phpgwapi' , $GLOBALS [ 'egw' ] -> auth -> previous_login );
$GLOBALS [ 'egw' ] -> accounts -> update_lastlogin ( $this -> account_id , $user_ip );
$GLOBALS [ 'egw' ] -> db -> transaction_commit ();
2008-08-07 23:12:44 +02:00
2015-08-19 12:41:06 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'usecookies' ] && ! $no_session )
{
self :: egw_setcookie ( self :: EGW_SESSION_NAME , $this -> sessionid );
self :: egw_setcookie ( 'kp3' , $this -> kp3 );
self :: egw_setcookie ( 'domain' , $this -> account_domain );
}
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'usecookies' ] && ! $no_session || isset ( $_COOKIE [ 'last_loginid' ]))
{
self :: egw_setcookie ( 'last_loginid' , $this -> account_lid , $now + 1209600 ); /* For 2 weeks */
self :: egw_setcookie ( 'last_domain' , $this -> account_domain , $now + 1209600 );
}
//if (!$this->sessionid) echo "<p>session::create(login='$login') = '$this->sessionid': lid='$this->account_lid', domain='$this->account_domain'</p>\n";
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $this->login , $this->passwd , $this->passwd_type , $no_session , $auth_check ) successfull sessionid= $this->sessionid " );
return $this -> sessionid ;
2008-08-07 23:12:44 +02:00
}
2015-08-19 12:41:06 +02:00
// catch all exceptions, as their (allways logged) trace (eg. on a database error) would contain the user password
catch ( Exception $e ) {
$this -> reason = $this -> cd_reason = $e -> getMessage ();
error_log ( __METHOD__ . " (' $login ', " . array2string ( str_repeat ( '*' , strlen ( $passwd ))) .
" , ' $passwd_type ', no_session= " . array2string ( $no_session ) .
" , auth_check= " . array2string ( $auth_check ) .
" , fail_on_forced_password_change= " . array2string ( $fail_on_forced_password_change ) .
" ) Exception " . $e -> getMessage ());
return false ;
2008-08-07 23:12:44 +02:00
}
}
/**
* Store eGW specific session - vars
*
* @ param string $login
* @ param string $user_ip
* @ param int $now
* @ param string $session_flags
*/
private function register_session ( $login , $user_ip , $now , $session_flags )
{
// restore session vars set before session was started
2008-10-10 15:11:37 +02:00
if ( is_array ( $this -> required_files ))
2008-08-07 23:12:44 +02:00
{
2008-10-08 20:38:30 +02:00
$_SESSION [ self :: EGW_REQUIRED_FILES ] = ! is_array ( $_SESSION [ self :: EGW_REQUIRED_FILES ]) ? $this -> required_files :
array_unique ( array_merge ( $_SESSION [ self :: EGW_REQUIRED_FILES ], $this -> required_files ));
2008-10-10 15:11:37 +02:00
unset ( $this -> required_files );
2008-08-07 23:12:44 +02:00
}
$_SESSION [ self :: EGW_SESSION_VAR ] = array (
'session_id' => $this -> sessionid ,
'session_lid' => $login ,
'session_ip' => $user_ip ,
'session_logintime' => $now ,
'session_dla' => $now ,
'session_action' => $_SERVER [ 'PHP_SELF' ],
'session_flags' => $session_flags ,
// we need the install-id to differ between serveral installs shareing one tmp-dir
'session_install_id' => $GLOBALS [ 'egw_info' ][ 'server' ][ 'install_id' ]
);
}
/**
* name of access - log table
*/
const ACCESS_LOG_TABLE = 'egw_access_log' ;
/**
* Write or update ( for logout ) the access_log
*
2012-06-12 08:37:58 +02:00
* @ param string | int $sessionid nummeric or PHP session id or 0 for unsuccessful logins
2014-11-18 13:55:32 +01:00
* @ param string $login = '' account_lid ( evtl . with domain ) or '' for setting the logout - time
* @ param string $user_ip = '' ip to log
* @ param int $account_id = 0 numerical account_id
2011-04-13 16:11:09 +02:00
* @ return int $sessionid primary key of egw_access_log for login , null otherwise
2008-08-07 23:12:44 +02:00
*/
2012-06-12 08:37:58 +02:00
private function log_access ( $sessionid , $login = '' , $user_ip = '' , $account_id = 0 )
2008-08-07 23:12:44 +02:00
{
$now = time ();
if ( $login )
{
$GLOBALS [ 'egw' ] -> db -> insert ( self :: ACCESS_LOG_TABLE , array (
2011-04-13 16:11:09 +02:00
'session_php' => $sessionid ,
2008-08-07 23:12:44 +02:00
'loginid' => $login ,
'ip' => $user_ip ,
'li' => $now ,
'account_id' => $account_id ,
2012-06-01 15:13:06 +02:00
'user_agent' => $_SERVER [ 'HTTP_USER_AGENT' ],
2012-06-12 08:37:58 +02:00
'session_dla' => $now ,
'session_action' => $this -> update_dla ( false ), // dont update egw_access_log
2008-08-07 23:12:44 +02:00
), false , __LINE__ , __FILE__ );
2011-04-13 16:11:09 +02:00
$ret = $GLOBALS [ 'egw' ] -> db -> get_last_insert_id ( self :: ACCESS_LOG_TABLE , 'sessionid' );
2008-08-07 23:12:44 +02:00
}
else
{
2012-06-12 08:37:58 +02:00
if ( ! is_numeric ( $sessionid ) && $sessionid == $this -> sessionid && $this -> sessionid_access_log )
{
$sessionid = $this -> sessionid_access_log ;
}
2011-04-13 16:11:09 +02:00
$GLOBALS [ 'egw' ] -> db -> update ( self :: ACCESS_LOG_TABLE , array (
'lo' => $now
), is_numeric ( $sessionid ) ? array (
'sessionid' => $sessionid ,
) : array (
'session_php' => $sessionid ,
), __LINE__ , __FILE__ );
2008-08-07 23:12:44 +02:00
2012-06-12 08:37:58 +02:00
// run maintenance only on logout, to not delay login
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_access_log_age' ])
{
$max_age = $now - $GLOBALS [ 'egw_info' ][ 'server' ][ 'max_access_log_age' ] * 24 * 60 * 60 ;
$GLOBALS [ 'egw' ] -> db -> delete ( self :: ACCESS_LOG_TABLE , " li < $max_age " , __LINE__ , __FILE__ );
}
2008-08-07 23:12:44 +02:00
}
2011-04-13 16:11:09 +02:00
//error_log(__METHOD__."('$sessionid', '$login', '$user_ip', $account_id) returning ".array2string($ret));
return $ret ;
2008-08-07 23:12:44 +02:00
}
/**
* Protect against brute force attacks , block login if too many unsuccessful login attmepts
*
* @ param string $login account_lid ( evtl . with domain )
* @ param string $ip ip of the user
* @ returns bool login blocked ?
*/
private function login_blocked ( $login , $ip )
{
$block_time = time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'block_time' ] * 60 ;
2012-06-12 08:37:58 +02:00
$false_id = $false_ip = 0 ;
foreach ( $GLOBALS [ 'egw' ] -> db -> union ( array (
array (
'table' => self :: ACCESS_LOG_TABLE ,
'cols' => " 'false_ip' AS name,COUNT(*) AS num " ,
'where' => array (
'account_id' => 0 ,
'ip' => $ip ,
" li > $block_time " ,
),
),
array (
'table' => self :: ACCESS_LOG_TABLE ,
'cols' => " 'false_id' AS name,COUNT(*) AS num " ,
'where' => array (
'account_id' => 0 ,
'loginid' => $login ,
" li > $block_time " ,
),
),
array (
'table' => self :: ACCESS_LOG_TABLE ,
'cols' => " 'false_id' AS name,COUNT(*) AS num " ,
'where' => array (
'account_id' => 0 ,
'loginid LIKE ' . $GLOBALS [ 'egw' ] -> db -> quote ( $login . '@%' ),
" li > $block_time " ,
)
),
), __LINE__ , __FILE__ ) as $row )
{
$ { $row [ 'name' ]} += $row [ 'num' ];
}
$blocked = $false_ip > $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_ip' ] ||
$false_id > $GLOBALS [ 'egw_info' ][ 'server' ][ 'num_unsuccessful_id' ];
//error_log(__METHOD__."('$login', '$ip') false_ip=$false_ip, false_id=$false_id --> blocked=".array2string($blocked));
2008-08-07 23:12:44 +02:00
if ( $blocked && $GLOBALS [ 'egw_info' ][ 'server' ][ 'admin_mails' ] &&
$GLOBALS [ 'egw_info' ][ 'server' ][ 'login_blocked_mail_time' ] < time () - 5 * 60 ) // max. one mail every 5mins
{
2015-04-24 12:01:22 +02:00
try {
$mailer = new egw_mailer ();
// notify admin(s) via email
$mailer -> setFrom ( 'eGroupWare@' . $GLOBALS [ 'egw_info' ][ 'server' ][ 'mail_suffix' ]);
$mailer -> addHeader ( 'Subject' , lang ( " eGroupWare: login blocked for user '%1', IP %2 " , $login , $ip ));
$mailer -> setBody ( lang ( " Too many unsucessful attempts to login: %1 for the user '%2', %3 for the IP %4 " , $false_id , $login , $false_ip , $ip ));
foreach ( preg_split ( '/,\s*/' , $GLOBALS [ 'egw_info' ][ 'server' ][ 'admin_mails' ]) as $mail )
{
$mailer -> addAddress ( $mail );
2012-02-16 15:35:42 +01:00
}
2015-04-24 12:01:22 +02:00
$mailer -> send ();
}
catch ( Exception $e ) {
// ignore exception, but log it, to block the account and give a correct error-message to user
error_log ( __METHOD__ . " (' $login ', ' $ip ') " . $e -> getMessage ());
2008-08-07 23:12:44 +02:00
}
// save time of mail, to not send to many mails
$config = new config ( 'phpgwapi' );
$config -> read_repository ();
$config -> value ( 'login_blocked_mail_time' , time ());
$config -> save_repository ();
}
return $blocked ;
}
2014-11-18 13:55:32 +01:00
/**
* Basename of scripts for which we create a pseudo session - id based on user - credentials
*
* @ var array
*/
static $pseudo_session_scripts = array (
'webdav.php' , 'groupdav.php' , 'remote.php' , 'share.php'
);
2009-04-04 10:38:56 +02:00
/**
* Get the sessionid from Cookie , Get - Parameter or basic auth
*
2014-11-18 13:55:32 +01:00
* @ param boolean $only_basic_auth = false return only a basic auth pseudo sessionid , default no
2009-04-04 10:38:56 +02:00
* @ return string
*/
static function get_sessionid ( $only_basic_auth = false )
{
// for WebDAV and GroupDAV we use a pseudo sessionid created from md5(user:passwd)
// --> allows this stateless protocolls which use basic auth to use sessions!
if ( isset ( $_SERVER [ 'PHP_AUTH_USER' ]) && isset ( $_SERVER [ 'PHP_AUTH_PW' ]) &&
2014-11-18 13:55:32 +01:00
( in_array ( basename ( $_SERVER [ 'SCRIPT_NAME' ]), self :: $pseudo_session_scripts ) ||
2014-08-25 16:04:24 +02:00
$_SERVER [ 'SCRIPT_NAME' ] === '/Microsoft-Server-ActiveSync' ))
2009-04-04 10:38:56 +02:00
{
// we generate a pseudo-sessionid from the basic auth credentials
2012-04-07 08:53:38 +02:00
$sessionid = md5 ( $_SERVER [ 'PHP_AUTH_USER' ] . ':' . $_SERVER [ 'PHP_AUTH_PW' ] . ':' . $_SERVER [ 'HTTP_HOST' ] . ':' .
2014-08-25 16:04:24 +02:00
EGW_SERVER_ROOT . ':' . self :: getuser_ip () . ':' . filemtime ( EGW_SERVER_ROOT . '/phpgwapi/setup/setup.inc.php' ) .
// for ActiveSync we add the DeviceID
2015-08-04 16:49:01 +02:00
( isset ( $_GET [ 'DeviceId' ]) && $_SERVER [ 'SCRIPT_NAME' ] === '/Microsoft-Server-ActiveSync' ? ':' . $_GET [ 'DeviceId' ] : '' ) .
':' . $_SERVER [ 'HTTP_USER_AGENT' ]);
2014-08-25 16:04:24 +02:00
//error_log(__METHOD__."($only_basic_auth) HTTP_HOST=$_SERVER[HTTP_HOST], PHP_AUTH_USER=$_SERVER[PHP_AUTH_USER], DeviceId=$_GET[DeviceId]: sessionid=$sessionid");
2009-04-04 10:38:56 +02:00
}
2010-05-05 11:19:37 +02:00
// same for digest auth
elseif ( isset ( $_SERVER [ 'PHP_AUTH_DIGEST' ]) &&
2014-11-18 13:55:32 +01:00
in_array ( basename ( $_SERVER [ 'SCRIPT_NAME' ]), self :: $pseudo_session_scripts ))
2010-05-05 11:19:37 +02:00
{
// we generate a pseudo-sessionid from the digest username, realm and nounce
// can't use full $_SERVER['PHP_AUTH_DIGEST'], as it changes (contains eg. the url)
$data = egw_digest_auth :: parse_digest ( $_SERVER [ 'PHP_AUTH_DIGEST' ]);
2012-04-07 08:53:38 +02:00
$sessionid = md5 ( $data [ 'username' ] . ':' . $data [ 'realm' ] . ':' . $data [ 'nonce' ] . ':' . $_SERVER [ 'HTTP_HOST' ] .
2015-08-04 16:49:01 +02:00
EGW_SERVER_ROOT . ':' . self :: getuser_ip () . ':' . filemtime ( EGW_SERVER_ROOT . '/phpgwapi/setup/setup.inc.php' ) .
':' . $_SERVER [ 'HTTP_USER_AGENT' ]);
2010-05-05 11:19:37 +02:00
}
2009-04-04 10:38:56 +02:00
elseif ( ! $only_basic_auth && isset ( $_REQUEST [ self :: EGW_SESSION_NAME ]))
{
$sessionid = $_REQUEST [ self :: EGW_SESSION_NAME ];
}
2009-06-07 17:49:12 +02:00
elseif ( ! $only_basic_auth && isset ( $_COOKIE [ self :: EGW_SESSION_NAME ]))
{
$sessionid = $_COOKIE [ self :: EGW_SESSION_NAME ];
}
2009-04-04 10:38:56 +02:00
else
{
$sessionid = false ;
}
2012-11-12 10:48:31 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " () _SERVER[REQUEST_URI]=' $_SERVER[REQUEST_URI] ' returning " . print_r ( $sessionid , true ));
2009-04-04 10:38:56 +02:00
return $sessionid ;
}
2009-08-22 21:32:28 +02:00
/**
* Get request or cookie variable with higher precedence to $_REQUEST then $_COOKIE
*
* In php < 5.3 that ' s identical to $_REQUEST [ $name ], but php5 . 3 + does no longer register cookied in $_REQUEST by default
*
* As a workaround for a bug in Safari Version 3.2 . 1 ( 5525.27 . 1 ), where cookie first letter get ' s upcased , we check that too .
*
* @ param string $name eg . 'kp3' or domain
* @ return mixed null if it ' s neither set in $_REQUEST or $_COOKIE
*/
static function get_request ( $name )
{
return isset ( $_REQUEST [ $name ]) ? $_REQUEST [ $name ] :
( isset ( $_COOKIE [ $name ]) ? $_COOKIE [ $name ] :
( isset ( $_COOKIE [ $name = ucfirst ( $name )]) ? $_COOKIE [ $name ] : null ));
}
2008-08-07 23:12:44 +02:00
/**
* Check to see if a session is still current and valid
*
* @ param string $sessionid session id to be verfied
* @ param string $kp3 ? ? to be verified
* @ return bool is the session valid ?
*/
2009-04-04 10:38:56 +02:00
function verify ( $sessionid = null , $kp3 = null )
2008-08-07 23:12:44 +02:00
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " (' $sessionid ',' $kp3 ') " . function_backtrace ());
2008-08-07 23:12:44 +02:00
$fill_egw_info_and_repositories = ! $GLOBALS [ 'egw_info' ][ 'flags' ][ 'restored_from_session' ];
if ( ! $sessionid )
{
2009-04-04 10:38:56 +02:00
$sessionid = self :: get_sessionid ();
2009-08-22 21:32:28 +02:00
$kp3 = self :: get_request ( 'kp3' );
2008-08-07 23:12:44 +02:00
}
$this -> sessionid = $sessionid ;
$this -> kp3 = $kp3 ;
2009-04-04 10:38:56 +02:00
if ( ! $this -> sessionid )
{
2009-08-22 21:32:28 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " (' $sessionid ') get_sessionid()=' " . self :: get_sessionid () . " ' No session ID " );
2008-12-09 15:02:22 +01:00
return false ;
}
2008-08-07 23:12:44 +02:00
session_name ( self :: EGW_SESSION_NAME );
session_id ( $this -> sessionid );
2013-02-19 16:34:19 +01:00
self :: cache_control ();
2008-08-07 23:12:44 +02:00
session_start ();
2009-04-04 10:38:56 +02:00
// check if we have a eGroupware session --> return false if not (but dont destroy it!)
if ( is_null ( $_SESSION ) || ! isset ( $_SESSION [ self :: EGW_SESSION_VAR ]))
{
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " (' $sessionid ') session does NOT exist! " );
return false ;
}
2008-08-07 23:12:44 +02:00
$session =& $_SESSION [ self :: EGW_SESSION_VAR ];
if ( $session [ 'session_dla' ] <= time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ])
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " (' $sessionid ') session timed out! " );
2008-08-07 23:12:44 +02:00
$this -> destroy ( $sessionid , $kp3 );
return false ;
}
$this -> session_flags = $session [ 'session_flags' ];
$this -> split_login_domain ( $session [ 'session_lid' ], $this -> account_lid , $this -> account_domain );
// This is to ensure that we authenticate to the correct domain (might not be default)
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ] && $this -> account_domain != $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ])
{
2010-01-12 04:55:42 +01:00
return false ; // session not verified, domain changed
2008-08-07 23:12:44 +02:00
}
$GLOBALS [ 'egw_info' ][ 'user' ][ 'kp3' ] = $this -> kp3 ;
// allow xajax / notifications to not update the dla, so sessions can time out again
if ( ! isset ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'no_dla_update' ]) || ! $GLOBALS [ 'egw_info' ][ 'flags' ][ 'no_dla_update' ])
{
$this -> update_dla ();
}
2011-04-13 16:11:09 +02:00
elseif ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] == 'notifications' )
{
$this -> update_notification_heartbeat ();
}
2008-08-07 23:12:44 +02:00
$this -> account_id = $GLOBALS [ 'egw' ] -> accounts -> name2id ( $this -> account_lid , 'account_lid' , 'u' );
if ( ! $this -> account_id )
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " *** session::verify( $sessionid ) !accounts::name2id(' $this->account_lid ') " );
2008-08-07 23:12:44 +02:00
return false ;
}
$GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ] = $this -> account_id ;
if ( $fill_egw_info_and_repositories )
{
2012-08-07 10:55:41 +02:00
$GLOBALS [ 'egw_info' ][ 'user' ] = $this -> read_repositories ();
}
else
{
// update prefs, which might be changed by an other session
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ] = $GLOBALS [ 'egw' ] -> preferences -> read_repository ();
2008-08-07 23:12:44 +02:00
}
2012-08-07 10:55:41 +02:00
if ( $GLOBALS [ 'egw' ] -> accounts -> is_expired ( $GLOBALS [ 'egw_info' ][ 'user' ]))
2008-08-07 23:12:44 +02:00
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " *** session::verify( $sessionid ) accounts is expired " );
2008-08-07 23:12:44 +02:00
return false ;
}
2013-02-21 10:43:38 +01:00
$this -> passwd = base64_decode ( egw_cache :: getSession ( 'phpgwapi' , 'password' ));
2008-08-07 23:12:44 +02:00
if ( $fill_egw_info_and_repositories )
{
$GLOBALS [ 'egw_info' ][ 'user' ][ 'session_ip' ] = $session [ 'session_ip' ];
2013-02-21 10:43:38 +01:00
$GLOBALS [ 'egw_info' ][ 'user' ][ 'passwd' ] = $this -> passwd ;
2008-08-07 23:12:44 +02:00
}
if ( $this -> account_domain != $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ])
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " *** session::verify( $sessionid ) wrong domain " );
2008-08-07 23:12:44 +02:00
return false ;
}
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_checkip' ])
{
if ( strtoupper ( substr ( PHP_OS , 0 , 3 )) != 'WIN' && ( ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'session_ip' ] ||
$GLOBALS [ 'egw_info' ][ 'user' ][ 'session_ip' ] != $this -> getuser_ip ()))
{
2010-01-12 04:55:42 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " *** session::verify( $sessionid ) wrong IP " );
2008-08-07 23:12:44 +02:00
return false ;
}
}
if ( $fill_egw_info_and_repositories )
{
$GLOBALS [ 'egw' ] -> acl -> acl ( $this -> account_id );
$GLOBALS [ 'egw' ] -> preferences -> preferences ( $this -> account_id );
$GLOBALS [ 'egw' ] -> applications -> applications ( $this -> account_id );
}
if ( ! $this -> account_lid )
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " *** session::verify( $sessionid ) !account_lid " );
2008-08-07 23:12:44 +02:00
return false ;
}
2011-04-13 16:11:09 +02:00
// query accesslog-id, if not set in session (session is made persistent after login!)
2011-05-05 17:50:22 +02:00
if ( ! $this -> sessionid_access_log && $this -> session_flags != 'A' )
2011-04-13 16:11:09 +02:00
{
$this -> sessionid_access_log = $GLOBALS [ 'egw' ] -> db -> select ( self :: ACCESS_LOG_TABLE , 'sessionid' , array (
'session_php' => $this -> sessionid ,
), __LINE__ , __FILE__ ) -> fetchColumn ();
//error_log(__METHOD__."() sessionid=$this->sessionid --> sessionid_access_log=$this->sessionid_access_log");
}
2012-11-12 10:48:31 +01:00
// check if we use cookies for the session, but no cookie set
// happens eg. in sitemgr (when redirecting to a different domain) or with new java notification app
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'usecookies' ] && isset ( $_REQUEST [ self :: EGW_SESSION_NAME ]) &&
$_REQUEST [ self :: EGW_SESSION_NAME ] === $this -> sessionid &&
( ! isset ( $_COOKIE [ self :: EGW_SESSION_NAME ]) || $_COOKIE [ self :: EGW_SESSION_NAME ] !== $_REQUEST [ self :: EGW_SESSION_NAME ]))
{
if ( self :: ERROR_LOG_DEBUG ) error_log ( " --> session::verify( $sessionid ) SUCCESS, but NO required cookies set --> setting them now " );
self :: egw_setcookie ( self :: EGW_SESSION_NAME , $this -> sessionid );
self :: egw_setcookie ( 'kp3' , $this -> kp3 );
self :: egw_setcookie ( 'domain' , $this -> account_domain );
}
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( " --> session::verify( $sessionid ) SUCCESS " );
2008-08-07 23:12:44 +02:00
return true ;
}
/**
* Terminate a session
*
2012-06-12 08:37:58 +02:00
* @ param int | string $sessionid nummeric or php session id of session to be terminated
2008-08-07 23:12:44 +02:00
* @ param string $kp3
* @ return boolean true on success , false on error
*/
2011-04-13 16:11:09 +02:00
function destroy ( $sessionid , $kp3 = '' )
2008-08-07 23:12:44 +02:00
{
if ( ! $sessionid && $kp3 )
{
return false ;
}
2011-04-13 16:11:09 +02:00
$this -> log_access ( $sessionid ); // log logout-time
2008-08-07 23:12:44 +02:00
2014-11-18 13:55:32 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $sessionid , $kp3 ) " );
2008-08-07 23:12:44 +02:00
2011-04-13 16:11:09 +02:00
if ( is_numeric ( $sessionid )) // do we have a access-log-id --> get PHP session id
{
$sessionid = $GLOBALS [ 'egw' ] -> db -> select ( self :: ACCESS_LOG_TABLE , 'session_php' , array (
'sessionid' => $sessionid ,
), __LINE__ , __FILE__ ) -> fetchColumn ();
}
2008-08-07 23:12:44 +02:00
$GLOBALS [ 'egw' ] -> hooks -> process ( array (
'location' => 'session_destroyed' ,
'sessionid' => $sessionid ,
), '' , true ); // true = run hooks from all apps, not just the ones the current user has perms to run
// Only do the following, if where working with the current user
if ( ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'sessionid' ] || $sessionid == $GLOBALS [ 'egw_info' ][ 'user' ][ 'sessionid' ])
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ********* about to call session_destroy! " );
2008-08-07 23:12:44 +02:00
session_unset ();
//echo '<p>'.__METHOD__.": session_destroy() returned ".(session_destroy() ? 'true' : 'false')."</p>\n";
@ session_destroy ();
2008-08-29 15:00:18 +02:00
// we need to (re-)load the eGW session-handler, as session_destroy unloads custom session-handlers
if ( function_exists ( 'init_session_handler' ))
{
init_session_handler ();
}
2008-08-07 23:12:44 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'usecookies' ])
{
self :: egw_setcookie ( session_name ());
}
}
else
{
2011-04-13 16:11:09 +02:00
$this -> commit_session (); // close our own session
2008-08-07 23:12:44 +02:00
2011-04-13 16:11:09 +02:00
session_id ( $sessionid );
if ( session_start ())
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
session_destroy ();
2008-08-07 23:12:44 +02:00
}
}
return true ;
}
/**
* Generate a url which supports url or cookies based sessions
*
* Please note , the values of the query get url encoded !
*
* @ param string $url a url relative to the egroupware install root , it can contain a query too
2010-05-09 15:58:57 +02:00
* @ param array | string $extravars query string arguements as string or array ( prefered )
* if string is used ambersands in vars have to be already urlencoded as '%26' , function ensures they get NOT double encoded
2008-08-07 23:12:44 +02:00
* @ return string generated url
*/
public static function link ( $url , $extravars = '' )
{
//echo '<p>'.__METHOD__."(url='$url',extravars='".array2string($extravars)."')";
if ( $url [ 0 ] != '/' )
{
$app = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ];
if ( $app != 'login' && $app != 'logout' )
{
$url = $app . '/' . $url ;
}
}
// append the url to the webserver url, but avoid more then one slash between the parts of the url
2011-07-03 08:54:32 +02:00
$webserver_url = $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ];
2011-11-15 13:43:59 +01:00
// patch inspired by vladimir kolobkov -> we should not try to match the webserver url against the url without '/' as delimiter,
2011-07-25 17:52:22 +02:00
// as $webserver_url may be part of $url (as /egw is part of phpgwapi/js/egw_instant_load.html)
if (( $url [ 0 ] != '/' || $webserver_url != '/' ) && ( ! $webserver_url || strpos ( $url , $webserver_url . '/' ) === false ))
2008-08-07 23:12:44 +02:00
{
2011-07-03 08:54:32 +02:00
if ( $url [ 0 ] != '/' && substr ( $webserver_url , - 1 ) != '/' )
2008-08-07 23:12:44 +02:00
{
2011-07-03 08:54:32 +02:00
$url = $webserver_url . '/' . $url ;
2008-08-07 23:12:44 +02:00
}
else
{
2011-07-03 08:54:32 +02:00
$url = $webserver_url . $url ;
2008-08-07 23:12:44 +02:00
}
}
if ( isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'enforce_ssl' ]) && $GLOBALS [ 'egw_info' ][ 'server' ][ 'enforce_ssl' ])
{
if ( substr ( $url , 0 , 4 ) != 'http' )
{
2011-05-31 15:33:26 +02:00
$url = 'https://' . $_SERVER [ 'HTTP_HOST' ] . $url ;
2008-08-07 23:12:44 +02:00
}
else
{
$url = str_replace ( 'http:' , 'https:' , $url );
}
}
$vars = array ();
// add session params if not using cookies
if ( ! $GLOBALS [ 'egw_info' ][ 'server' ][ 'usecookies' ])
{
$vars [ self :: EGW_SESSION_NAME ] = $GLOBALS [ 'egw' ] -> session -> sessionid ;
$vars [ 'kp3' ] = $GLOBALS [ 'egw' ] -> session -> kp3 ;
$vars [ 'domain' ] = $GLOBALS [ 'egw' ] -> session -> account_domain ;
}
// check if the url already contains a query and ensure that vars is an array and all strings are in extravars
2014-11-18 13:55:32 +01:00
list ( $ret_url , $othervars ) = explode ( '?' , $url , 2 );
2008-08-07 23:12:44 +02:00
if ( $extravars && is_array ( $extravars ))
{
$vars += $extravars ;
$extravars = $othervars ;
}
else
{
2009-03-30 10:17:28 +02:00
if ( $othervars ) $extravars .= ( $extravars ? '&' : '' ) . $othervars ;
2008-08-07 23:12:44 +02:00
}
// parse extravars string into the vars array
if ( $extravars )
{
foreach ( explode ( '&' , $extravars ) as $expr )
{
list ( $var , $val ) = explode ( '=' , $expr , 2 );
2010-05-09 15:58:57 +02:00
if ( strpos ( $val , '%26' ) != false ) $val = str_replace ( '%26' , '&' , $val ); // make sure to not double encode &
2008-08-07 23:12:44 +02:00
if ( substr ( $var , - 2 ) == '[]' )
{
$vars [ substr ( $var , 0 , - 2 )][] = $val ;
}
else
{
$vars [ $var ] = $val ;
}
}
}
// if there are vars, we add them urlencoded to the url
if ( count ( $vars ))
{
$query = array ();
foreach ( $vars as $key => $value )
{
if ( is_array ( $value ))
{
foreach ( $value as $val )
{
$query [] = $key . '[]=' . urlencode ( $val );
}
}
else
{
$query [] = $key . '=' . urlencode ( $value );
}
}
2014-11-18 13:55:32 +01:00
$ret_url .= '?' . implode ( '&' , $query );
2008-08-07 23:12:44 +02:00
}
2014-11-18 13:55:32 +01:00
return $ret_url ;
2008-08-07 23:12:44 +02:00
}
/**
* Stores or retrieve applications data in / form the eGW session
*
* @ param string $location free lable to store the data
2014-11-18 13:55:32 +01:00
* @ param string $appname = '' default current application ( egw_info [ flags ][ currentapp ])
* @ param mixed $data = '##NOTHING##' if given , data to store , if not specified
2012-08-07 10:55:41 +02:00
* @ deprecated use egw_cache :: setSession ( $appname , $location , $data ) or egw_cache :: getSession ( $appname , $location )
2008-08-07 23:12:44 +02:00
* @ return mixed session data or false if no data stored for $appname / $location
*/
public static function & appsession ( $location = 'default' , $appname = '' , $data = '##NOTHING##' )
{
2008-10-09 14:24:41 +02:00
if ( isset ( $_SESSION [ self :: EGW_SESSION_ENCRYPTED ]))
{
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . ' called after session was encrypted --> ignored!' );
2008-10-09 14:24:41 +02:00
return false ; // can no longer store something in the session, eg. because commit_session() was called
}
2008-08-07 23:12:44 +02:00
if ( ! $appname )
{
$appname = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ];
}
// allow to store eg. '' as the value.
if ( $data === '##NOTHING##' )
{
if ( isset ( $_SESSION [ self :: EGW_APPSESSION_VAR ][ $appname ]) && array_key_exists ( $location , $_SESSION [ self :: EGW_APPSESSION_VAR ][ $appname ]))
{
2008-10-09 11:54:24 +02:00
$ret =& $_SESSION [ self :: EGW_APPSESSION_VAR ][ $appname ][ $location ];
2008-08-07 23:12:44 +02:00
}
2008-10-09 11:54:24 +02:00
else
{
$ret = false ;
}
}
else
{
$_SESSION [ self :: EGW_APPSESSION_VAR ][ $appname ][ $location ] =& $data ;
$ret =& $_SESSION [ self :: EGW_APPSESSION_VAR ][ $appname ][ $location ];
}
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG === 'appsession' )
2008-10-09 11:54:24 +02:00
{
error_log ( __METHOD__ . " ( $location , $appname , $data ) === " . ( is_scalar ( $ret ) && strlen ( $ret ) < 50 ?
( is_bool ( $ret ) ? ( $ret ? '(bool)true' : '(bool)false' ) : $ret ) :
( strlen ( $r = array2string ( $ret )) < 50 ? $r : substr ( $r , 0 , 50 ) . ' ...' )));
2008-08-07 23:12:44 +02:00
}
2008-10-09 11:54:24 +02:00
return $ret ;
2008-08-07 23:12:44 +02:00
}
/**
* Get the ip address of current users
*
* @ return string ip address
*/
public static function getuser_ip ()
{
return isset ( $_SERVER [ 'HTTP_X_FORWARDED_FOR' ]) ? $_SERVER [ 'HTTP_X_FORWARDED_FOR' ] : $_SERVER [ 'REMOTE_ADDR' ];
}
/**
* domain for cookies
*
* @ var string
*/
2008-08-08 08:02:45 +02:00
private static $cookie_domain = '' ;
2008-08-07 23:12:44 +02:00
/**
* path for cookies
*
* @ var string
*/
2008-08-08 08:02:45 +02:00
private static $cookie_path = '/' ;
2008-08-07 23:12:44 +02:00
2014-12-02 13:57:06 +01:00
/**
* iOS web - apps will loose cookie if set with a livetime of 0 / session - cookie
*
* Therefore we set a fixed lifetime of 24 h from session - start instead .
* Server - side session will timeout earliert anyway , if there ' s no activity .
*/
const IOS_SESSION_COOKIE_LIFETIME = 86400 ;
2008-08-07 23:12:44 +02:00
/**
* Set a cookie with eGW ' s cookie - domain and - path settings
*
* @ param string $cookiename name of cookie to be set
2014-11-18 13:55:32 +01:00
* @ param string $cookievalue = '' value to be used , if unset cookie is cleared ( optional )
* @ param int $cookietime = 0 when cookie should expire , 0 for session only ( optional )
* @ param string $cookiepath = null optional path ( eg . '/' ) if the eGW install - dir should not be used
2008-08-07 23:12:44 +02:00
*/
public static function egw_setcookie ( $cookiename , $cookievalue = '' , $cookietime = 0 , $cookiepath = null )
{
2010-09-25 10:19:22 +02:00
if ( empty ( self :: $cookie_domain ) || empty ( self :: $cookie_path ))
2008-08-07 23:12:44 +02:00
{
self :: set_cookiedomain ();
}
2009-12-02 15:56:41 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " ( $cookiename , $cookievalue , $cookietime , $cookiepath , " . self :: $cookie_domain . " ) " );
2008-08-07 23:12:44 +02:00
2014-12-02 13:57:06 +01:00
// if we are installed in iOS as web-app, we must not set a cookietime==0 (session-cookie),
// as every change between apps will cause the cookie to get lost
static $is_iOS = null ;
if ( ! $cookietime && ! isset ( $is_iOS )) $is_iOS = ( bool ) preg_match ( '/^(iPhone|iPad|iPod)/i' , html :: $ua_mobile );
2010-11-26 21:09:50 +01:00
if ( ! headers_sent ()) // gives only a warning, but can not send the cookie anyway
{
2014-12-02 13:57:06 +01:00
setcookie ( $cookiename , $cookievalue ,
! $cookietime && $is_iOS ? time () + self :: IOS_SESSION_COOKIE_LIFETIME : $cookietime ,
2013-09-11 15:06:00 +02:00
is_null ( $cookiepath ) ? self :: $cookie_path : $cookiepath , self :: $cookie_domain ,
// if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true)
empty ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'insecure_cookies' ]) && ! empty ( $_SERVER [ 'HTTPS' ]) && $_SERVER [ 'HTTPS' ] !== 'off' , true );
2010-11-26 21:09:50 +01:00
}
2008-08-07 23:12:44 +02:00
}
/**
* Set the domain and path used for cookies
*/
private static function set_cookiedomain ()
{
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'cookiedomain' ])
{
// Admin set domain, eg. .domain.com to allow egw.domain.com and www.domain.com
self :: $cookie_domain = $GLOBALS [ 'egw_info' ][ 'server' ][ 'cookiedomain' ];
}
else
{
// Use HTTP_X_FORWARDED_HOST if set, which is the case behind a none-transparent proxy
self :: $cookie_domain = isset ( $_SERVER [ 'HTTP_X_FORWARDED_HOST' ]) ? $_SERVER [ 'HTTP_X_FORWARDED_HOST' ] : $_SERVER [ 'HTTP_HOST' ];
}
// remove port from HTTP_HOST
2014-11-18 13:55:32 +01:00
$arr = null ;
2008-08-07 23:12:44 +02:00
if ( preg_match ( " /^(.*):(.*) $ / " , self :: $cookie_domain , $arr ))
{
self :: $cookie_domain = $arr [ 1 ];
}
if ( count ( explode ( '.' , self :: $cookie_domain )) <= 1 )
{
// setcookie dont likes domains without dots, leaving it empty, gets setcookie to fill the domain in
self :: $cookie_domain = '' ;
}
if ( ! $GLOBALS [ 'egw_info' ][ 'server' ][ 'cookiepath' ] ||
! ( self :: $cookie_path = parse_url ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ], PHP_URL_PATH )))
{
2014-11-18 13:55:32 +01:00
self :: $cookie_path = '/' ;
2008-08-07 23:12:44 +02:00
}
//echo "<p>cookie_path='self::$cookie_path', cookie_domain='self::$cookie_domain'</p>\n";
2014-11-18 13:55:32 +01:00
session_set_cookie_params ( 0 , self :: $cookie_path , self :: $cookie_domain ,
2013-09-11 15:06:00 +02:00
// if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true)
empty ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'insecure_cookies' ]) && ! empty ( $_SERVER [ 'HTTPS' ]) && $_SERVER [ 'HTTPS' ] !== 'off' , true );
2008-08-07 23:12:44 +02:00
}
/**
* Search the instance matching the request
*
2008-10-26 13:18:57 +01:00
* @ param string $login on login $_POST [ 'login' ], $_SERVER [ 'PHP_AUTH_USER' ] or $_SERVER [ 'REMOTE_USER' ]
2009-08-22 21:32:28 +02:00
* @ param string $domain_requested usually self :: get_request ( 'domain' )
2008-08-07 23:12:44 +02:00
* @ param string & $default_domain usually $default_domain get ' s set eg . by sitemgr
2014-11-18 13:55:32 +01:00
* @ param string | array $server_names usually array ( $_SERVER [ 'HTTP_HOST' ], $_SERVER [ 'SERVER_NAME' ])
* @ param array $domains = null defaults to $GLOBALS [ 'egw_domain' ] from the header
2008-08-07 23:12:44 +02:00
* @ return string $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ] set with the domain / instance to use
*/
2012-10-12 17:11:42 +02:00
public static function search_instance ( $login , $domain_requested , & $default_domain , $server_names , array $domains = null )
2008-08-07 23:12:44 +02:00
{
2012-10-12 17:11:42 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " (' $login ',' $domain_requested ', " . array2string ( $default_domain ) . " . " . array2string ( $server_names ) . " . " . array2string ( $domains ) . " ) " );
2010-05-05 11:19:37 +02:00
2008-08-07 23:12:44 +02:00
if ( is_null ( $domains )) $domains = $GLOBALS [ 'egw_domain' ];
if ( ! isset ( $default_domain ) || ! isset ( $domains [ $default_domain ])) // allow to overwrite the default domain
{
2012-10-12 17:11:42 +02:00
foreach (( array ) $server_names as $server_name )
2008-08-07 23:12:44 +02:00
{
2012-10-12 17:11:42 +02:00
list ( $server_name ) = explode ( ':' , $server_name ); // remove port from HTTP_HOST
if ( isset ( $domains [ $server_name ]))
2008-08-07 23:12:44 +02:00
{
2012-10-12 17:11:42 +02:00
$default_domain = $server_name ;
break ;
2008-08-07 23:12:44 +02:00
}
else
{
2014-11-18 13:55:32 +01:00
$parts = explode ( '.' , $server_name );
array_shift ( $parts );
$domain_part = implode ( '.' , $parts );
2012-10-12 17:11:42 +02:00
if ( isset ( $domains [ $domain_part ]))
{
$default_domain = $domain_part ;
break ;
}
else
{
reset ( $domains );
list ( $default_domain ) = each ( $domains );
}
unset ( $domain_part );
2008-08-07 23:12:44 +02:00
}
}
}
if ( isset ( $login )) // on login
{
if ( strpos ( $login , '@' ) === false || count ( $domains ) == 1 )
{
$login .= '@' . ( isset ( $_POST [ 'logindomain' ]) ? $_POST [ 'logindomain' ] : $default_domain );
}
$parts = explode ( '@' , $login );
$domain = array_pop ( $parts );
$GLOBALS [ 'login' ] = $login ;
}
else // on "normal" pageview
{
$domain = $domain_requested ;
}
if ( ! isset ( $domains [ $domain ]))
{
$domain = $default_domain ;
}
2010-01-12 04:55:42 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " () default_domain= " . array2string ( $default_domain ) . ', login=' . array2string ( $login ) . " returning " . array2string ( $domain ));
2010-05-05 11:19:37 +02:00
2008-08-07 23:12:44 +02:00
return $domain ;
}
/**
2011-04-13 16:11:09 +02:00
* Update session_action and session_dla ( session last used time )
2012-06-12 08:37:58 +02:00
*
2014-11-18 13:55:32 +01:00
* @ param boolean $update_access_log = true false : dont update egw_access_log table
2012-06-12 08:37:58 +02:00
* @ return string action as written to egw_access_log . session_action
2008-08-07 23:12:44 +02:00
*/
2012-06-12 08:37:58 +02:00
private function update_dla ( $update_access_log = true )
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
// This way XML-RPC users aren't always listed as xmlrpc.php
if ( $this -> xmlrpc_method_called )
{
$action = $this -> xmlrpc_method_called ;
}
elseif ( isset ( $_GET [ 'menuaction' ]))
2008-08-07 23:12:44 +02:00
{
$action = $_GET [ 'menuaction' ];
}
else
{
$action = $_SERVER [ 'PHP_SELF' ];
2011-04-13 16:11:09 +02:00
// remove EGroupware path, if not installed in webroot
$egw_path = $GLOBALS [ 'egw_info' ][ 'server' ][ 'webserver_url' ];
if ( $egw_path [ 0 ] != '/' ) $egw_path = parse_url ( $egw_path , PHP_URL_PATH );
2012-06-12 08:37:58 +02:00
if ( $action == '/Microsoft-Server-ActiveSync' )
{
$action .= '?Cmd=' . $_GET [ 'Cmd' ] . '&DeviceId=' . $_GET [ 'DeviceId' ];
}
elseif ( $egw_path )
2011-04-13 16:11:09 +02:00
{
list (, $action ) = explode ( $egw_path , $action , 2 );
}
2008-08-07 23:12:44 +02:00
}
2011-04-13 16:11:09 +02:00
// update dla in access-log table, if we have an access-log row (non-anonymous session)
2012-06-12 08:37:58 +02:00
if ( $this -> sessionid_access_log && $update_access_log )
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
$GLOBALS [ 'egw' ] -> db -> update ( self :: ACCESS_LOG_TABLE , array (
'session_dla' => time (),
'session_action' => $action ,
'lo' => null , // just in case it was (automatic) timed out before
), array (
'sessionid' => $this -> sessionid_access_log ,
), __LINE__ , __FILE__ );
2008-08-07 23:12:44 +02:00
}
$_SESSION [ self :: EGW_SESSION_VAR ][ 'session_dla' ] = time ();
$_SESSION [ self :: EGW_SESSION_VAR ][ 'session_action' ] = $action ;
2009-04-04 10:38:56 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . '() _SESSION[' . self :: EGW_SESSION_VAR . ']=' . array2string ( $_SESSION [ self :: EGW_SESSION_VAR ]));
2012-06-12 08:37:58 +02:00
return $action ;
2008-08-07 23:12:44 +02:00
}
2011-04-13 16:11:09 +02:00
/**
* Update notification_heartbeat time of session
*/
private function update_notification_heartbeat ()
{
// update dla in access-log table, if we have an access-log row (non-anonymous session)
if ( $this -> sessionid_access_log )
{
$GLOBALS [ 'egw' ] -> db -> update ( self :: ACCESS_LOG_TABLE , array (
'notification_heartbeat' => time (),
), array (
'sessionid' => $this -> sessionid_access_log ,
'lo IS NULL' ,
), __LINE__ , __FILE__ );
}
}
2008-08-07 23:12:44 +02:00
/**
* Read the diverse repositories / init classes with data from the just loged in user
*
2012-08-07 10:55:41 +02:00
* @ return array used to assign to $GLOBALS [ 'egw_info' ][ 'user' ]
2008-08-07 23:12:44 +02:00
*/
2008-08-15 16:37:34 +02:00
public function read_repositories ()
2008-08-07 23:12:44 +02:00
{
$GLOBALS [ 'egw' ] -> acl -> acl ( $this -> account_id );
$GLOBALS [ 'egw' ] -> preferences -> preferences ( $this -> account_id );
$GLOBALS [ 'egw' ] -> applications -> applications ( $this -> account_id );
2012-08-07 10:55:41 +02:00
$user = $GLOBALS [ 'egw' ] -> accounts -> read ( $this -> account_id );
2011-11-15 13:43:59 +01:00
// set homedirectory from auth_ldap or auth_ads, to be able to use it in vfs
2012-08-07 10:55:41 +02:00
if ( ! isset ( $user [ 'homedirectory' ]))
2011-11-15 13:43:59 +01:00
{
2011-11-15 20:16:09 +01:00
// authentication happens in login.php, which does NOT yet create egw-object in session
// --> need to store homedirectory in session
if ( isset ( $GLOBALS [ 'auto_create_acct' ][ 'homedirectory' ]))
{
egw_cache :: setSession ( __CLASS__ , 'homedirectory' ,
2012-08-07 10:55:41 +02:00
$user [ 'homedirectory' ] = $GLOBALS [ 'auto_create_acct' ][ 'homedirectory' ]);
2011-11-15 20:16:09 +01:00
}
else
{
2012-08-07 10:55:41 +02:00
$user [ 'homedirectory' ] = egw_cache :: getSession ( __CLASS__ , 'homedirectory' );
2011-11-15 20:16:09 +01:00
}
2011-11-15 13:43:59 +01:00
}
2012-08-07 10:55:41 +02:00
$user [ 'preferences' ] = $GLOBALS [ 'egw' ] -> preferences -> read_repository ();
2008-08-07 23:12:44 +02:00
if ( is_object ( $GLOBALS [ 'egw' ] -> datetime ))
{
$GLOBALS [ 'egw' ] -> datetime -> datetime (); // to set tz_offset from the now read prefs
}
2012-08-07 10:55:41 +02:00
$user [ 'apps' ] = $GLOBALS [ 'egw' ] -> applications -> read_repository ();
$user [ 'domain' ] = $this -> account_domain ;
$user [ 'sessionid' ] = $this -> sessionid ;
$user [ 'kp3' ] = $this -> kp3 ;
$user [ 'session_ip' ] = $this -> getuser_ip ();
$user [ 'session_lid' ] = $this -> account_lid . '@' . $this -> account_domain ;
$user [ 'account_id' ] = $this -> account_id ;
$user [ 'account_lid' ] = $this -> account_lid ;
$user [ 'userid' ] = $this -> account_lid ;
$user [ 'passwd' ] = $this -> passwd ;
return $user ;
2008-08-07 23:12:44 +02:00
}
/**
* Splits a login - name into account_lid and eGW - domain /- instance
*
* @ param string $login login - name ( ie . user @ default )
* @ param string & $account_lid returned account_lid ( ie . user )
* @ param string & $domain returned domain ( ie . domain )
*/
private function split_login_domain ( $login , & $account_lid , & $domain )
{
$parts = explode ( '@' , $login );
//conference - for strings like vinicius@thyamad.com@default ,
//allows that user have a login that is his e-mail. (viniciuscb)
if ( count ( $parts ) > 1 )
{
$probable_domain = array_pop ( $parts );
//Last part of login string, when separated by @, is a domain name
if ( in_array ( $probable_domain , $this -> egw_domains ))
{
$got_login = true ;
$domain = $probable_domain ;
$account_lid = implode ( '@' , $parts );
}
}
if ( ! $got_login )
{
$domain = $GLOBALS [ 'egw_info' ][ 'server' ][ 'default_domain' ];
$account_lid = $login ;
}
}
2008-11-09 17:15:42 +01:00
/**
* Create a hash from user and pw
*
* Can be used to check setup config user / password inside egroupware :
*
* if ( egw_session :: user_pw_hash ( $user , $pw ) === $GLOBALS [ 'egw_info' ][ 'server' ][ 'config_hash' ])
*
* @ param string $user username
* @ param string $password password or md5 hash of password if $allow_password_md5
2014-11-18 13:55:32 +01:00
* @ param boolean $allow_password_md5 = false can password alread be an md5 hash
2008-11-09 17:15:42 +01:00
* @ return string
*/
static function user_pw_hash ( $user , $password , $allow_password_md5 = false )
{
$password_md5 = $allow_password_md5 && preg_match ( '/^[a-f0-9]{32}$/' , $password ) ? $password : md5 ( $password );
$hash = sha1 ( strtolower ( $user ) . $password_md5 );
//echo "<p>".__METHOD__."('$user','$password',$allow_password_md5) sha1('".strtolower($user)."$password_md5')='$hash'</p>\n";
return $hash ;
}
2008-08-07 23:12:44 +02:00
/*
* Funtions to access the used session - handler , specified in header . inc . php : $GLOBALS [ 'egw_info' ][ 'server' ][ 'session_handler' ]
*/
/**
* Name of session - handler - class
*
* @ var string
*/
private static $session_handler = 'egw_session_files' ;
/**
* Initialise the used session handler
*/
public static function init_handler ()
{
if ( isset ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'session_handler' ]) && class_exists ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'session_handler' ]))
{
self :: $session_handler = $GLOBALS [ 'egw_info' ][ 'server' ][ 'session_handler' ];
}
2009-12-02 15:56:41 +01:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . '() session_handler=' . self :: $session_handler . ', egw_info[server][session_handler]=' . $GLOBALS [ 'egw_info' ][ 'server' ][ 'session_handler' ] . ' called from:' . function_backtrace ());
2008-08-07 23:12:44 +02:00
if ( method_exists ( self :: $session_handler , 'init_session_handler' ))
{
call_user_func ( array ( self :: $session_handler , 'init_session_handler' ));
}
ini_set ( 'session.use_cookies' , 0 ); // disable the automatic use of cookies, as it uses the path / by default
session_name ( self :: EGW_SESSION_NAME );
2009-04-04 10:38:56 +02:00
if (( $sessionid = self :: get_sessionid ()))
2008-10-08 20:38:30 +02:00
{
2009-04-04 10:38:56 +02:00
session_id ( $sessionid );
2013-02-21 10:43:38 +01:00
self :: cache_control ();
2009-08-22 21:32:28 +02:00
$ok = session_start ();
2009-04-04 10:38:56 +02:00
self :: decrypt ();
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " () sessionid= $sessionid , _SESSION[ " . self :: EGW_SESSION_VAR . ']=' . array2string ( $_SESSION [ self :: EGW_SESSION_VAR ]));
2009-08-22 21:32:28 +02:00
return $ok ;
2009-04-04 10:38:56 +02:00
}
2009-08-22 21:32:28 +02:00
if ( self :: ERROR_LOG_DEBUG ) error_log ( __METHOD__ . " () no active session! " );
return false ;
2008-08-07 23:12:44 +02:00
}
2013-02-19 16:34:19 +01:00
/**
2015-02-13 09:27:08 +01:00
* Controling caching and expires header
*
* Headers are send based on given parameters or $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ] :
2013-02-19 16:34:19 +01:00
* - not set of false --> no caching ( default )
* - true --> private caching by browser ( no expires header )
* - " public " or integer --> public caching with given cache_expire in minutes or php . ini default session_cache_expire
*
2014-11-18 13:55:32 +01:00
* @ param int $expire = null expiration time in seconds , default $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ] or php . ini session . cache_expire
2015-02-13 09:27:08 +01:00
* @ param int $private = null allows to set private caching with given expiration time , by setting it to true
2013-02-19 16:34:19 +01:00
*/
2015-02-13 09:27:08 +01:00
public static function cache_control ( $expire = null , $private = null )
2013-02-19 16:34:19 +01:00
{
if ( is_null ( $expire ) && isset ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ]) && is_int ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ]))
{
$expire = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ];
}
// session not yet started: use PHP session_cache_limiter() and session_cache_expires() functions
if ( ! isset ( $_SESSION ))
{
// controling caching and expires header
2015-02-13 09:27:08 +01:00
if ( ! isset ( $expire ) && ( ! isset ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ]) ||
! $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ]))
2013-02-19 16:34:19 +01:00
{
session_cache_limiter ( 'nocache' );
}
2015-02-13 09:27:08 +01:00
elseif ( isset ( $expire ) || $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ] === 'public' || is_int ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'nocachecontrol' ]))
2013-02-19 16:34:19 +01:00
{
// allow public caching: proxys, cdns, ...
if ( isset ( $expire ))
{
session_cache_expire (( int ) ceil ( $expire / 60 )); // in minutes
}
2015-02-13 09:27:08 +01:00
session_cache_limiter ( $private ? 'private' : 'public' );
2013-02-19 16:34:19 +01:00
}
else
{
// allow caching by browser
session_cache_limiter ( 'private_no_expire' );
}
}
// session already started
if ( isset ( $_SESSION ))
{
2014-01-18 15:52:47 +01:00
if ( $expire && ( session_cache_limiter () !== ( $expire === true ? 'private_no_expire' : 'public' ) ||
is_int ( $expire ) && $expire / 60 !== session_cache_expire ()))
2013-02-19 16:34:19 +01:00
{
2014-11-18 13:55:32 +01:00
$file = $line = null ;
2013-02-19 16:34:19 +01:00
if ( headers_sent ( $file , $line ))
{
error_log ( __METHOD__ . " ( $expire ) called, but header already sent in $file : $line " );
2014-01-18 15:52:47 +01:00
return ;
}
2014-01-28 16:06:47 +01:00
if ( $expire === true ) // same behavior as session_cache_limiter('private_no_expire')
2014-01-18 15:52:47 +01:00
{
header ( 'Cache-Control: private, max-age=' . ( 60 * session_cache_expire ()));
2014-01-28 16:06:47 +01:00
header_remove ( 'Expires' );
2013-02-19 16:34:19 +01:00
}
2015-02-13 09:27:08 +01:00
elseif ( $private )
{
header ( 'Cache-Control: private, max-age=' . $expire );
header ( 'Expires: ' . gmdate ( 'D, d M Y H:i:s' , time () + $expire ) . ' GMT' );
}
2013-02-19 16:34:19 +01:00
else
{
2013-04-05 12:23:58 +02:00
header ( 'Cache-Control: public, max-age=' . $expire );
2013-02-19 16:34:19 +01:00
header ( 'Expires: ' . gmdate ( 'D, d M Y H:i:s' , time () + $expire ) . ' GMT' );
2014-01-18 15:52:47 +01:00
}
// remove Pragma header, might be set by old header
if ( function_exists ( 'header_remove' )) // PHP 5.3+
{
header_remove ( 'Pragma' );
}
else
{
header ( 'Pragma:' );
2013-02-19 16:34:19 +01:00
}
}
}
}
2008-08-07 23:12:44 +02:00
/**
* Get a session list ( of the current instance )
*
* @ param int $start
2014-11-18 13:55:32 +01:00
* @ param string $sort = 'DESC' ASC or DESC
* @ param string $order = 'session_dla' session_lid , session_id , session_started , session_logintime , session_action , or ( default ) session_dla
* @ param boolean $all_no_sort = False skip sorting and limiting to maxmatchs if set to true
* @ param array $filter = array () extra filter for sessions
2011-04-13 16:11:09 +02:00
* @ return array with sessions ( values for keys as in $sort )
2008-08-07 23:12:44 +02:00
*/
2012-05-19 20:19:43 +02:00
public static function session_list ( $start , $sort = 'DESC' , $order = 'session_dla' , $all_no_sort = False , array $filter = array ())
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
$sessions = array ();
2012-05-19 20:19:43 +02:00
if ( ! preg_match ( '/^[a-z0-9_ ,]+$/i' , $order_by = $order . ' ' . $sort ) || $order_by == ' ' )
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
$order_by = 'session_dla DESC' ;
2008-08-07 23:12:44 +02:00
}
2012-05-19 20:19:43 +02:00
$filter [ 'lo' ] = null ;
2014-08-19 19:15:50 +02:00
$filter [] = 'account_id>0' ;
2012-05-19 20:19:43 +02:00
$filter [] = 'session_dla > ' . ( int )( time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ]);
$filter [] = '(notification_heartbeat IS NULL OR notification_heartbeat > ' . self :: heartbeat_limit () . ')' ;
foreach ( $GLOBALS [ 'egw' ] -> db -> select ( self :: ACCESS_LOG_TABLE , '*' , $filter , __LINE__ , __FILE__ ,
$all_no_sort ? false : $start , 'ORDER BY ' . $order_by ) as $row )
2011-04-13 16:11:09 +02:00
{
$sessions [ $row [ 'sessionid' ]] = $row ;
}
return $sessions ;
2008-08-07 23:12:44 +02:00
}
/**
* Query number of sessions ( not more then once every N secs )
*
2014-08-19 19:15:50 +02:00
* @ param array $filter = array () extra filter for sessions
2011-04-13 16:11:09 +02:00
* @ return int number of active sessions
2008-08-07 23:12:44 +02:00
*/
2012-05-19 20:19:43 +02:00
public static function session_count ( array $filter = array ())
2008-08-07 23:12:44 +02:00
{
2012-05-19 20:19:43 +02:00
$filter [ 'lo' ] = null ;
2014-08-19 19:15:50 +02:00
$filter [] = 'account_id>0' ;
2012-05-19 20:19:43 +02:00
$filter [] = 'session_dla > ' . ( int )( time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ]);
$filter [] = '(notification_heartbeat IS NULL OR notification_heartbeat > ' . self :: heartbeat_limit () . ')' ;
return $GLOBALS [ 'egw' ] -> db -> select ( self :: ACCESS_LOG_TABLE , 'COUNT(*)' , $filter , __LINE__ , __FILE__ ) -> fetchColumn ();
2011-04-13 16:11:09 +02:00
}
/**
* Get limit / latest time of heartbeat for session to be active
*
* @ return int TS in server - time
*/
public static function heartbeat_limit ()
{
2014-08-19 19:15:50 +02:00
static $limit = null ;
2011-04-13 16:11:09 +02:00
if ( is_null ( $limit ))
2008-08-07 23:12:44 +02:00
{
2011-04-13 16:11:09 +02:00
$config = config :: read ( 'notifications' );
if ( ! ( $popup_poll_interval = $config [ 'popup_poll_interval' ]))
{
$popup_poll_interval = 60 ;
}
$limit = ( int )( time () - $popup_poll_interval - 10 ); // 10s grace periode
2008-08-07 23:12:44 +02:00
}
2011-04-13 16:11:09 +02:00
return $limit ;
}
/**
* Check if given user can be reached via notifications
*
* Checks if notifications callback checked in not more then heartbeat_limit () seconds ago
*
* @ param int $account_id
* @ param int number of active sessions of given user with notifications running
*/
public static function notifications_active ( $account_id )
{
return $GLOBALS [ 'egw' ] -> db -> select ( self :: ACCESS_LOG_TABLE , 'COUNT(*)' , array (
'lo' => null ,
'session_dla > ' . ( int )( time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ]),
'account_id' => $account_id ,
2011-04-13 19:09:18 +02:00
'notification_heartbeat > ' . self :: heartbeat_limit (),
2011-04-13 16:11:09 +02:00
), __LINE__ , __FILE__ ) -> fetchColumn ();
2008-08-07 23:12:44 +02:00
}
/*
* depricated functions , to be removed after 1.6
*/
2008-08-09 06:24:54 +02:00
/**
* Delete all data from the session cache for a user
*
* @ param int $accountid user account id , defaults to current user ( optional )
* @ deprecated not longer used / necessary
*/
function delete_cache ( $accountid = '' )
{
2014-11-18 13:55:32 +01:00
unset ( $accountid ); // not used, but required by function signature
2008-08-09 06:24:54 +02:00
}
2008-08-07 23:12:44 +02:00
}