* @version $Revision: 1.220 $ * @package users * @subpackage BitUser */ class BitUser extends LibertyMime { var $mUserId; var $mUsername; var $mGroups; var $mInfo; var $mTicket; var $mAuth; /** * Constructor - will automatically load all relevant data if passed a user string * * @access public * @author Christian Fowler * @return returnString */ function BitUser( $pUserId=NULL, $pContentId=NULL ) { LibertyMime::LibertyMime(); $this->mContentTypeGuid = BITUSER_CONTENT_TYPE_GUID; $this->registerContentType( BITUSER_CONTENT_TYPE_GUID, array( 'content_type_guid' => BITUSER_CONTENT_TYPE_GUID, 'content_description' => 'User Information', 'handler_class' => 'BitUser', 'handler_package' => 'users', 'handler_file' => 'BitUser.php', 'maintainer_url' => 'http://www.bitweaver.org' ) ); $this->mUserId = ( @$this->verifyId( $pUserId ) ? $pUserId : NULL); $this->mContentId = $pContentId; } /** * load - loads all settings & preferences for this user * * @param boolean $pFull Load additional user data like * @param string $pUserName User login name * @access public * @author Chrstian Fowler * @return returnString */ function load( $pFull=FALSE, $pUserName=NULL ) { global $gBitSystem; $this->mInfo = NULL; if( isset( $this->mUserId ) ) { $whereSql = "WHERE uu.`user_id`=?"; $bindVars = array( $this->mUserId ); } elseif( isset( $this->mContentId ) ) { $whereSql = "WHERE uu.`content_id`=?"; $bindVars = array( $this->mContentId ); } elseif( !empty( $pUserName ) ) { $whereSql = "WHERE uu.`login`=?"; $bindVars = array( $pUserName ); } if( isset( $whereSql ) ) { $fullSelect = ''; $fullJoin = ''; if( $pFull ) { $fullSelect = ' , lc.* '; $fullJoin = " LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( uu.`content_id`=lc.`content_id` )"; $this->getServicesSql( 'content_load_sql_function', $fullSelect, $fullJoin, $whereSql, $bindVars ); } // uu.`user_id` AS `uu_user_id` is last and aliases to avoid possible column name collisions $query = " SELECT uu.*, tf_ava.`storage_path` AS `avatar_storage_path`, tf_por.`storage_path` AS `portrait_storage_path`, tf_logo.`storage_path` AS `logo_storage_path` $fullSelect, uu.`user_id` AS `uu_user_id` FROM `".BIT_DB_PREFIX."users_users` uu LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_ava ON ( uu.`avatar_attachment_id`=ta_ava.`attachment_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` tf_ava ON ( tf_ava.`file_id`=ta_ava.`foreign_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_por ON ( uu.`portrait_attachment_id`=ta_por.`attachment_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` tf_por ON ( tf_por.`file_id`=ta_por.`foreign_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_logo ON ( uu.`logo_attachment_id`=ta_logo.`attachment_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` tf_logo ON ( tf_logo.`file_id`=ta_logo.`foreign_id` ) $fullJoin $whereSql"; if(( $result = $this->mDb->query( $query, $bindVars )) && $result->numRows() ) { $this->mInfo = $result->fetchRow(); $this->mInfo['user'] = $this->mInfo['login']; $this->mInfo['valid'] = @$this->verifyId( $this->mInfo['uu_user_id'] ); $this->mInfo['user_id'] = $this->mInfo['uu_user_id']; $this->mInfo['is_registered'] = $this->isRegistered(); $this->mInfo['avatar_url'] = liberty_fetch_thumbnail_url( array( 'storage_path' => $this->mInfo['avatar_storage_path'], 'size' => 'avatar', 'mime_image' => FALSE )); $this->mInfo['portrait_url'] = liberty_fetch_thumbnail_url( array( 'storage_path' => $this->mInfo['portrait_storage_path'], 'size' => 'medium', 'mime_image' => FALSE )); $this->mInfo['logo_url'] = ( !empty( $this->mInfo['logo_storage_path'] ) ? BIT_ROOT_URL.$this->mInfo['logo_storage_path'] : NULL); $this->mInfo['avatar_path'] = ( !empty( $this->mInfo['avatar_storage_path'] ) ? BIT_ROOT_PATH.$this->mInfo['avatar_storage_path'] : NULL); $this->mInfo['portrait_path'] = ( !empty( $this->mInfo['portrait_storage_path'] ) ? BIT_ROOT_PATH.$this->mInfo['portrait_storage_path']: NULL); $this->mInfo['logo_path'] = ( !empty( $this->mInfo['logo_storage_path'] ) ? BIT_ROOT_PATH.$this->mInfo['logo_storage_path'] : NULL); $this->mUserId = $this->mInfo['uu_user_id']; $this->mContentId = $this->mInfo['content_id']; $this->mUsername = $this->mInfo['login']; // a few random security conscious unset's - SPIDER unset( $this->mInfo['user_password'] ); unset( $this->mInfo['hash'] ); $this->loadPreferences(); // Load attachments LibertyMime::load(); if( $this->getPreference( 'users_country' ) ) { $this->setPreference( 'flag', $this->getPreference( 'users_country' ) ); $this->setPreference( 'users_country', str_replace( '_', ' ', $this->getPreference( 'users_country' ) ) ); } if( $pFull ) { $this->mInfo['real_name'] = trim( $this->mInfo['real_name'] ); $this->mInfo['display_name'] = ( ( !empty( $this->mInfo['real_name'] ) ? $this->mInfo['real_name'] : ( !empty( $this->mUsername) ? $this->mUsername : ( !empty( $this->mInfo['email'] ) ? substr( $this->mInfo['email'], 0, strpos( $this->mInfo['email'],'@' )) : $this->mUserId ))) ); //print("displayName: ".$this->mInfo['display_name']); $this->defaults(); $this->mInfo['publicEmail'] = scramble_email( $this->mInfo['email'], ( $this->getPreference( 'users_email_display' ) ? $this->getPreference( 'users_email_display' ) : NULL ) ); } $this->mTicket = substr( md5( session_id() . $this->mUserId ), 0, 20 ); } else { $this->mUserId = NULL; } } if( !$gBitSystem->isFeatureActive( 'i18n_browser_languages' ) ) { global $gBitLanguage, $gBitUser; //change language only if if logged user is this user //otherwise it's just logged user (lang A) watching other user's page (lang B) and don't change if( $this->mUserId && $this->mUserId != ANONYMOUS_USER_ID && $gBitUser === $this) { $gBitLanguage->mLanguage = $this->getPreference( 'bitlanguage', $gBitLanguage->mLanguage ); } elseif( isset( $_SESSION['bitlanguage'] )) { // users not logged that change the preference $gBitLanguage->mLanguage = $_SESSION['bitlanguage']; } } return( $this->isValid() ); } /** * defaults set a default set of preferences in mPrefs for new users * * @access public * @return void */ function defaults() { global $gBitSystem, $gBitThemes, $gBitLanguage; if( !$this->getPreference( 'users_information' ) ) { $this->setPreference( 'users_information', 'public' ); } if( !$this->getPreference( 'messages_allow_messages' ) ) { $this->setPreference( 'messages_allow_messages', 'y' ); } if( !$this->getPreference( 'site_display_utc' ) ) { $this->setPreference( 'site_display_utc', 'Local' ); } /* * site_display_timezone is not used for 'Local' time display so daylight saving offset is not available * both of these should pick up the 'site default' values if( !$this->getPreference( 'site_display_timezone' ) ) { $this->setPreference( 'site_display_timezone', 'UTC' ); } */ if( !$this->getPreference( 'bitlanguage' ) ) { $this->setPreference( 'bitlanguage', $gBitLanguage->mLanguage ); } if( !$this->getPreference( 'theme' ) ) { $this->setPreference( 'theme', $gBitThemes->getStyle() ); } } /** * verify store hash * * @param array $pParamHash Data to be verified * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function verify( &$pParamHash ) { global $gBitSystem; trim_array( $pParamHash ); // DO NOT REMOVE - to allow specific setting of the user_id during the first store. // used by ROOT_USER_ID or ANONYMOUS_USER_ID during install. if( @$this->verifyId( $pParamHash['user_id'] ) ) { $pParamHash['user_store']['user_id'] = $pParamHash['user_id']; } if( !empty( $pParamHash['login'] ) ) { if( $this->userExists( array( 'login' => $pParamHash['login'] ) ) ) { $this->mErrors['login'] = 'The username "'.$pParamHash['login'].'" is already in use'; } elseif( preg_match( '/[^A-Za-z0-9_.-]/', $pParamHash["login"] ) ) { $this->mErrors['login'] = tra( "Your username can only contain numbers, characters, underscores and hyphens." ); } else { // LOWER CASE all logins $pParamHash['login'] = strtolower( $pParamHash['login'] ); $pParamHash['user_store']['login'] = $pParamHash['login']; } } if( !empty( $pParamHash['real_name'] ) ) { $pParamHash['user_store']['real_name'] = substr( $pParamHash['real_name'], 0, 64 ); } if( !empty( $pParamHash['email'] ) ) { // LOWER CASE all emails $pParamHash['email'] = strtolower( $pParamHash['email'] ); if( $this->verifyEmail( $pParamHash['email'] ) ) { $pParamHash['user_store']['email'] = strtolower( substr( $pParamHash['email'], 0, 200 ) ); } } // check some new user requirements if( !$this->isRegistered() ) { if( empty( $pParamHash['login'] ) ) { // choose a login based on the username in the email $loginBase = preg_replace( '/[^A-Za-z0-9_]/', '', substr( $pParamHash['email'], 0, strpos( $pParamHash['email'], '@' ) ) ); $login = $loginBase; do { if( $loginTaken = $this->userExists( array( 'login' => $login ) ) ) { $login = $loginBase.rand(100,999); } } while( $loginTaken ); $pParamHash['login'] = $login; } if( empty( $pParamHash['registration_date'] ) ) { $pParamHash['registration_date'] = date( "U" ); } $pParamHash['user_store']['registration_date'] = $pParamHash['registration_date']; if( empty( $pParamHash['email'] ) ) { $this->mErrors['email'] = tra( 'You must enter your email address' ); } if( $gBitSystem->isFeatureActive( 'users_validate_user' ) ) { $pParamHash['user_store']['provpass'] = md5(BitSystem::genPass()); $pParamHash['pass_due'] = 0; } elseif( empty( $pParamHash['password'] ) ) { $this->mErrors['password'] = tra( 'Your password should be at least '.$gBitSystem->getConfig( 'users_min_pass_length', 4 ).' characters long' ); } } elseif( $this->isValid() ) { // Prevent loosing user info on save if( empty( $pParamHash['edit'] ) ) { $pParamHash['edit'] = $this->mInfo['data']; } } if( isset( $pParamHash['password'] ) ) { if (!$this->isValid() || isset($pParamHash['password']) ) { $passswordError = $this->verifyPasswordFormat( $pParamHash['password'] ); } if( !empty( $passswordError ) ) { $this->mErrors['password'] = $passswordError; } else { // Generate a unique hash //$pParamHash['user_store']['hash'] = md5( strtolower( (!empty($pParamHash['login'])?$pParamHash['login']:'') ).$pPassword.$pParamHash['email'] ); $pParamHash['user_store']['hash'] = md5( $pParamHash['password'] ); $now = $gBitSystem->getUTCTime(); if( !isset( $pParamHash['pass_due'] ) && $gBitSystem->getConfig('users_pass_due') ) { $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $gBitSystem->getConfig('users_pass_due') ); } elseif( isset( $pParamHash['pass_due'] ) ) { // renew password only next half year ;) $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $pParamHash['pass_due']); } if( $gBitSystem->isFeatureActive( 'users_clear_passwords' ) || !empty( $pParamHash['user_store']['provpass'] ) ) { $pParamHash['user_store']['user_password'] = $pParamHash['password']; } } } // if we have an error we get them all by checking parent classes for additional errors if( count( $this->mErrors ) > 0 ){ parent::verify( $pParamHash ); } return ( count($this->mErrors) == 0 ); } /** * verifyPasswordFormat * * @param array $pPassword * @param array $pPassword2 * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function verifyPasswordFormat( $pPassword, $pPassword2=NULL ) { global $gBitSystem; $minPassword = $gBitSystem->getConfig( 'users_min_pass_length', 4 ); if( strlen( $pPassword ) < $minPassword ) { return ( tra( 'Your password should be at least '.$minPassword.' characters long' )); } if( !empty( $pPassword2 ) && $pPassword != $pPassword2 ) { return( tra( 'The passwords do not match' )); } if( $gBitSystem->isFeatureActive( 'users_pass_chr_num' ) && ( !preg_match_all( "/[0-9]+/",$pPassword,$foo ) || !preg_match_all( "/[A-Za-z]+/",$pPassword,$foo ))) { return ( tra( 'Password must contain both letters and numbers' )); } return FALSE; } /** * getSmtpResponse * * @param array $pConnect * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getSmtpResponse( &$pConnect ) { $out = ""; while( 1 ) { $work = fgets( $pConnect, 1024 ); $out .= $work; if( !preg_match( '/^\d\d\d-/',$work )) { break; } } return $out; } /** * verifyEmail * * @param array $pEmail * @param array $pValidate * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function verifyEmail( $pEmail, $pValidate = FALSE ) { global $gBitSystem; if( !empty( $this ) ) { $errors = &$this->mErrors; } else { $errors = array(); } // check for existing user first, so root@localhost doesn't get attempted to re-register if( !empty( $this ) && is_object( $this ) && $this->userExists( array( 'email' => $pEmail ) ) ) { $errors['email'] = 'The email address "'.$pEmail.'" has already been registered.'; // during install we have some @localhost as email address. we won't cause problems on those } elseif( $pEmail == 'root@localhost' || $pEmail == 'guest@localhost' ) { // nothing to do } elseif( !validate_email_syntax( $pEmail ) ) { $errors['email'] = 'The email address "'.$pEmail.'" is invalid.'; } elseif( $gBitSystem->isFeatureActive( 'users_validate_email' ) ) { if( !$this->verifyMX( $pEmail, $pValidate ) ) { $errors['email'] = 'Cannot find a valid MX host'; } } return( count( $errors ) == 0 ); } /** * verifyMX * * @param array $pEmail * @param array $pValidate * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function verifyMX( $pEmail, $pValidate = FALSE ) { global $gBitSystem, $gDebug; $HTTP_HOST=$_SERVER['SERVER_NAME']; if( !empty( $this ) ) { $errors = &$this->mErrors; } else { $errors = array(); } list ( $Username, $domain ) = split ("@",$pEmail); // That MX(mail exchanger) record exists in domain check . // checkdnsrr function reference : http://www.php.net/manual/en/function.checkdnsrr.php if( !is_windows() and checkdnsrr ( $domain, "MX" ) ) { bitdebug( "Confirmation : MX record about {$domain} exists." ); // If MX record exists, save MX record address. // getmxrr function reference : http://www.php.net/manual/en/function.getmxrr.php // Sometimes only the highest priority MX are active $MXWeights = array(); $lowest_weight = 99999; $lowest_weight_index = 0; if( getmxrr ( $domain, $MXHost, $MXWeights ) ) { for( $i = 0; $i < count( $MXHost ); $i++ ) { if( $MXWeights[$i] < $lowest_weight ) { $lowest_weight = $MXWeights[$i]; $lowest_weight_index = $i; } } if( !empty( $gDebug )) { $debug = "Confirmation : Is confirming address by MX LOOKUP.
"; for ( $i = 0,$j = 1; $i < count ( $MXHost ); $i++,$j++ ) { $debug .= " • Result( $j ) - $MXHost[$i]
"; } } } // Getmxrr function does to store MX record address about $domain in arrangement form to $MXHost. // $ConnectAddress socket connection address. $ConnectAddress = $MXHost[$lowest_weight_index]; } else { // If there is no MX record simply @ to next time address socket connection do . $ConnectAddress = $domain; bitdebug( "Confirmation : MX record about {$domain} does not exist." ); } if( !$pValidate ) { // Skip the connecting test if it didn't work the first time // fsockopen function reference : http://www.php.net/manual/en/function.fsockopen.php $Connect = @fsockopen ( $ConnectAddress, 25 ); // Success in socket connection if( $Connect ) { bitdebug( "Connection succeeded to {$ConnectAddress} SMTP." ); // Judgment is that service is preparing though begin by 220 getting string after connection . // fgets function reference : http://www.php.net/manual/en/function.fgets.php // A "Real domain name required for sender address" stream_set_timeout( $Connect, 90 ); $out = $this->getSmtpResponse( $Connect ); if( ereg ( "^220", $out ) ) { // Inform client's reaching to server who connect. if( $gBitSystem->hasValidSenderEmail() ) { $senderEmail = $gBitSystem->getConfig( 'site_sender_email' ); fputs( $Connect, "HELO $HTTP_HOST\r\n" ); bitdebug( "Run : HELO $HTTP_HOST" ); // Receive server's answering cord. $out = $this->getSmtpResponse( $Connect ); // Inform sender's address to server. fputs ( $Connect, "MAIL FROM: <{$senderEmail}>\r\n" ); bitdebug( "Run : MAIL FROM: <{$senderEmail}>" ); // Receive server's answering cord. $from = $this->getSmtpResponse( $Connect ); // Inform listener's address to server. fputs ( $Connect, "RCPT TO: <{$pEmail}>\r\n" ); bitdebug( "Run : RCPT TO: <{$pEmail}>" ); // Receive server's answering cord. $to = $this->getSmtpResponse( $Connect ); // Finish connection. fputs( $Connect, "QUIT\r\n" ); bitdebug( "Run : QUIT" ); fclose( $Connect ); // Server's answering cord about MAIL and TO command checks. // Server about listener's address reacts to 550 codes if there does not exist // checking that mailbox is in own E-Mail account. if( !ereg ( "^250", $from ) || ( !ereg ( "^250", $to ) && !ereg( "Please use your ISP relay", $to ))) { $errors['email'] = $pEmail." is not recognized by the mail server to=$to= from=$from= out=$out="; } } } } else { if( empty( $out )) { $out = 'n/a'; } $errors['email'] = "Cannot connect to mail server ({$ConnectAddress}). response='$out'"; } } return( count( $errors ) == 0 ); } /** * register - will handle everything necessary for registering a user and sending appropriate emails, etc. * * @access public * @author Christian Fowler * @return TRUE on success, FALSE on failure */ function register( &$pParamHash ) { global $notificationlib, $gBitSmarty, $gBitSystem, $gBitUser; $ret = FALSE; if( !empty( $_FILES['user_portrait_file'] ) && empty( $_FILES['user_avatar_file'] ) ) { $pParamHash['user_auto_avatar'] = TRUE; } if( $this->verify( $pParamHash )) { for( $i = 0; $i < BaseAuth::getAuthMethodCount(); $i++ ) { $instance = BaseAuth::init( $i ); if( $instance && $instance->canManageAuth() ) { if( $userId = $instance->createUser( $pParamHash )) { $this->mUserId = $userId; break; } else { $this->mErrors = array_merge( $this->mErrors, $instance->mErrors ); return FALSE; } } } $this->load( FALSE, $pParamHash['login'] ); require_once( KERNEL_PKG_PATH.'notification_lib.php' ); $notificationlib->post_new_user_event( $pParamHash['login'] ); $this->mLogs['register'] = 'New user registered.'; $ret = TRUE; // set local time zone as default when registering $this->storePreference( 'site_display_utc', 'Local' ); if( !empty( $_REQUEST['CUSTOM'] ) ) { foreach( $_REQUEST['CUSTOM'] as $field=>$value ) { $this->storePreference( $field, $value ); } } // Handle optional user preferences that may be collected during registration if( !empty( $pParamHash['prefs'] ) ) { foreach( array_keys( $pParamHash['prefs'] ) as $key ) { $this->storePreference( $key, $pParamHash['prefs'][$key] ); } } $siteName = $gBitSystem->getConfig('site_title', $_SERVER['HTTP_HOST'] ); $gBitSmarty->assign( 'siteName',$_SERVER["SERVER_NAME"] ); $gBitSmarty->assign( 'mail_site',$_SERVER["SERVER_NAME"] ); $gBitSmarty->assign( 'mail_user',$pParamHash['login'] ); if( $gBitSystem->isFeatureActive( 'users_validate_user' ) ) { // $apass = addslashes(substr(md5($gBitSystem->genPass()),0,25)); $apass = $pParamHash['user_store']['provpass']; $foo = parse_url( $_SERVER["REQUEST_URI"] ); $foo1 = str_replace( "register", "confirm", $foo["path"] ); $machine = httpPrefix().$foo1; // Send the mail $gBitSmarty->assign( 'msg',tra( 'You will receive an email with information to login for the first time into this site' )); $gBitSmarty->assign( 'mail_machine',$machine ); $gBitSmarty->assign( 'mailUserId',$this->mUserId ); $gBitSmarty->assign( 'mailProvPass',$apass ); $mail_data = $gBitSmarty->fetch( 'bitpackage:users/user_validation_mail.tpl' ); mail( $pParamHash["email"], $siteName.' - '.tra( 'Your registration information' ), $mail_data, "From: ".$gBitSystem->getConfig('site_sender_email')."\r\nContent-type: text/plain;charset=utf-8\r\n" ); $gBitSmarty->assign( 'showmsg', 'y' ); $this->mLogs['confirm'] = 'Validation email sent.'; } elseif( $gBitSystem->isFeatureActive( 'send_welcome_email' ) ) { // Send the welcome mail $gBitSmarty->assign( 'mailPassword',$pParamHash['password'] ); $gBitSmarty->assign( 'mailEmail',$pParamHash['email'] ); $mail_data = $gBitSmarty->fetch( 'bitpackage:users/welcome_mail.tpl' ); mail( $pParamHash["email"], tra( 'Welcome to' ).' '.$siteName, $mail_data, "From: ".$gBitSystem->getConfig( 'site_sender_email' )."\r\nContent-type: text/plain;charset=utf-8\r\n" ); $this->mLogs['welcome'] = 'Welcome email sent.'; } $logHash['action_log']['title'] = $pParamHash['login']; $this->storeActionLog( $logHash ); } return( $ret ); } /** * verifyCaptcha * * @param array $pCaptcha * @access public * @return TRUE on success, FALSE on failure */ function verifyCaptcha( $pCaptcha = NULL ) { if( $this->hasPermission( 'p_users_bypass_captcha' ) || ( !empty( $_SESSION['captcha_verified'] ) && $_SESSION['captcha_verified'] === TRUE ) ) { return TRUE; } else { if( empty( $pCaptcha ) || empty( $_SESSION['captcha'] ) || $_SESSION['captcha'] != md5( $pCaptcha ) ) { return FALSE; } else { $_SESSION['captcha_verified'] = TRUE; return TRUE; } } } /** * store * * @param array $pParamHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function store( &$pParamHash ) { if( $this->verify( $pParamHash ) ) { $this->mDb->StartTrans(); $pParamHash['content_type_guid'] = BITUSER_CONTENT_TYPE_GUID; if( !empty( $pParamHash['user_store'] ) && count( $pParamHash['user_store'] ) ) { if( $this->isValid() ) { $userId = array ( "user_id" => $this->mUserId ); $result = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'], $userId ); } else { if( empty( $pParamHash['user_store']['user_id'] ) ) { $pParamHash['user_store']['user_id'] = $this->mDb->GenID( 'users_users_user_id_seq' ); } $this->mUserId = $pParamHash['user_store']['user_id']; $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'] ); } } // Prevent liberty from assuming ANONYMOUS_USER_ID while storing $pParamHash['user_id'] = $this->mUserId; // Don't let LA snarf these now so we can do extra things. $pParamHash['_files_override'] = array(); if( LibertyMime::store( $pParamHash ) ) { if( empty( $this->mInfo['content_id'] ) || ($pParamHash['content_id'] != $this->mInfo['content_id']) ) { $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `content_id`=? WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $pParamHash['content_id'], $this->mUserId ) ); $this->mInfo['content_id'] = $pParamHash['content_id']; } } $this->mDb->CompleteTrans(); $this->load( TRUE ); } return( count( $this->mErrors ) == 0 ); } /** * Imports a user record from csv file * This is a admin specific function * * @param $pParamHash an array with user data * @return TRUE if import succeed **/ function importUser( &$pParamHash ) { global $gBitUser; if( ! $gBitUser->hasPermission( 'p_users_admin' ) ) { return FALSE; } if( $this->verifyUserImport( $pParamHash ) ) { $this->mDb->StartTrans(); $pParamHash['content_type_guid'] = BITUSER_CONTENT_TYPE_GUID; if( !empty( $pParamHash['user_store'] ) && count( $pParamHash['user_store'] ) ) { // lookup and asign the default group for user $defaultGroups = BitPermUser::getDefaultGroup(); if( !empty( $defaultGroups ) ) { $pParamHash['user_store']['default_group_id'] = key( $defaultGroups ); } if( $this->isValid() ) { $userId = array ( "user_id" => $this->mUserId ); $result = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'], $userId ); } else { if( empty( $pParamHash['user_store']['user_id'] ) ) { $pParamHash['user_store']['user_id'] = $this->mDb->GenID( 'users_users_user_id_seq' ); } $this->mUserId = $pParamHash['user_store']['user_id']; $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'] ); } // make sure user is added into the default group map if( !empty( $pParamHash['user_store']['default_group_id'] ) ) { BitPermUser::addUserToGroup( $pParamHash['user_store']['user_id'],$pParamHash['user_store']['default_group_id'] ); } } // Prevent liberty from assuming ANONYMOUS_USER_ID while storing $pParamHash['user_id'] = $this->mUserId; if( LibertyContent::store( $pParamHash )) { if( empty( $this->mInfo['content_id'] ) || $pParamHash['content_id'] != $this->mInfo['content_id'] ) { $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `content_id`=? WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $pParamHash['content_id'], $this->mUserId ) ); $this->mInfo['content_id'] = $pParamHash['content_id']; } } $this->mDb->CompleteTrans(); // store any uploaded images $this->storeImages( $pParamHash ); $this->load( TRUE ); } return( count( $this->mErrors ) == 0 ); } /** * Verify and validate the data when * importing a user record from csv file * This is a admin specific function * * @param $pParamHash an array with user data * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure **/ function verifyUserImport( &$pParamHash ) { global $gBitSystem, $gBitUser; if( ! $gBitUser->hasPermission( 'p_users_admin' ) ) { return FALSE; } trim_array( $pParamHash ); // perhaps someone is importing users and *knows* what they are doing if( @$this->verifyId( $pParamHash['user_id'] ) ) { // only import user_id if it doesn't exist or overwrite is set. if( !$this->userExists( array( 'user_id' => $pParamHash['user_id'] ) ) || !empty( $_REQUEST['overwrite'] ) ) { $pParamHash['user_store']['user_id'] = $pParamHash['user_id']; } else { unset( $pParamHash['user_id'] ); } } if( !empty( $pParamHash['login'] ) ) { $ret = $this->userExists( array( 'login' => $pParamHash['login'] ) ); if( !empty( $ret ) ) { // On batch import admin can overwrite existing user, so don't error if set // however, prevent overwrite of a mix of user records if( !empty( $_REQUEST['overwrite'] ) && (!isset($pParamHash['user_store']['user_id']) || $pParamHash['user_store']['user_id'] == $ret ) ) { $pParamHash['user_id'] = $ret; $pParamHash['user_store']['user_id'] = $pParamHash['user_id']; } else { $this->mErrors['login'] = 'The username "'.$pParamHash['login'].'" is already in use'; } } elseif( preg_match( '/[^A-Za-z0-9_.-]/', $pParamHash["login"] ) ) { $this->mErrors['login'] = tra( "Your username can only contain numbers, characters, underscores and hyphens." ); } if( !isset($this->mErrors['login']) ) { // LOWER CASE all logins $pParamHash['login'] = strtolower($pParamHash['login']); $pParamHash['user_store']['login'] = $pParamHash['login']; } } else { $this->mErrors['login'] = 'Value for username is missing'; } if( !empty( $pParamHash['real_name'] ) ) { $pParamHash['user_store']['real_name'] = substr( $pParamHash['real_name'], 0, 64 ); } if( !empty( $pParamHash['email'] ) ) { // LOWER CASE all emails admin_verify_email $pParamHash['email'] = strtolower( $pParamHash['email'] ); if( validate_email_syntax( $pParamHash['email'] ) ) { $ret = $this->userExists( array( 'email' => $pParamHash['email'] ) ); if( !empty($ret) ) { if( !empty( $_REQUEST['overwrite'] ) && (!isset($pParamHash['user_store']['user_id']) || $pParamHash['user_store']['user_id'] == $ret ) ) { $pParamHash['user_id'] = $ret; $pParamHash['user_store']['user_id'] = $pParamHash['user_id']; } else { $this->mErrors['email'] = 'The email address "'.$pParamHash['email'].'" has already been registered.'; } } if( !empty( $_REQUEST['admin_verify_email'] ) ) { if( !$this->verifyMX( $pParamHash['email'] ) ) { $this->mErrors['email'] = 'Cannot find a valid MX host'; } } if( !isset($this->mErrors['email']) ) { $pParamHash['user_store']['email'] = strtolower( substr( $pParamHash['email'], 0, 200 ) ); } } else { $this->mErrors['email'] = 'The email address "'.$pParamHash['email'].'" has an invalid syntax.'; } } else { $this->mErrors['email'] = tra( 'You must enter your email address' ); } // check some new user requirements if( !$this->isRegistered() ) { if( isset($pParamHash['user_store']['user_id']) && !empty( $_REQUEST['overwrite'] ) ) { $this->mUserId = $this->userExists( array( 'user_id' => $pParamHash['user_store']['user_id'] ) ); } if( empty( $pParamHash['registration_date'] ) ) { $pParamHash['registration_date'] = date( "U" ); } $pParamHash['user_store']['registration_date'] = $pParamHash['registration_date']; if( !empty($pParamHash['hash'] ) ) { unset( $pParamHash['password'] ); if($gBitSystem->isFeatureActive( 'users_clear_passwords' ) ) { $this->mErrors['password'] = tra( 'You cannot import a password hash when setting to stor password in plan text is set.' ); } elseif( strlen( $pParamHash['hash'] ) <> 32 ) { $this->mErrors['password'] = tra( 'When importing a MD5 password hash it needto have a length of 32 bytes.' ); } } else { if( !empty( $_REQUEST['admin_verify_user'] ) ) { $pParamHash['user_store']['provpass'] = md5(BitSystem::genPass()); $pParamHash['user_store']['hash'] = ''; $pParamHash['pass_due'] = 0; unset( $pParamHash['password'] ); } elseif( empty($pParamHash['password'] ) ) { $pParamHash['password'] = $gBitSystem->genPass(); } } } elseif( $this->isValid() ) { // Prevent loosing user info on save if( empty( $pParamHash['edit'] ) ) { $pParamHash['edit'] = $this->mInfo['data']; } } if( isset( $pParamHash['password'] ) ) { if (!$this->isValid() || isset($pParamHash['password']) ) { $passswordError = $this->verifyPasswordFormat( $pParamHash['password'] ); } if( !empty( $passswordError ) ) { $this->mErrors['password'] = $passswordError; } else { // Generate a unique hash //$pParamHash['user_store']['hash'] = md5( strtolower( (!empty($pParamHash['login'])?$pParamHash['login']:'') ).$pPassword.$pParamHash['email'] ); $pParamHash['user_store']['hash'] = md5( $pParamHash['password'] ); $now = $gBitSystem->getUTCTime(); if( !isset( $pParamHash['pass_due'] ) && $gBitSystem->getConfig('users_pass_due') ) { $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $gBitSystem->getConfig('users_pass_due') ); } elseif( isset( $pParamHash['pass_due'] ) ) { // renew password only next half year ;) $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $pParamHash['pass_due']); } if( $gBitSystem->isFeatureActive( 'users_clear_passwords' ) ) { $pParamHash['user_store']['user_password'] = $pParamHash['password']; } else { $pParamHash['user_store']['user_password'] = ''; } } } elseif( !empty( $pParamHash['hash'] )) { $pParamHash['user_store']['hash'] = $pParamHash['hash']; $now = $gBitSystem->getUTCTime(); if( !isset( $pParamHash['pass_due'] ) && $gBitSystem->getConfig( 'users_pass_due' )) { $pParamHash['user_store']['pass_due'] = $now + ( 60 * 60 * 24 * $gBitSystem->getConfig( 'users_pass_due' )); } elseif( isset( $pParamHash['pass_due'] ) ) { // renew password only next half year ;) $pParamHash['user_store']['pass_due'] = $now + ( 60 * 60 * 24 * $pParamHash['pass_due'] ); } } return ( count( $this->mErrors ) == 0 ); } /** * expunge removes user and associated private data * * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function expunge( $pExpungeContent = NULL ) { global $gBitSystem; $this->mDb->StartTrans(); if( !empty( $pExpungeContent ) ) { if( $pExpungeContent == 'all' ) { if( $userContent = $this->mDb->getAssoc( "SELECT content_id, content_type_guid FROM `".BIT_DB_PREFIX."liberty_content` WHERE `user_id`=? AND `content_type_guid` != 'bituser'", array( $this->mUserId ) ) ) { foreach( $userContent as $contentId=>$contentTypeGuid ) { if( $delContent = LibertyBase::getLibertyObject( $contentId, $contentTypeGuid ) ) { $delContent->expunge(); } } } } } if( $this->mUserId != ANONYMOUS_USER_ID ) { $this->purgeImage( 'avatar' ); $this->purgeImage( 'portrait' ); $this->purgeImage( 'logo' ); $this->invokeServices( 'users_expunge_function' ); $userTables = array( 'users_cnxn', 'users_watches', 'users_favorites_map', 'users_users', ); foreach( $userTables as $table ) { $query = "DELETE FROM `".BIT_DB_PREFIX.$table."` WHERE `user_id` = ?"; $result = $this->mDb->query( $query, array( $this->mUserId ) ); } parent::expunge(); $logHash['action_log']['title'] = $this->mInfo['login']; $this->mLogs['user_del'] = 'User deleted'; $this->storeActionLog( $logHash ); $this->mDb->CompleteTrans(); return TRUE; } else { $this->mDb->RollbackTrans(); $gBitSystem->fatalError( tra( 'The anonymous user cannot be deleted' ) ); } } // {{{ ==================== Sessions and logging in and out methods ==================== /** * updateSession * * @param array $pSessionId * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function updateSession( $pSessionId ) { global $gLightWeightScan; if ( !$this->isDatabaseValid() ) return true; global $gBitSystem, $gBitUser; $update['last_get'] = $gBitSystem->getUTCTime(); $update['current_view'] = $_SERVER['PHP_SELF']; if( empty( $gLightWeightScan ) ) { $this->mDb->StartTrans(); $row = $this->mDb->getRow( "SELECT `last_get`, `connect_time`, `get_count`, `user_agent`, `current_view` FROM `".BIT_DB_PREFIX."users_cnxn` WHERE `cookie`=? ", array( $pSessionId ) ); if( $gBitUser->isRegistered() ) { $update['user_id'] = $gBitUser->mUserId; } if( $row ) { if( empty( $row['ip'] ) || $row['ip'] != $_SERVER['REMOTE_ADDR'] ) { $update['ip'] = $_SERVER['REMOTE_ADDR']; } if( !empty( $_SERVER['HTTP_USER_AGENT'] ) && (empty( $row['user_agent'] ) || $row['user_agent'] != $_SERVER['HTTP_USER_AGENT']) ) { $update['user_agent'] = substr( $_SERVER['HTTP_USER_AGENT'], 0, 128 ); } $update['get_count'] = $row['get_count'] + 1; $ret = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_cnxn', $update, array( 'cookie' => $pSessionId ) ); } else { if( $this->isRegistered() ) { $update['user_id'] = $this->mUserId; $update['ip'] = $_SERVER['REMOTE_ADDR']; $update['user_agent'] = (string)substr( $_SERVER['HTTP_USER_AGENT'], 0, 128 ); $update['get_count'] = 1; $update['connect_time'] = $update['last_get']; $update['cookie'] = $pSessionId; $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_cnxn', $update ); } } // Delete old connections nightly during the hour of 3 am // This needs moving to an event that is known to happen if( date( 'H' ) == '03' && date( 'i' ) > 0 && date( 'i' ) < 2 ) { // Default to 30 days history $oldy = $update['last_get'] - ($gBitSystem->getConfig( 'users_cnxn_history_days', 30 ) * 24 * 60 * 60); $query = "DELETE from `".BIT_DB_PREFIX."users_cnxn` where `connect_time` < ?"; $result = $this->mDb->query($query, array($oldy)); } $this->mDb->CompleteTrans(); } return true; } /** * countSessions * * @param array $pActive * @access public * @return count of sessions */ function countSessions( $pActive = FALSE ) { $query = "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_cnxn`"; if( $pActive ) { $query .=" WHERE `cookie` IS NOT NULL"; } return $this->mDb->getOne( $query,array() ); } /** * logout * * @access public * @return void */ function logout() { global $user_cookie_site, $gBitSystem; if( !empty( $_COOKIE[$user_cookie_site] ) ) { $this->mDb->query( "UPDATE `".BIT_DB_PREFIX."users_cnxn` SET `cookie`=NULL WHERE `cookie`=?", array( $_COOKIE[$user_cookie_site] ) ); } $cookie_time = time() - 3600; $cookie_path = BIT_ROOT_URL; $cookie_domain = ""; // Now if the remember me feature is on and the user checked the user_remember_me checkbox then ... if( $gBitSystem->isFeatureActive( 'users_remember_me' ) && isset( $_REQUEST['rme'] ) && $_REQUEST['rme'] == 'on' ) { $cookie_time = ( int )( time() + $gBitSystem->getConfig( 'users_remember_time', 86400 )); $cookie_path = $gBitSystem->getConfig( 'cookie_path', $cookie_path ); $cookie_domain = $gBitSystem->getConfig( 'cookie_domain', $cookie_domain ); } setcookie( $user_cookie_site, '', $cookie_time , $cookie_path, $cookie_domain ); //session_unregister ('user'); session_destroy(); $this->mUserId = NULL; // ensure Guest default page is loaded if required $this->mInfo['default_group_id'] = -1; } /** * verifyTicket * * @param array $pFatalOnError * @param array $pForceCheck * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function verifyTicket( $pFatalOnError=TRUE, $pForceCheck=TRUE ) { global $gBitSystem, $gBitUser; $ret = FALSE; if( $pForceCheck == TRUE || !empty( $_REQUEST['tk'] ) ) { if( empty( $_REQUEST['tk'] ) || (!($ret = $_REQUEST['tk'] == $this->mTicket ) && $pFatalOnError) ) { $userString = $gBitUser->isRegistered() ? "\nUSER ID: ".$gBitUser->mUserId.' ( '.$gBitUser->getField( 'email' ).' ) ' : ''; @error_log( tra( "Security Violation" )."$userString ".$_SERVER['REMOTE_ADDR']."\nURI: $_SERVER[REQUEST_URI] \nREFERER: $_SERVER[HTTP_REFERER] " ); $gBitSystem->fatalError( tra( "Security Violation" )); } } return $ret; } // }}} // {{{ ==================== Banning ==================== /** * ban sets the user account status to -201 suspended * * @access public * @return TRUE on success, Display error message on failure */ function ban(){ global $gBitSystem; if( $this->mUserId == ANONYMOUS_USER_ID || $this->mUserId == ROOT_USER_ID || $this->isAdmin()) { $gBitSystem->fatalError( tra( 'You cannot ban the user' )." ".$this->mInfo['login'] ); } else { $this->storeStatus( -201 ); return TRUE; } } /** * ban unban the user * * @access public * @return TRUE on success */ function unban(){ global $gBitSystem; $this->storeStatus( 50 ); return TRUE; } // }}} /** * genPass generate random password * * @param array $pLength Length of final password * @access public * @return password */ function genPass( $pLength=NULL ) { global $gBitSystem; $vocales = "AaEeIiOoUu13580"; $consonantes = "BbCcDdFfGgHhJjKkLlMmNnPpQqRrSsTtVvWwXxYyZz24679"; $ret = ''; if( empty( $pLength ) || !is_numeric( $pLength ) ) { $pLength = $gBitSystem->getConfig( 'users_min_pass_length', 4 ); } for( $i = 0; $i < $pLength; $i++ ) { if( $i % 2 ) { $ret .= $vocales{rand( 0, strlen( $vocales ) - 1 )}; } else { $ret .= $consonantes{rand( 0, strlen( $consonantes ) - 1 )}; } } return $ret; } /** * generateChallenge * * @access public * @return md5 string */ function generateChallenge() { return( md5( BitSystem::genPass() )); } /** * login * * @param array $pLogin * @param array $pPassword * @param array $pChallenge * @param array $pResponse * @access public * @return URL the user should be sent to after login */ function login( $pLogin, $pPassword, $pChallenge=NULL, $pResponse=NULL ) { global $gBitSystem, $user_cookie_site; $isvalid = false; // Make sure cookies are enabled if ( !isset( $_COOKIE[$user_cookie_site] )) { $this->mErrors['login'] = tra( 'No cookie found. Please enable cookies and try again.' ); $url = USERS_PKG_URL.'login.php?error=' . urlencode( $this->mErrors['login'] ); return( $url ); } // Verify user is valid if( $this->validate( $pLogin, $pPassword, $pChallenge, $pResponse )) { $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login'; $userInfo = $this->getUserInfo( array( $loginCol => $pLogin )); // If the password is valid but it is due then force the user to change the password by // sending the user to the new password change screen without letting him use // The user must re-nter the old password so no secutiry risk here if( $this->isPasswordDue() ) { // Redirect the user to the screen where he must change his password. // Note that the user is not logged in he's just validated to change his password // The user must re-enter his old password so no secutiry risk involved $url = USERS_PKG_URL.'change_password.php?user_id='.$userInfo['user_id']; } elseif( $userInfo['user_id'] != ANONYMOUS_USER_ID ) { // User is valid and not due to change pass.. $this->mUserId = $userInfo['user_id']; $this->load(); $this->loadPermissions( TRUE ); $url = isset($_SESSION['loginfrom']) ? $_SESSION['loginfrom'] : $gBitSystem->getDefaultPage(); unset( $_SESSION['loginfrom'] ); $userInfo['cookie'] = md5( time().$userInfo['email'] ); $cookie_time = 0; $cookie_path = BIT_ROOT_URL; $cookie_domain = ""; // Now if the remember me feature is on and the user checked the user_remember_me checkbox then ... if( $gBitSystem->isFeatureActive( 'users_remember_me' ) && isset( $_REQUEST['rme'] ) && $_REQUEST['rme'] == 'on' ) { $cookie_time = ( int )( time() + $gBitSystem->getConfig( 'users_remember_time', 86400 )); $cookie_path = $gBitSystem->getConfig( 'cookie_path', $cookie_path ); $cookie_domain = $gBitSystem->getConfig( 'cookie_domain', $cookie_domain ); } $session_id = session_id(); setcookie( $user_cookie_site, $session_id, $cookie_time, $cookie_path, $cookie_domain ); $this->updateSession( $session_id ); } } else { $this->mUserId = ANONYMOUS_USER_ID; unset( $this->mInfo ); $this->mErrors['login'] = tra( 'Invalid username or password' ); $url = USERS_PKG_URL.'login.php?error=' . urlencode( $this->mErrors['login'] ); } // check for HTTPS mode and redirect back to non-ssl when not requested, or a SSL login was forced if( isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on' ) { $refererSsl = isset( $_SERVER['HTTP_REFERER'] ) && substr( $_SERVER['HTTP_REFERER'], 0, 5 ) == 'https'; if( ($gBitSystem->getConfig( 'site_https_login_required' ) && !$refererSsl) || ($refererSsl && empty( $_REQUEST['stay_in_ssl_mode'] )) ) { // start setting up the URL redirect without SSL $prefix = 'http://' . $gBitSystem->getConfig( 'site_http_domain', $_SERVER['HTTP_HOST'] ); // add port to prefix if needed $port = $gBitSystem->getConfig( 'site_http_port', 80 ); if( $port != 80 ) { $prefix .= ':'.$port; } $prefix .= $gBitSystem->getConfig( 'site_http_prefix', BIT_ROOT_URL ); if( strrpos( $prefix, '/' ) == (strlen( $prefix ) - 1) ) { $prefix = substr( $prefix, 0, strlen( $prefix ) - 1 ); } // join prefix and URL $url = $prefix.$url; } } return( $url ); } /** * validate * * @param array $pUser * @param array $pPass * @param array $pChallenge * @param array $pResponse * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure * @todo rewrite this mess. this is horrible stuff. - xing - Thursday Oct 16, 2008 09:47:20 CEST */ function validate( $pUser, $pPass, $pChallenge, $pResponse ) { global $gBitSystem; // these will help us keep tabs of what is going on $authValid = $authPresent = FALSE; $createAuth = ( $gBitSystem->getConfig( "users_create_user_auth", "n" ) == "y" ); for( $i = 0; $i < BaseAuth::getAuthMethodCount(); $i++ ) { $instance = BaseAuth::init( $i ); if( $instance ) { $result = $instance->validate( $pUser, $pPass, $pChallenge, $pResponse ); switch( $result ) { case USER_VALID: unset($this->mErrors['login']); $authPresent = TRUE; $authValid = TRUE; break; case PASSWORD_INCORRECT: // this mErrors assignment is CRUCIAL so that bit auth fails properly. DO NOT FUCK WITH THIS unless you know what you are doing and have checked with me first. XOXOX - spiderr // This might have broken other auth, but at this point, bw auth was TOTALLY busted. If you need to fix, please come find me. $this->mErrors['login'] = 'Password incorrect'; $authPresent = TRUE; break; case USER_NOT_FOUND: break; } if( $authPresent ) { if( empty( $instance->mInfo['email'] )) { $instance->mInfo['email'] = $pUser; } //If we're given a user_id then the user is already in the database: if( !empty( $instance->mInfo['user_id'] )) { $this->mUserId = $instance->mInfo['user_id']; //Is the user already in the database: } elseif( $this->mDb->getOne( "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_users` WHERE `login` = ?", array( $instance->mLogin )) > 0 ) { // Update Details $authUserInfo = array( 'login' => $instance->mInfo['login'], 'password' => $instance->mInfo['password'], 'real_name' => $instance->mInfo['real_name'], 'email' => $instance->mInfo['email'] ); $userInfo = $this->getUserInfo( array( 'login' => $pUser )); $this->mUserId = $userInfo['user_id']; $this->store( $authUserInfo ); $this->mErrors = array(); } else { $authUserInfo = array( 'login' => $instance->mInfo['login'], 'password' => $instance->mInfo['password'], 'real_name' => $instance->mInfo['real_name'], 'email' => $instance->mInfo['email'] ); // TODO somehow, mUserId gets set to -1 at this point - no idea how // set to NULL to prevent overwriting Guest user - wolff_borg $this->mUserId = NULL; $this->store( $authUserInfo ); } if( $createAuth && $i > 0 ) { // if the user was logged into this system and we should progate users down other auth methods for( $j = $i; $i >= 0; $j-- ) { $probMethodName = $gBitSystem->getConfig( "users_auth_method_$j", $default ); if( !empty( $probMethodName )) { $probInstance = BaseAuth::init( $probMethodName ); if( $probInstance && $probInstance->canManageAuth() ) { $result = $probInstance->validate( $pUser, $pPass, $pChallenge, $pResponse ); if( $result == USER_VALID || $result == PASSWORD_INCORRECT ) { // see if we can create a new account $userattr = $instance->getUserData(); if( empty( $userattr['login'] )) { $userattr['login'] = $pUser; } if( empty( $userattr['password'] )) { $userattr['password'] = $pPass; } $probInstance->createUser( $userattr ); } } $this->mErrors = array_merge( $this->mErrors, $probInstance->mErrors ); } } } $this->mAuth = $instance; break; } $this->mErrors = array_merge( $this->mErrors,$instance->mErrors ); } } if( $this->mUserId != ANONYMOUS_USER_ID ) { $this->load(); //on first time login we run the users registation service if( $this->mInfo['last_login'] == NULL ) { $this->invokeServices( 'users_register_function' ); } $this->updateLastLogin( $this->mUserId ); } return( count( $this->mErrors ) == 0 ); } /** * updateLastLogin * * @param array $pUserId * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function updateLastLogin( $pUserId ) { $ret = FALSE; if( @$this->verifyId( $pUserId ) ) { global $gBitSystem; $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `last_login` = `current_login`, `current_login` = ? WHERE `user_id` = ?"; $result = $this->mDb->query( $query, array( $gBitSystem->getUTCTime(), $pUserId )); $ret = TRUE; } return $ret; } /** * confirmRegistration * * @param array $pUserId * @param array $pProvpass * @access public * @return registered user, empty array on failure */ function confirmRegistration( $pUserId, $pProvpass ) { global $gBitSystem; $query = " SELECT `user_id`, `provpass`, `user_password`, `login`, `email` FROM `".BIT_DB_PREFIX."users_users` WHERE `user_id`=? AND `provpass`=? AND ( `provpass_expires` IS NULL OR `provpass_expires` > ?)"; return( $this->mDb->getRow( $query, array( $pUserId, $pProvpass, $gBitSystem->getUTCTime() ))); } /** * changeUserEmail * * @param array $pUserId * @param array $pEmail * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function changeUserEmail( $pUserId, $pEmail ) { if( $this->userExists( array( 'email' => $pEmail ))) { $this->mErrors['duplicate_mail'] = tra( "The email address you selected already exists." ); } else { $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `email`=? WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $pEmail, $pUserId ) ); $query = "UPDATE `".BIT_DB_PREFIX."users_watches` SET `email`=? WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $pEmail, $pUserId ) ); // update value in hash $this->mInfo['email'] = $_REQUEST['email']; } return( count( $this->mErrors ) == 0 ); } /** * lookupHomepage * * @param array $iHomepage * @access public * @return user_id that can be used to point to users homepage */ function lookupHomepage( $iHomepage ) { $ret = NULL; if( @$this->verifyId( $iHomepage )) { // iHomepage is the user_id for the user... $key = 'user_id'; // force to proper integer to get things like "007." to properly query $iHomepage = (integer)$iHomepage; } elseif( substr( $iHomepage, 0, 7 ) == 'mailto:' ) { // iHomepage is the email address of the user... $key = 'email'; } else { // iHomepage is the 'login' of the user... $key = 'login'; } $tmpUser = $this->getUserInfo( array( $key => $iHomepage )); if( @$this->verifyId( $tmpUser['user_id'] )) { $ret = $tmpUser['user_id']; } return $ret; } /** * getUserPreference * * @param array $pPrefName * @param array $pPrefDefault * @param array $pUserId * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getUserPreference( $pPrefName, $pPrefDefault, $pUserId ) { // Alternate to LibertyContent::getPreference when all you have is a user_id and a pref_name, and you need a value... global $gBitDb; $ret = NULL; if( BitBase::verifyId( $pUserId ) ) { $query = " SELECT lcp.`pref_value` FROM `".BIT_DB_PREFIX."liberty_content_prefs` lcp INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON (lcp.`content_id`=uu.`content_id`) WHERE uu.`user_id` = ? AND lcp.`pref_name` = ?"; if( !$ret = $gBitDb->getOne( $query, array( $pUserId, $pPrefName ))) { $ret = $pPrefDefault; } } return $ret; } /** * getUserInfo will fetch the user info of a given user * * @param array $pUserMixed hash key can be any column in users_users table e.g.: 'login', 'user_id', 'email', 'content_id' * @access public * @return user info on success, NULL on failure */ function getUserInfo( $pUserMixed ) { $ret = NULL; if( is_array( $pUserMixed ) ) { $val = current( $pUserMixed ); if( !is_numeric( $val ) ) { $col = "UPPER( uu.`".key( $pUserMixed )."` ) "; $val = strtoupper( $val ); } else { $col = " uu.`".key( $pUserMixed )."` "; if( $val > 0xFFFFFFFF ) { // 32 bit overflow, set to zero to avoid fatal error in databases with 32 bit integer columns $val = 0; } } $query = "SELECT uu.* FROM `".BIT_DB_PREFIX."users_users` uu LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (lc.`content_id`=uu.`content_id`) WHERE $col = ?"; $ret = $this->mDb->getRow( $query, array( $val )); } return $ret; } /** * getByHash get user from cookie hash * * @param array $pHash * @access public * @return user info */ function getUserIdFromCookieHash( $pHash ) { $query = "SELECT `user_id` FROM `".BIT_DB_PREFIX."users_cnxn` WHERE `cookie` = ?"; return $this->mDb->getOne( $query, array( $pHash )); } /** * isPasswordDue work out if a user has to change their password * * @access public * @return TRUE when the password is due, FALSE if it isn't, NULL when no due time is set * @note NULL password due means *no* expiration */ function isPasswordDue() { global $gBitSystem; $ret = FALSE; if( $this->isRegistered() ) { // get user_id to avoid NULL and zero confusion $query = " SELECT `user_id`, `pass_due` FROM `".BIT_DB_PREFIX."users_users` WHERE `pass_due` IS NOT NULL AND `user_id`=? "; $due = $this->mDb->getAssoc( $query, array( $this->mUserId ) ); if( @$this->verifyId( $due['user_id'] ) ) { $ret = $due['pass_due'] <= $gBitSystem->getUTCTime(); } } return $ret; } /** * createTempPassword * * @param array $pLogin * @param array $pPass * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function createTempPassword( $pLogin, $pPass ) { global $gBitSystem; $ret = array( '', '' ); if( empty( $pLogin ) ) { $pLogin = $this->getField( 'email' ); } if( !empty( $pLogin )) { $pass = BitSystem::genPass(); $provpass = md5( $pass ); $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login'; #temp passwords good for 3 days -- prob should be an config option $passDue = $gBitSystem->getUTCTime() + ( 60 * 60 * 24 * 3 ); $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `provpass` = ?, `provpass_expires` = ? WHERE `".$loginCol."` = ?"; $result = $this->mDb->query( $query, array( $provpass, $passDue, $pLogin )); $ret = array( $pass, $provpass ); } return $ret; } /** * storePassword * * @param array $pPass * @param array $pLogin * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storePassword( $pPass, $pLogin=NULL ) { global $gBitSystem; $ret = FALSE; if( empty( $pLogin ) ) { $pLogin = $this->getField( 'email' ); } if( !empty( $pLogin )) { $ret = TRUE; $hash = md5( $pPass ); $now = $gBitSystem->getUTCTime();; $passDue = $now + ( 60 * 60 * 24 * $gBitSystem->getConfig( 'users_pass_due' )); if( !$gBitSystem->isFeatureActive( 'users_clear_passwords' )) { $pPass = NULL; } $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login'; $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `provpass`= NULL, `provpass_expires` = NULL,`hash`=? ,`user_password`=? ,`pass_due`=? WHERE `".$loginCol."`=?"; $result = $this->mDb->query( $query, array( $hash, $pPass, $passDue, $pLogin )); } return $ret; } /** * getUserActivity * * @param array $pListHash * @access public * @return array of users and what they have been up to */ function getUserActivity( &$pListHash ) { $bindVars = array(); if( empty( $pListHash['sort_mode'] ) ) { $pListHash['sort_mode'] = 'last_get_desc'; } LibertyContent::prepGetList( $pListHash ); $whereSql = ''; if( !empty( $pListHash['last_get'] ) ) { $whereSql .= ' AND uc.`last_get` > ? '; $bindVars[] = time() - $pListHash['last_get']; } if( @BitBase::verifyId( $pListHash['user_id'] ) ) { $whereSql .= ' AND uc.`user_id` = ? '; $bindVars[] = $pListHash['user_id']; } if( !empty( $pListHash['ip'] ) ) { $whereSql .= ' AND uc.`ip` = ? '; $bindVars[] = $pListHash['ip']; } if( !empty( $pListHash['online'] ) ) { $whereSql .= ' AND uc.`cookie` IS NOT NULL '; } $query = " SELECT DISTINCT uc.`user_id`, `login`, `real_name`, `connect_time`, `ip`, `user_agent`, `last_get`, uu.`content_id` FROM `".BIT_DB_PREFIX."users_cnxn` uc INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON (uc.`user_id` = uu.`user_id`) WHERE uc.`user_id` IS NOT NULL $whereSql ORDER BY ".$this->mDb->convertSortmode( $pListHash['sort_mode'] ); $result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] ); $ret = array(); while( $res = $result->fetchRow() ) { $res['users_information'] = $this->getPreference( 'users_information', 'public', $res['content_id'] ); $ret[] = $res; } $countSql = " SELECT COUNT( uc.`user_id` ) FROM `".BIT_DB_PREFIX."users_cnxn` uc INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON (uc.`user_id` = uu.`user_id`) WHERE uc.`user_id` IS NOT NULL $whereSql"; $pListHash['cant'] = $this->mDb->GetOne( $countSql, $bindVars ); $this->postGetList( $pListHash ); return $ret; } /** * getUserDomain * * @param array $pLogin * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getUserDomain( $pLogin ) { $ret = array(); if( $pLogin == $this->getField( 'login' ) && $this->getPreference( 'domain_style' ) ) { $ret = $this->mInfo; $ret['style'] = $this->getPreference( 'domain_style' ); } else { $sql = " SELECT uu.*, lcp.`pref_value` AS `style` FROM `".BIT_DB_PREFIX."users_users` uu INNER JOIN `".BIT_DB_PREFIX."liberty_content_prefs` lcp ON( uu.`content_id` = lcp.`content_id` ) WHERE uu.`login` = ? AND lcp.`pref_name` = ?"; $ret = $this->mDb->getRow( $sql, array( $pLogin, 'domain_style' ) ); } return( $ret ); } /** * getDomain * * @param array $pContentId * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getDomain( $pContentId ) { $ret = array(); if( $this->verifyId( $pContentId ) ) { $ret['content_id'] = $pContentId; $ret['style'] = $this->mDb->getOne( "SELECT `pref_value` FROM `".BIT_DB_PREFIX."liberty_content_prefs` WHERE `content_id`=? AND `pref_name`=?", array( $pContentId, 'domain_style' )); } return( $ret ); } /** * canCustomizeTheme check if a user can customise their theme * * @access public * @return TRUE on success, FALSE on failure */ function canCustomizeTheme() { global $gBitSystem; return( $this->hasPermission( 'p_tidbits_custom_home_theme' ) || $gBitSystem->getConfig( 'users_themes' ) == 'y' || $gBitSystem->getConfig( 'users_themes' ) == 'h' || $gBitSystem->getConfig( 'users_themes' ) == 'u' ); } /** * canCustomizeLayout check if a user can customise their layout * * @access public * @return TRUE on success, FALSE on failure */ function canCustomizeLayout() { global $gBitSystem; return( $this->hasPermission( 'p_tidbits_custom_home_layout' ) || $gBitSystem->getConfig( 'users_layouts' ) == 'y' || $gBitSystem->getConfig( 'users_layouts' ) == 'h' || $gBitSystem->getConfig( 'users_layouts' ) == 'u' ); } // {{{ ==================== image and file functions ==================== /** * getThumbnailUrl * * @param string $pSize * @param array $pInfoHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getThumbnailUrl( $pSize = 'small', $pInfoHash = NULL ) { $ret = ''; if( $pInfoHash ) { // do some stuff if we are passed a hash-o-crap, not implemented currently } elseif( $this->isValid() ) { $ret = $this->getField( 'avatar_url' ); } return $ret; } /** * storeImages will store any user images - please note that uploaded files have to be in predefined keys in $_FILES * $_FILES['user_portrait_file'] * $_FILES['user_auto_avatar'] * $_FILES['user_logo_file'] * * @param array $pParamHash array of options * @param boolean $pParamHash['user_auto_avatar'] automatically create avatar from portrait * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storeImages( $pParamHash ) { if( isset( $_FILES['user_portrait_file'] ) && is_uploaded_file( $_FILES['user_portrait_file']['tmp_name'] ) && $_FILES['user_portrait_file']['size'] > 0 ) { $portraitHash = $pParamHash; $portraitHash['user_id'] = $this->mUserId; $portraitHash['content_id'] = $this->mContentId; $portraitHash['upload'] = $_FILES['user_portrait_file']; $portraitHash['upload']['source_file'] = $_FILES['user_portrait_file']['tmp_name']; $this->storePortrait( $portraitHash, !empty( $portraitHash['user_auto_avatar'] )); } if( isset( $_FILES['user_avatar_file'] ) && is_uploaded_file( $_FILES['user_avatar_file']['tmp_name'] ) && $_FILES['user_avatar_file']['size'] > 0 ) { $avatarHash = $pParamHash; $avatarHash['user_id'] = $this->mUserId; $avatarHash['content_id'] = $this->mContentId; $avatarHash['upload'] = $_FILES['user_avatar_file']; $avatarHash['upload']['source_file'] = $_FILES['user_avatar_file']['tmp_name']; $this->storeAvatar( $avatarHash ); } if( isset( $_FILES['user_logo_file'] ) && is_uploaded_file( $_FILES['user_logo_file']['tmp_name'] ) && $_FILES['user_logo_file']['size'] > 0 ) { $logoHash = $pParamHash; $logoHash['user_id'] = $this->mUserId; $logoHash['content_id'] = $this->mContentId; $logoHash['upload'] = $_FILES['user_logo_file']; $logoHash['upload']['source_file'] = $_FILES['user_logo_file']['tmp_name']; $this->storeLogo( $logoHash ); } } /** * storePortrait * * @param array $pStorageHash * @param array $pGenerateAvatar * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storePortrait( &$pStorageHash, $pGenerateAvatar = FALSE ) { if( $this->isValid() && count( $pStorageHash )) { // make a copy before the uploaded file disappears if( $pGenerateAvatar ) { $avatarHash = $pStorageHash; $avatarHash['upload']['tmp_name'] = $pStorageHash['upload']['tmp_name'].'.av'; copy( $pStorageHash['upload']['tmp_name'], $pStorageHash['upload']['tmp_name'].'.av' ); } if( $this->storeUserImage( $pStorageHash, 'portrait' ) && $pGenerateAvatar ) { $this->storeAvatar( $avatarHash ); // nuke copy of image @unlink( $pStorageHash['upload']['tmp_name'].'.av' ); } } return( count( $this->mErrors ) == 0 ); } /** * storeAvatar * * @param array $pStorageHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storeAvatar( &$pStorageHash ) { return( $this->storeUserImage( $pStorageHash, 'avatar' )); } /** * storeLogo * * @param array $pStorageHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storeLogo( &$pStorageHash ) { return( $this->storeUserImage( $pStorageHash, 'logo' )); } /** * storeUserImage * * @param array $pStorageHash * @param string $pType * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function storeUserImage( &$pStorageHash, $pType = 'portrait' ) { if( $this->isValid() && count( $pStorageHash ) ) { // don't do the content thing $pStorageHash['skip_content_store'] = TRUE; // setup the hash for central storage functions $pStorageHash['no_perm_check'] = TRUE; $pStorageHash['_files_override'][$pType] = $pStorageHash['upload']; $pStorageHash['_files_override'][$pType]['max_width'] = constant( strtoupper( $pType )."_MAX_DIM" ); $pStorageHash['_files_override'][$pType]['max_height'] = constant( strtoupper( $pType )."_MAX_DIM" ); $pStorageHash['_files_override'][$pType]['attachment_id'] = !empty( $this->mInfo["{$pType}_attachment_id"] ) ? $this->mInfo["{$pType}_attachment_id"] : NULL; $pStorageHash['_files_override'][$pType]['user_id'] = $this->mUserId; if( LibertyMime::store( $pStorageHash )) { $file = $pStorageHash['upload_store']['files'][$pType]; if( empty( $this->mInfo["{$pType}_attachment_id"] ) || $this->mInfo["{$pType}_attachment_id"] != $file['attachment_id'] ) { $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `{$pType}_attachment_id` = ? WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $file['attachment_id'], $this->mUserId ) ); $this->mInfo["{$pType}_attachment_id"] = $file['attachment_id']; $pStorageHash["{$pType}_storage_path"] = $file['upload']['dest_path']; } } else { $this->mErrors["{$pType}_file"] = 'File '.$pStorageHash['upload_store']['files'][$pType]['name'].' could not be stored.'; } } return( count( $this->mErrors ) == 0 ); } /** * purgeImage * * @param array $pType * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function purgeImage( $pType ) { if( $this->isValid() && @$this->verifyId( $this->mInfo[$pType.'_attachment_id'] ) ) { $this->mDb->StartTrans(); $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `".$pType."_attachment_id` = NULL WHERE `user_id`=?"; $result = $this->mDb->query( $query, array( $this->mUserId ) ); if( $this->expungeAttachment( $this->getField( $pType.'_attachment_id' ) ) ) { unset( $this->mInfo[$pType.'_storage_path'] ); unset( $this->mInfo[$pType.'_attachment_id'] ); unset( $this->mInfo[$pType.'_url'] ); } $this->mDb->CompleteTrans(); return TRUE; } } /** * purgePortrait * * @access public * @return TRUE on success, FALSE on failure */ function purgePortrait() { return $this->purgeImage( 'portrait' ); } /** * purgeAvatar * * @access public * @return TRUE on success, FALSE on failure */ function purgeAvatar() { return $this->purgeImage( 'avatar' ); } /** * purgeLogo * * @access public * @return TRUE on success, FALSE on failure */ function purgeLogo() { return $this->purgeImage( 'logo' ); } // }}} // {{{ ==================== Watches ==================== // TODO: clean up all watch functions. these are old and messy. - xing - Thursday Oct 16, 2008 11:07:55 CEST /** * storeWatch * * @param array $pEvent * @param array $pObject * @param array $pType * @param array $pTitle * @param array $pUrl * @access public * @return TRUE on success, FALSE on failure */ function storeWatch( $pEvent, $pObject, $pType, $pTitle, $pUrl ) { global $userlib; if( $this->isValid() ) { $hash = md5( uniqid( '.' )); $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?"; $this->mDb->query($query,array( $this->mUserId, $pEvent, $pObject ) ); $query = "INSERT INTO `".BIT_DB_PREFIX."users_watches`(`user_id` ,`event` ,`object` , `email`, `hash`, `watch_type`, `title`, `url`) VALUES(?,?,?,?,?,?,?,?)"; $this->mDb->query( $query, array( $this->mUserId, $pEvent, $pObject, $this->mInfo['email'], $hash, $pType, $pTitle, $pUrl ) ); return TRUE; } } /** * getWatches * * @param string $pEvent * @access public * @return TRUE on success, FALSE on failure */ function getWatches( $pEvent = '' ) { $ret = NULL; if( $this->isValid() ) { $mid = ''; $bindvars=array( $this->mUserId ); if ($pEvent) { $mid = " and `event`=? "; $bindvars[]=$pEvent; } $query = "select * from `".BIT_DB_PREFIX."users_watches` where `user_id`=? $mid"; $result = $this->mDb->query($query,$bindvars); $ret = array(); while ($res = $result->fetchRow()) { $ret[] = $res; } } return $ret; } /** * getEventWatches * * @param array $pEvent * @param array $object * @access public * @return TRUE on success, FALSE on failure */ function getEventWatches( $pEvent, $pObject ) { $ret = NULL; if( $this->isValid() ) { $query = "SELECT * FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?"; $result = $this->mDb->query($query,array( $this->mUserId, $pEvent, $pObject ) ); if ( $result->numRows() ) { $ret = $result->fetchRow(); } } return $ret; } /** * get_event_watches * * @param array $pEvent * @param array $pObject * @access public * @return TRUE on success, FALSE on failure */ function get_event_watches( $pEvent, $pObject ) { $ret = array(); $query = "select * from `".BIT_DB_PREFIX."users_watches` tw INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON ( tw.`user_id`=uu.`user_id` ) where `event`=? and `object`=?"; $result = $this->mDb->query( $query,array( $pEvent,$pObject )); if( !$result->numRows() ) { return $ret; } while ($res = $result->fetchRow()) { $ret[] = $res; } return $ret; } /** * remove_user_watch_by_hash * * @param array $pHash * @access public * @return TRUE on success, FALSE on failure */ function remove_user_watch_by_hash( $pHash ) { $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `hash`=?"; $this->mDb->query( $query,array( $pHash )); } /** * expungeWatch * * @param array $pEvent * @param array $pObject * @access public * @return TRUE on success, FALSE on failure */ function expungeWatch( $pEvent, $pObject ) { if( $this->isValid() ) { $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?"; $this->mDb->query( $query, array( $this->mUserId, $pEvent, $pObject )); } } /** * get_watches_events * * @access public * @return TRUE on success, FALSE on failure */ function get_watches_events() { $query = "select distinct `event` from `".BIT_DB_PREFIX."users_watches`"; $result = $this->mDb->query($query,array()); $ret = array(); while ($res = $result->fetchRow()) { $ret[] = $res['event']; } return $ret; } // }}} /** * getUserAttachments * * @param array $pListHash * @access public * @return list of attachments */ function getUserAttachments( &$pListHash ) { $pListHash['user_id'] = $this->mUserId; $mime = new LibertyMime(); return $mime->getAttachmentList( $pListHash ); } // {{{ ==================== Favorites ==================== /** * storeFavorite * * @param array $pContentId * @access public * @return TRUE on success, FALSE on failure */ function storeFavorite( $pContentId ) { $ret = FALSE; if( $this->isValid() && $this->verifyId( $pContentId )) { $this->mDb->query( "INSERT INTO `".BIT_DB_PREFIX."users_favorites_map` ( `user_id`, `favorite_content_id` ) VALUES (?,?)", array( $this->mUserId, $pContentId ) ); $ret = TRUE; } return( $ret ); } function expungeFavorite( $pContentId ) { $ret = FALSE; if( $this->isValid() && $this->verifyId( $pContentId ) ) { $this->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."users_favorites_map` WHERE `user_id`=? AND `favorite_content_id`=?", array( $this->mUserId, $pContentId ) ); $ret = TRUE; } return( $ret ); } // }}} /** * getUserId * * @access public * @return user id of currently loaded user */ function getUserId() { return( $this->isValid() ? $this->mUserId : ANONYMOUS_USER_ID ); } /** * getDisplayUrl * * @param array $pUserName * @param array $pMixed * @access public * @return URL to users homepage */ function getDisplayUrl( $pUserName=NULL, $pMixed=NULL ) { if( empty( $pUserName ) && !empty( $this ) && $this->isValid() ) { $pUserName = $this->mUsername; } if( function_exists( 'override_user_url' ) ) { $ret = override_user_url( $pUserName ); } else { global $gBitSystem; $rewrite_tag = $gBitSystem->isFeatureActive( 'pretty_urls_extended' ) ? 'view/':''; if ($gBitSystem->isFeatureActive( 'pretty_urls' ) || $gBitSystem->isFeatureActive( 'pretty_urls_extended' ) ) { $ret = USERS_PKG_URL . $rewrite_tag; $ret .= urlencode( $pUserName ); } else { $ret = USERS_PKG_URL . 'index.php?home='; $ret .= urlencode( $pUserName ); } } return $ret; } /** * getDisplayLink * * @param array $pUserName * @param array $pDisplayHash * @access public * @return get a link to the the users homepage */ function getDisplayLink( $pUserName, $pDisplayHash ) { return BitUser::getDisplayName( TRUE, $pDisplayHash ); } /** * getTitle * * @param array $pHash * @access public * @return get the users display name */ function getTitle( $pHash = NULL ) { return BitUser::getDisplayName( FALSE, $pHash ); } /** * Get user information for a particular user * * @param pUseLink return the information in the form of a url that links to the users information page * @param pHash todo - need explanation on how to use this... * @return display name or link to user information page **/ function getDisplayName( $pUseLink = FALSE, $pHash=NULL ) { global $gBitSystem, $gBitUser; $ret = NULL; if( empty( $pHash ) && !empty( $this ) && !empty( $this->mInfo )) { $pHash = &$this->mInfo; } if( !empty( $pHash )) { if( !empty( $pHash['real_name'] ) && $gBitSystem->getConfig( 'users_display_name', 'real_name' ) == 'real_name' ) { $displayName = $pHash['real_name']; } elseif( !empty( $pHash['user'] )) { $displayName = $pHash['user']; } elseif( !empty( $pHash['login'] )) { $displayName = $pHash['login']; } elseif( !empty( $pHash['email'] )) { $displayName = substr( $pHash['email'], 0, strpos( $pHash['email'], '@' )); } else { $displayName = $pHash['user_id']; } if( !empty( $pHash['user'] )) { $iHomepage = $pHash['user']; } elseif( !empty( $pHash['login'] )) { // user of 'login' is deprecated and eventually should go away! $iHomepage = $pHash['login']; } elseif( BitBase::verifyId( $pHash['user_id'] )) { $iHomepage = $pHash['user_id']; } elseif( !empty( $pHash['email'] )) { $iHomepage = $pHash['email']; } else { // this won't work right now, we need to alter userslib::interpret_home() to interpret a real name $iHomepage = $pHash['real_name']; } if( $pUseLink && $gBitUser->hasPermission( 'p_users_view_user_homepage' )) { $ret = '' . htmlspecialchars( isset( $pHash['link_label'] ) ? $pHash['link_label'] : $displayName ) .''; } else { $ret = htmlspecialchars( $displayName ); } } else { $ret = tra( "Anonymous" ); } return $ret; } /** * getRenderFile Returns include file that will * * @access public * @return the fully specified path to file to be included */ function getRenderFile() { return USERS_PKG_PATH."display_bituser_inc.php"; } /** * getSelectionList get a list of users that can be used in dropdown lists in forms to choose from * * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function getSelectionList() { $query = " SELECT uu.`user_id`, uu.`login`, uu.`real_name` FROM `".BIT_DB_PREFIX."users_users` uu ORDER BY uu.`login`"; $result = $this->mDb->query( $query ); $ret = array(); while( $res = $result->fetchRow()) { $ret[$res['user_id']] = $res['login'].(( !empty( $res['real_name'] ) && $res['real_name'] != $res['login'] ) ? ' - '.$res['real_name'] : '' ); } return $ret; } /** * getList get a list of users * * @param array $pParamHash * @access public * @return array of users */ function getList( &$pParamHash ) { global $gBitSystem; if( empty( $pParamHash['sort_mode'] )) { $pParamHash['sort_mode'] = 'registration_date_desc'; } LibertyContent::prepGetList( $pParamHash ); $selectSql = $joinSql = $whereSql = ''; $bindVars = array(); array_push( $bindVars, 'bituser' ); $this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, NULL, $pParamHash ); // lets search for a user if ( $pParamHash['find'] ) { $whereSql .= " AND UPPER( uu.`login` ) LIKE ? OR UPPER( uu.`real_name` ) LIKE ? OR UPPER( uu.`email` ) LIKE ? "; $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%'; $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%'; $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%'; } // Return an array of users indicating name, email, last changed pages, versions, last_login $query = " SELECT uu.*, lc.`content_status_id`, tf_ava.`storage_path` AS `avatar_storage_path` $selectSql FROM `".BIT_DB_PREFIX."users_users` uu INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (uu.`content_id`=lc.`content_id`) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content_hits` lch ON ( lc.`content_id` = lch.`content_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_ava ON ( uu.`avatar_attachment_id`=ta_ava.`attachment_id` ) LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` tf_ava ON ( tf_ava.`file_id`=ta_ava.`foreign_id` ) $joinSql WHERE lc.`content_type_guid` = ? $whereSql ORDER BY ".$this->mDb->convertSortmode( $pParamHash['sort_mode'] ); $result = $this->mDb->query( $query, $bindVars, $pParamHash['max_records'], $pParamHash['offset'] ); $ret = array(); while( $res = $result->fetchRow() ) { if( !empty( $res['avatar_storage_path'] )) { $res['avatar_url'] = $res['avatar_storage_path']; $res['thumbnail_url'] = liberty_fetch_thumbnail_url( array( 'storage_path' => $res['avatar_url'], // TODO: Make this a preference 'size' => 'avatar' )); } $res["groups"] = $this->getGroups( $res['user_id'] ); array_push( $ret, $res ); } $retval = array(); // TODO: update this to not put all the results in 'data' key $pParamHash["data"] = $ret; $query = " SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_users` uu INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (uu.`content_id`=lc.`content_id`) $joinSql WHERE lc.`content_type_guid` = ? $whereSql"; $pParamHash["cant"] = $this->mDb->getOne( $query, $bindVars ); LibertyContent::postGetList( $pParamHash ); return $ret; } // PURE VIRTUAL FUNCTIONS function getGroups () { print "CALL TO PURE VIRTUAL FUNCTIONS"; bt(); die; } /** * isValid * * @access public * @return TRUE if user is loaded */ function isValid() { return ( $this->verifyId( $this->mUserId ) ); } /** * isAdmin "PURE VIRTUAL BASE FUNCTION"; * * @access public * @return FALSE */ function isAdmin() { return FALSE; } /** * isRegistered * * @access public * @return TRUE if user is registered, FALSE otherwise */ function isRegistered() { return ( $this->mUserId > ANONYMOUS_USER_ID ); } /** * verifyRegistered * * @access public * @return TRUE if user is registered, otherwise a login dialog is displayed */ function verifyRegistered( $pMsg = "" ) { global $gBitSystem; if( !$this->isRegistered() ) { $gBitSystem->fatalPermission( "", $pMsg ); } return TRUE; } /** * userExists * * @param array $pUserMixed * @access public * @return TRUE on success, FALSE on failure */ function userExists( $pUserMixed ) { $ret = FALSE; if ( is_array( $pUserMixed ) ) { if( $cur = current( $pUserMixed ) ) { if( is_numeric( $cur ) ) { $conditionSql = " `".key( $pUserMixed )."` "; } else { $conditionSql = " UPPER(`".key( $pUserMixed )."`)"; } $query = "SELECT `user_id` FROM `".BIT_DB_PREFIX."users_users` WHERE $conditionSql = ?"; $ret = $this->mDb->getOne( $query, array( strtoupper( $cur ) ) ); } } return $ret; } // {{{ ==================== deprecated methods - will be removed soon ==================== // - xing - Thursday Oct 16, 2008 10:35:11 CEST /** * @deprecated deprecated since version 2.1.0-beta */ function get_SMTP_response( &$pConnect ) { deprecated( 'Method has been renamed to $gBitUser->getSmtpResponse()' ); $this->getSmtpResponse( $pConnect ); } /** * @deprecated deprecated since version 2.1.0-beta */ function update_lastlogin( $pUserId ) { deprecated( 'Method has been renamed to $gBitUser->updateLastLogin()' ); $this->updateLastLogin( $pUserId ); } /** * @deprecated deprecated since version 2.1.0-beta */ function get_users_names() { deprecated( 'Method has been removed. Please use $gBitUser->getSelectionList() instead.' ); $this->getSelectionList(); } /** * @deprecated deprecated since version 2.1.0-beta */ function getUserFromContentId( $content_id ) { deprecated( 'Method has been deprecated. Use $gBitUser->getUserInfo() instead' ); $userInfo = $this->getUserInfo( array( 'content_id' => $content_id )); return $userInfo['user_id']; } /** * @deprecated deprecated since version 2.1.0-beta */ function getByHash( $pHash ) { deprecated( "Method has been renamed to getUserIdFromCookieHash()" ); $this->getUserIdFromCookieHash( $pHash ); } /** * @deprecated deprecated since version 2.1.0-beta */ function renewPassword( $pLogin ) { deprecated( "This method doesn't seem to be used. Please remove this note if it is actually used." ); global $gBitSystem; $pass = BitSystem::genPass(); $this->storePassword( $pass, $pLogin ); return $pass; } /** * @deprecated deprecated since version 2.1.0-beta */ function get_users($offset = 0, $max_records = -1, $sort_mode = 'login_desc', $find = '') { deprecated( "This method doesn't seem to be used. Please remove this note if it is actually used." ); $sort_mode = $this->mDb->convertSortmode($sort_mode); // Return an array of users indicating name, email, last changed pages, versions, last_login if ($find) { $findesc = '%' . strtoupper( $find ) . '%'; $mid = " where UPPER(`login`) like ?"; $bindvars = array($findesc); } else { $mid = ''; $bindvars = array(); } $query = "select * from `".BIT_DB_PREFIX."users_users` $mid order by $sort_mode"; $query_cant = "select count(*) from `".BIT_DB_PREFIX."users_users`"; $result = $this->mDb->query($query, $bindvars, $max_records, $offset); $cant = $this->mDb->getOne($query_cant, array()); $ret = array(); while( $res = $result->fetchRow() ) { //$res["groups"] = $this->get_user_groups( $res['login'] ); $res["groups"] = $this->getGroups( $res['user_id'] ); array_push( $ret, $res ); } $retval = array(); $retval["data"] = $ret; $retval["cant"] = $cant; return $retval; } /** * @deprecated deprecated since version 2.1.0-beta */ function getUserFiles() { deprecated( 'Please use $gBitUser->getUserAttachments() instead.' ); global $gLibertySystem; $ret = array(); $ret['files'] = NULL; $ret['diskUsage'] = 0; if( $this->mUserId ) { $query = " SELECT a.`attachment_id`, a.`foreign_id` FROM `".BIT_DB_PREFIX."liberty_attachments` a WHERE a.`user_id` = ? AND a.`attachment_plugin_guid` = 'liberty_files'"; $result = $this->mDb->query($query, array($this->mUserId)); $attachmentIds = $result->getRows(); $bit_files_load_func = $gLibertySystem->getPluginFunction( 'bitfile', 'load_function' ); if ($bit_files_load_func && count($attachmentIds) > 0) { $files = array(); foreach ($attachmentIds as $attachmentId) { if ($attachmentId != $this->mInfo['portrait_attachment_id'] && $attachmentId != $this->mInfo['avatar_attachment_id'] && $attachmentId != $this->mInfo['logo_attachment_id']) { $fileInfo = $bit_files_load_func($attachmentId); $ret['diskUsage'] += $fileInfo['size']; $files[] = $fileInfo; } } $ret['files'] = $files; } } return $ret; } /** * @deprecated deprecated since version 2.1.0-beta */ function storeRealName($newRealName) { deprecated( "This method doesn't seem to be used. Please remove this note if it is actually used." ); if (strlen($newRealName) > REAL_NAME_COL_SIZE) { $newRealName = substr($newRealName,0,REAL_NAME_COL_SIZE); } if ($this->mUserId) { $sql = "UPDATE `".BIT_DB_PREFIX."users_users` SET `real_name` = ? WHERE `user_id` = ?"; $this->mDb->query($sql, array($newRealName, $this->mUserId)); } } /** * @deprecated deprecated since version 2.1.0-beta */ function storeLogin($newLogin) { deprecated( "This method doesn't seem to be used. Please remove this note if it is actually used." ); $newLogin = substr($newLogin,0,40); if ($this->userExists(array('login' => $newLogin))) { $this->mErrors[] = "The username '$newLogin' is already taken"; } elseif ($this->mUserId) { $sql = "UPDATE `".BIT_DB_PREFIX."users_users` SET `login` = ? WHERE `user_id` = ?"; $rs = $this->mDb->query($sql, array($newLogin, $this->mUserId)); } else { $this->mErrors[] = "Invalid user"; } return (count($this->mErrors) == 0); } /** * @deprecated deprecated since version 2.1.0-beta */ function isSemaphoreSet( $pSemName, $pLimit ) { deprecated( "Semaphore methods in users have been deprecated. Please use the semaphore package instead." ); if( !empty( $pSemName ) && !empty( $pLimit )) { global $gBitSystem; $pLimit = $gBitSystem->getUTCTime() - $pLimit; $query = "DELETE FROM `".BIT_DB_PREFIX."users_semaphores` WHERE `sem_name` = ? AND `created` < ?"; $result = $this->mDb->query( $query, array( $pSemName, ( int )$pLimit) ); $query = "SELECT `sem_name` FROM `".BIT_DB_PREFIX."users_semaphores` WHERE `sem_name`=?"; $result = $this->mDb->query( $query, array( $pSemName )); return $result->numRows(); } } /** * @deprecated deprecated since version 2.1.0-beta */ function hasSemaphoreConflict( $pSemName, $pLimit ) { deprecated( "Semaphore methods in users have been deprecated. Please use the semaphore package instead." ); if( !empty( $pSemName ) && !empty( $pLimit )) { global $gBitSystem; $ret = NULL; $pLimit = $gBitSystem->getUTCTime() - $pLimit; $query = "delete from `".BIT_DB_PREFIX."users_semaphores` where `sem_name`=? and `created`mDb->query($query,array( $pSemName, (int)$pLimit) ); $query = " SELECT uu.`login`, uu.`real_name`, uu.`email`, uu.`user_id` FROM `".BIT_DB_PREFIX."users_semaphores` ls INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON( uu.`user_id`=ls.`user_id`) WHERE `sem_name`=? AND ls.`user_id` <> ?"; if( $ret = $this->mDb->getRow( $query, array( $pSemName, ( $this->isValid() ? $this->mUserId : ANONYMOUS_USER_ID )))) { $ret['nolink'] = TRUE; } return( $ret ); } } /** * @deprecated deprecated since version 2.1.0-beta */ function storeSemaphore( $pSemName ) { deprecated( "Semaphore methods in users have been deprecated. Please use the semaphore package instead." ); if( !empty( $pSemName ) ) { global $gBitSystem; $query = "DELETE FROM `".BIT_DB_PREFIX."users_semaphores` WHERE `sem_name`=?"; $this->mDb->query( $query, array( $pSemName )); $query = "INSERT INTO `".BIT_DB_PREFIX."users_semaphores`(`sem_name`,`created`,`user_id`) VALUES(?,?,?)"; $result = $this->mDb->query( $query, array( $pSemName, $gBitSystem->getUTCTime(), ( $this->isValid() ? $this->mUserId : ANONYMOUS_USER_ID ))); return $gBitSystem->getUTCTime(); } } // }}} } /* vim: :set fdm=marker : */ ?>