includes/sessions.php
changeset 228 b0a4d179be85
parent 195 eec079676fe7
parent 227 0eca1498a77b
child 248 ed13b72b13cc
equal deleted inserted replaced
197:90b7a52bea45 228:b0a4d179be85
   360    */
   360    */
   361   
   361   
   362   function start()
   362   function start()
   363   {
   363   {
   364     global $db, $session, $paths, $template, $plugins; // Common objects
   364     global $db, $session, $paths, $template, $plugins; // Common objects
       
   365     global $lang;
   365     if($this->started) return;
   366     if($this->started) return;
   366     $this->started = true;
   367     $this->started = true;
   367     $user = false;
   368     $user = false;
   368     if(isset($_COOKIE['sid']))
   369     if(isset($_COOKIE['sid']))
   369     {
   370     {
   379       {
   380       {
   380         $data = RenderMan::strToPageID($paths->get_pageid_from_url());
   381         $data = RenderMan::strToPageID($paths->get_pageid_from_url());
   381         
   382         
   382         if(!$this->compat && $userdata['account_active'] != 1 && $data[1] != 'Special' && $data[1] != 'Admin')
   383         if(!$this->compat && $userdata['account_active'] != 1 && $data[1] != 'Special' && $data[1] != 'Admin')
   383         {
   384         {
       
   385           $language = intval(getConfig('default_language'));
       
   386           $lang = new Language($language);
       
   387           
   384           $this->logout();
   388           $this->logout();
   385           $a = getConfig('account_activation');
   389           $a = getConfig('account_activation');
   386           switch($a)
   390           switch($a)
   387           {
   391           {
   388             case 'none':
   392             case 'none':
   478             $this->style = $template->default_style;
   482             $this->style = $template->default_style;
   479           }
   483           }
   480         }
   484         }
   481         $user = true;
   485         $user = true;
   482         
   486         
       
   487         // Set language
       
   488         if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
       
   489         {
       
   490           $lang_id = intval($userdata['user_lang']);
       
   491           $lang = new Language($lang_id);
       
   492         }
       
   493         
   483         if(isset($_REQUEST['auth']) && !$this->sid_super)
   494         if(isset($_REQUEST['auth']) && !$this->sid_super)
   484         {
   495         {
   485           // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
   496           // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
   486           if($this->compat)
   497           if($this->compat)
   487           {
   498           {
   545    * @param string $username The username
   556    * @param string $username The username
   546    * @param string $aes_data The encrypted password, hex-encoded
   557    * @param string $aes_data The encrypted password, hex-encoded
   547    * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   558    * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
   548    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   559    * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
   549    * @param int $level The privilege level we're authenticating for, defaults to 0
   560    * @param int $level The privilege level we're authenticating for, defaults to 0
       
   561    * @param array $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
       
   562    * @param array $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
   550    * @return string 'success' on success, or error string on failure
   563    * @return string 'success' on success, or error string on failure
   551    */
   564    */
   552    
   565    
   553   function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER)
   566   function login_with_crypto($username, $aes_data, $aes_key, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false)
   554   {
   567   {
   555     global $db, $session, $paths, $template, $plugins; // Common objects
   568     global $db, $session, $paths, $template, $plugins; // Common objects
   556     
   569     
   557     $privcache = $this->private_key;
   570     $privcache = $this->private_key;
       
   571 
       
   572     if ( !defined('IN_ENANO_INSTALL') )
       
   573     {
       
   574       // Lockout stuff
       
   575       $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
       
   576       $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
       
   577       // convert to minutes
       
   578       $duration  = $duration * 60;
       
   579       $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
       
   580       if ( $policy == 'captcha' && $captcha_hash && $captcha_code )
       
   581       {
       
   582         // policy is captcha -- check if it's correct, and if so, bypass lockout check
       
   583         $real_code = $this->get_captcha($captcha_hash);
       
   584       }
       
   585       if ( $policy != 'disable' && !( $policy == 'captcha' && isset($real_code) && $real_code == $captcha_code ) )
       
   586       {
       
   587         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   588         $timestamp_cutoff = time() - $duration;
       
   589         $q = $this->sql('SELECT timestamp FROM '.table_prefix.'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
       
   590         $fails = $db->numrows();
       
   591         if ( $fails >= $threshold )
       
   592         {
       
   593           // ooh boy, somebody's in trouble ;-)
       
   594           $row = $db->fetchrow();
       
   595           $db->free_result();
       
   596           return array(
       
   597               'success' => false,
       
   598               'error' => 'locked_out',
       
   599               'lockout_threshold' => $threshold,
       
   600               'lockout_duration' => ( $duration / 60 ),
       
   601               'lockout_fails' => $fails,
       
   602               'lockout_policy' => $policy,
       
   603               'time_rem' => ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ),
       
   604               'lockout_last_time' => $row['timestamp']
       
   605             );
       
   606         }
       
   607         $db->free_result();
       
   608       }
       
   609     }
   558     
   610     
   559     // Instanciate the Rijndael encryption object
   611     // Instanciate the Rijndael encryption object
   560     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   612     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   561     
   613     
   562     // Fetch our decryption key
   614     // Fetch our decryption key
   563     
   615     
   564     $aes_key = $this->fetch_public_key($aes_key);
   616     $aes_key = $this->fetch_public_key($aes_key);
   565     if(!$aes_key)
   617     if(!$aes_key)
   566       return 'Couldn\'t look up public key "'.$aes_key.'" for decryption';
   618       return array(
       
   619         'success' => false,
       
   620         'error' => 'key_not_found'
       
   621         );
   567     
   622     
   568     // Convert the key to a binary string
   623     // Convert the key to a binary string
   569     $bin_key = hexdecode($aes_key);
   624     $bin_key = hexdecode($aes_key);
   570     
   625     
   571     if(strlen($bin_key) != AES_BITS / 8)
   626     if(strlen($bin_key) != AES_BITS / 8)
   572       return 'The decryption key is the wrong length';
   627       return array(
       
   628         'success' => false,
       
   629         'error' => 'key_wrong_length'
       
   630         );
   573     
   631     
   574     // Decrypt our password
   632     // Decrypt our password
   575     $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   633     $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
   576     
   634     
   577     // Initialize our success switch
   635     // Initialize our success switch
   588       // This wasn't logged in <1.0.2, dunno how it slipped through
   646       // This wasn't logged in <1.0.2, dunno how it slipped through
   589       if($level > USER_LEVEL_MEMBER)
   647       if($level > USER_LEVEL_MEMBER)
   590         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   648         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   591       else
   649       else
   592         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   650         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   593       return "The username and/or password is incorrect.";  
   651     
       
   652       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   653       {
       
   654         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   655         // increment fail count
       
   656         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', UNIX_TIMESTAMP(), \'credential\');');
       
   657         $fails++;
       
   658         // ooh boy, somebody's in trouble ;-)
       
   659         return array(
       
   660             'success' => false,
       
   661             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   662             'lockout_threshold' => $threshold,
       
   663             'lockout_duration' => ( $duration / 60 ),
       
   664             'lockout_fails' => $fails,
       
   665             'time_rem' => ( $duration / 60 ),
       
   666             'lockout_policy' => $policy
       
   667           );
       
   668       }
       
   669       
       
   670       return array(
       
   671           'success' => false,
       
   672           'error' => 'invalid_credentials'
       
   673         );
   594     }
   674     }
   595     $row = $db->fetchrow();
   675     $row = $db->fetchrow();
   596     
   676     
   597     // Check to see if we're logging in using a temporary password
   677     // Check to see if we're logging in using a temporary password
   598     
   678     
   639       }
   719       }
   640     }
   720     }
   641     if($success)
   721     if($success)
   642     {
   722     {
   643       if($level > $row['user_level'])
   723       if($level > $row['user_level'])
   644         return 'You are not authorized for this level of access.';
   724         return array(
       
   725           'success' => false,
       
   726           'error' => 'too_big_for_britches'
       
   727         );
   645       
   728       
   646       $sess = $this->register_session(intval($row['user_id']), $username, $password, $level);
   729       $sess = $this->register_session(intval($row['user_id']), $username, $password, $level);
   647       if($sess)
   730       if($sess)
   648       {
   731       {
   649         $this->username = $username;
   732         $this->username = $username;
   659         $code = $plugins->setHook('login_success');
   742         $code = $plugins->setHook('login_success');
   660         foreach ( $code as $cmd )
   743         foreach ( $code as $cmd )
   661         {
   744         {
   662           eval($cmd);
   745           eval($cmd);
   663         }
   746         }
   664         return 'success';
   747         return array(
       
   748           'success' => true
       
   749         );
   665       }
   750       }
   666       else
   751       else
   667         return 'Your login credentials were correct, but an internal error occurred while registering the session key in the database.';
   752         return array(
       
   753           'success' => false,
       
   754           'error' => 'backend_fail'
       
   755         );
   668     }
   756     }
   669     else
   757     else
   670     {
   758     {
   671       if($level > USER_LEVEL_MEMBER)
   759       if($level > USER_LEVEL_MEMBER)
   672         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   760         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   673       else
   761       else
   674         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   762         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   675         
   763         
   676       return 'The username and/or password is incorrect.';
   764       // Do we also need to increment the lockout countdown?
       
   765       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   766       {
       
   767         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   768         // increment fail count
       
   769         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', UNIX_TIMESTAMP(), \'credential\');');
       
   770         $fails++;
       
   771         return array(
       
   772             'success' => false,
       
   773             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   774             'lockout_threshold' => $threshold,
       
   775             'lockout_duration' => ( $duration / 60 ),
       
   776             'lockout_fails' => $fails,
       
   777             'time_rem' => ( $duration / 60 ),
       
   778             'lockout_policy' => $policy
       
   779           );
       
   780       }
       
   781         
       
   782       return array(
       
   783         'success' => false,
       
   784         'error' => 'invalid_credentials'
       
   785       );
   677     }
   786     }
   678   }
   787   }
   679   
   788   
   680   /**
   789   /**
   681    * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   790    * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
   697     if($this->compat)
   806     if($this->compat)
   698     {
   807     {
   699       return $this->login_compat($username, $pass_hashed, $level);
   808       return $this->login_compat($username, $pass_hashed, $level);
   700     }
   809     }
   701     
   810     
       
   811     if ( !defined('IN_ENANO_INSTALL') )
       
   812     {
       
   813       // Lockout stuff
       
   814       $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
       
   815       $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
       
   816       // convert to minutes
       
   817       $duration  = $duration * 60;
       
   818       $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
       
   819       if ( $policy == 'captcha' && $captcha_hash && $captcha_code )
       
   820       {
       
   821         // policy is captcha -- check if it's correct, and if so, bypass lockout check
       
   822         $real_code = $this->get_captcha($captcha_hash);
       
   823       }
       
   824       if ( $policy != 'disable' && !( $policy == 'captcha' && isset($real_code) && $real_code == $captcha_code ) )
       
   825       {
       
   826         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   827         $timestamp_cutoff = time() - $duration;
       
   828         $q = $this->sql('SELECT timestamp FROM '.table_prefix.'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
       
   829         $fails = $db->numrows();
       
   830         if ( $fails > $threshold )
       
   831         {
       
   832           // ooh boy, somebody's in trouble ;-)
       
   833           $row = $db->fetchrow();
       
   834           $db->free_result();
       
   835           return array(
       
   836               'success' => false,
       
   837               'error' => 'locked_out',
       
   838               'lockout_threshold' => $threshold,
       
   839               'lockout_duration' => ( $duration / 60 ),
       
   840               'lockout_fails' => $fails,
       
   841               'lockout_policy' => $policy,
       
   842               'time_rem' => $duration - round( ( time() - $row['timestamp'] ) / 60 ),
       
   843               'lockout_last_time' => $row['timestamp']
       
   844             );
       
   845         }
       
   846         $db->free_result();
       
   847       }
       
   848     }
       
   849     
   702     // Instanciate the Rijndael encryption object
   850     // Instanciate the Rijndael encryption object
   703     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   851     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
   704     
   852     
   705     // Initialize our success switch
   853     // Initialize our success switch
   706     $success = false;
   854     $success = false;
   707     
   855     
   708     // Retrieve the real password from the database
   856     // Retrieve the real password from the database
   709     $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';');
   857     $this->sql('SELECT password,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix.'users WHERE lcase(username)=\''.$this->prepare_text(strtolower($username)).'\';');
   710     if ( $db->numrows() < 1 )
   858     if($db->numrows() < 1)
   711     {
   859     {
   712       // This wasn't logged in <1.0.2, dunno how it slipped through
   860       // This wasn't logged in <1.0.2, dunno how it slipped through
   713       if($level > USER_LEVEL_MEMBER)
   861       if($level > USER_LEVEL_MEMBER)
   714         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   862         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   715       else
   863       else
   716         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   864         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   717       return "The username and/or password is incorrect.";  
   865       
       
   866       // Do we also need to increment the lockout countdown?
       
   867       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   868       {
       
   869         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   870         // increment fail count
       
   871         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', UNIX_TIMESTAMP(), \'credential\');');
       
   872         $fails++;
       
   873         return array(
       
   874             'success' => false,
       
   875             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   876             'lockout_threshold' => $threshold,
       
   877             'lockout_duration' => ( $duration / 60 ),
       
   878             'lockout_fails' => $fails,
       
   879             'lockout_policy' => $policy
       
   880           );
       
   881       }
       
   882       
       
   883       return array(
       
   884         'success' => false,
       
   885         'error' => 'invalid_credentials'
       
   886       );
   718     }
   887     }
   719     $row = $db->fetchrow();
   888     $row = $db->fetchrow();
   720     
   889     
   721     // Check to see if we're logging in using a temporary password
   890     // Check to see if we're logging in using a temporary password
   722     
   891     
   762       }
   931       }
   763     }
   932     }
   764     if($success)
   933     if($success)
   765     {
   934     {
   766       if((int)$level > (int)$row['user_level'])
   935       if((int)$level > (int)$row['user_level'])
   767         return 'You are not authorized for this level of access.';
   936         return array(
       
   937           'success' => false,
       
   938           'error' => 'too_big_for_britches'
       
   939         );
   768       $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level);
   940       $sess = $this->register_session(intval($row['user_id']), $username, $real_pass, $level);
   769       if($sess)
   941       if($sess)
   770       {
   942       {
   771         if($level > USER_LEVEL_MEMBER)
   943         if($level > USER_LEVEL_MEMBER)
   772           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   944           $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   777         foreach ( $code as $cmd )
   949         foreach ( $code as $cmd )
   778         {
   950         {
   779           eval($cmd);
   951           eval($cmd);
   780         }
   952         }
   781         
   953         
   782         return 'success';
   954         return array(
       
   955           'success' => true
       
   956           );
   783       }
   957       }
   784       else
   958       else
   785         return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.';
   959         return array(
       
   960           'success' => false,
       
   961           'error' => 'backend_fail'
       
   962         );
   786     }
   963     }
   787     else
   964     else
   788     {
   965     {
   789       if($level > USER_LEVEL_MEMBER)
   966       if($level > USER_LEVEL_MEMBER)
   790         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   967         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
   791       else
   968       else
   792         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   969         $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.date('d M Y h:i a').'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
   793         
   970         
   794       return 'The username and/or password is incorrect.';
   971       // Do we also need to increment the lockout countdown?
       
   972       if ( $policy != 'disable' && !defined('IN_ENANO_INSTALL') )
       
   973       {
       
   974         $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
       
   975         // increment fail count
       
   976         $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action) VALUES(\'' . $ipaddr . '\', UNIX_TIMESTAMP(), \'credential\');');
       
   977         $fails++;
       
   978         return array(
       
   979             'success' => false,
       
   980             'error' => ( $fails >= $threshold ) ? 'locked_out' : 'invalid_credentials',
       
   981             'lockout_threshold' => $threshold,
       
   982             'lockout_duration' => ( $duration / 60 ),
       
   983             'lockout_fails' => $fails,
       
   984             'lockout_policy' => $policy
       
   985           );
       
   986       }
       
   987         
       
   988       return array(
       
   989         'success' => false,
       
   990         'error' => 'invalid_credentials'
       
   991       );
   795     }
   992     }
   796   }
   993   }
   797   
   994   
   798   /**
   995   /**
   799    * Attempts to log in using the old table structure and algorithm.
   996    * Attempts to log in using the old table structure and algorithm.
   861     }
  1058     }
   862     else
  1059     else
   863     {
  1060     {
   864       // Stash it in a cookie
  1061       // Stash it in a cookie
   865       // For now, make the cookie last forever, we can change this in 1.1.x
  1062       // For now, make the cookie last forever, we can change this in 1.1.x
   866       setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/' );
  1063       setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/', null, ( isset($_SERVER['HTTPS']) ) );
   867       $_COOKIE['sid'] = $session_key;
  1064       $_COOKIE['sid'] = $session_key;
   868     }
  1065     }
   869     // $keyhash is stored in the database, this is for compatibility with the older DB structure
  1066     // $keyhash is stored in the database, this is for compatibility with the older DB structure
   870     $keyhash = md5($session_key);
  1067     $keyhash = md5($session_key);
   871     // Record the user's IP
  1068     // Record the user's IP
   923    */
  1120    */
   924    
  1121    
   925   function register_guest_session()
  1122   function register_guest_session()
   926   {
  1123   {
   927     global $db, $session, $paths, $template, $plugins; // Common objects
  1124     global $db, $session, $paths, $template, $plugins; // Common objects
       
  1125     global $lang;
   928     $this->username = $_SERVER['REMOTE_ADDR'];
  1126     $this->username = $_SERVER['REMOTE_ADDR'];
   929     $this->user_level = USER_LEVEL_GUEST;
  1127     $this->user_level = USER_LEVEL_GUEST;
   930     if($this->compat || defined('IN_ENANO_INSTALL'))
  1128     if($this->compat || defined('IN_ENANO_INSTALL'))
   931     {
  1129     {
   932       $this->theme = 'oxygen';
  1130       $this->theme = 'oxygen';
   936     {
  1134     {
   937       $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
  1135       $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
   938       $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : substr($template->named_theme_list[$this->theme]['default_style'], 0, strlen($template->named_theme_list[$this->theme]['default_style'])-4);
  1136       $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : substr($template->named_theme_list[$this->theme]['default_style'], 0, strlen($template->named_theme_list[$this->theme]['default_style'])-4);
   939     }
  1137     }
   940     $this->user_id = 1;
  1138     $this->user_id = 1;
       
  1139     if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
       
  1140     {
       
  1141       // This is a VERY special case we are allowing. It lets the installer create languages using the Enano API.
       
  1142       $language = intval(getConfig('default_language'));
       
  1143       $lang = new Language($language);
       
  1144     }
   941   }
  1145   }
   942   
  1146   
   943   /**
  1147   /**
   944    * Validates a session key, and returns the userdata associated with the key or false
  1148    * Validates a session key, and returns the userdata associated with the key or false
   945    * @param string $key The session key to validate
  1149    * @param string $key The session key to validate
   963       // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
  1167       // echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
   964       return false;
  1168       return false;
   965     }
  1169     }
   966     $keyhash = md5($key);
  1170     $keyhash = md5($key);
   967     $salt = $db->escape($keydata[3]);
  1171     $salt = $db->escape($keydata[3]);
   968     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,x.* FROM '.table_prefix.'session_keys AS k
  1172     $query = $db->sql_query('SELECT u.user_id AS uid,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms,u.user_lang,x.* FROM '.table_prefix.'session_keys AS k
   969                                LEFT JOIN '.table_prefix.'users AS u
  1173                                LEFT JOIN '.table_prefix.'users AS u
   970                                  ON ( u.user_id=k.user_id )
  1174                                  ON ( u.user_id=k.user_id )
   971                                LEFT JOIN '.table_prefix.'users_extra AS x
  1175                                LEFT JOIN '.table_prefix.'users_extra AS x
   972                                  ON ( u.user_id=x.user_id OR x.user_id IS NULL )
  1176                                  ON ( u.user_id=x.user_id OR x.user_id IS NULL )
   973                                LEFT JOIN '.table_prefix.'privmsgs AS p
  1177                                LEFT JOIN '.table_prefix.'privmsgs AS p
  1118     $ou = $this->username;
  1322     $ou = $this->username;
  1119     $oid = $this->user_id;
  1323     $oid = $this->user_id;
  1120     if($level > USER_LEVEL_CHPREF)
  1324     if($level > USER_LEVEL_CHPREF)
  1121     {
  1325     {
  1122       $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1326       $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
  1123       if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD) return 'success';
  1327       if(!$this->user_logged_in || $this->auth_level < USER_LEVEL_MOD)
       
  1328       {
       
  1329         return 'success';
       
  1330       }
  1124       // Destroy elevated privileges
  1331       // Destroy elevated privileges
  1125       $keyhash = md5(strrev($this->sid_super));
  1332       $keyhash = md5(strrev($this->sid_super));
  1126       $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1333       $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
  1127       $this->sid_super = false;
  1334       $this->sid_super = false;
  1128       $this->auth_level = USER_LEVEL_MEMBER;
  1335       $this->auth_level = USER_LEVEL_MEMBER;