users
[ class tree: users ] [ index: users ] [ all elements ]

Source for file RoleUser.php

Documentation is available at RoleUser.php

  1. <?php
  2. /**
  3.  * Lib for user administration, roles and permissions
  4.  * This lib uses pear so the constructor requieres
  5.  * a pear DB object
  6.  
  7.  * Copyright (c) 2004 bitweaver.org
  8.  * Copyright (c) 2003 tikwiki.org
  9.  * Copyright (c) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
  10.  * All Rights Reserved. See below for details and a complete list of authors.
  11.  * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details
  12.  *
  13.  * @package users
  14.  */
  15.  
  16. /**
  17.  * required setup
  18.  */
  19. require_once( LIBERTY_PKG_PATH.'LibertyMime.php' );
  20. require_once( USERS_PKG_PATH.'users_lib.php' );
  21.  
  22. define( 'AVATAR_TYPE_CENTRALIZED', 'c' );
  23. define( 'AVATAR_TYPE_USER_DB', 'u' );
  24. define( 'AVATAR_TYPE_LIBRARY', 'l' );
  25.  
  26. // Column sizes for users_users table
  27. define( 'REAL_NAME_COL_SIZE', 64 );
  28.  
  29. define( 'BITUSER_CONTENT_TYPE_GUID', 'bituser' );
  30.  
  31. // some  definitions for helping with authentication
  32. define( "USER_VALID", 2 );
  33. define( "SERVER_ERROR", -1 );
  34. define( "PASSWORD_INCORRECT", -3 );
  35. define( "USER_NOT_FOUND", -5 );
  36. define( "ACCOUNT_DISABLED", -6 );
  37.  
  38. /**
  39.  * Class that holds all information for a given user
  40.  *
  41.  * @author   spider <spider@steelsun.com>
  42.  * @package  users
  43.  * @subpackage  BitUser
  44.  */
  45. class BitUser extends LibertyMime {
  46.     var $mUserId;
  47.     var $mUsername;
  48.     var $mRoles;
  49.     var $mTicket;
  50.     var $mAuth;
  51.  
  52.     /**
  53.      * Constructor - will automatically load all relevant data if passed a user string
  54.      *
  55.      * @access public
  56.      * @author Christian Fowler <spider@viovio.com>
  57.      * @return returnString 
  58.      */
  59.     function BitUser( $pUserId=NULL, $pContentId=NULL ) {
  60.         parent::__construct();
  61.         $this->mContentTypeGuid = BITUSER_CONTENT_TYPE_GUID;
  62.         $this->registerContentType(
  63.             BITUSER_CONTENT_TYPE_GUID, array(
  64.                 'content_type_guid'   => BITUSER_CONTENT_TYPE_GUID,
  65.                 'content_name'        => 'User Information',
  66.                 'content_name_plural' => 'User Information',
  67.                 'handler_class'       => 'BitUser',
  68.                 'handler_package'     => 'users',
  69.                 'handler_file'        => 'RoleUser.php',
  70.                 'maintainer_url'      => 'http://www.bitweaver.org'
  71.             )
  72.         );
  73.         $this->mUserId = ( @$this->verifyId( $pUserId ) ? $pUserId : NULL);
  74.         $this->mContentId = $pContentId;
  75.     }
  76.  
  77.     public function getCacheKey() {
  78.         $siteCookie = static::getSiteCookieName();
  79.         if( $this->isRegistered() && !empty( $_COOKIE[$siteCookie] ) ) { 
  80.             return $_COOKIE[$siteCookie];
  81.         } else {
  82.             return ANONYMOUS_USER_ID;
  83.         }
  84.     }
  85.  
  86.     public static function isCacheableClass() {
  87.         return false;
  88.     }
  89.  
  90.     /**
  91.      * Determines if a user object is cacheable. Out of paranoia, admin's are never cached.
  92.      * @return boolean if object can be cached
  93.      */
  94.     public function isCacheableObject() {
  95.         return parent::isCacheableObject() && !$this->isAdmin();
  96.     }
  97.  
  98.     /**
  99.      * Validate inbound sort_mode parameter
  100.      * @return array of fields which are valid sorts
  101.      */
  102.     public static function getSortModeFields() {
  103.         $fields = parent::getSortModeFields();
  104.         $fields[] = 'map_position';
  105.         return $fields;
  106.     }
  107.  
  108.     /**
  109.      * load - loads all settings & preferences for this user
  110.      *
  111.      * @param boolean $pFull Load additional user data like
  112.      * @param string $pUserName User login name
  113.      * @access public
  114.      * @author Chrstian Fowler <spider@steelsun.com>
  115.      * @return returnString 
  116.      */
  117.     function load( $pFull=FALSE, $pUserName=NULL ) {
  118.         global $gBitSystem;
  119.         $this->mInfo = NULL;
  120.         if( isset( $this->mUserId ) ) {
  121.             $whereSql = "WHERE uu.`user_id`=?";
  122.             $bindVars = array( $this->mUserId );
  123.         } elseif( isset( $this->mContentId ) ) {
  124.             $whereSql = "WHERE uu.`content_id`=?";
  125.             $bindVars = array( $this->mContentId );
  126.         } elseif( !empty( $pUserName ) ) {
  127.             $whereSql = "WHERE uu.`login`=?";
  128.             $bindVars = array( $pUserName );
  129.         }
  130.         if( isset( $whereSql ) ) {
  131.             $fullSelect = '';
  132.             $fullJoin = '';
  133.             if( $pFull ) {
  134.                 $fullSelect = ' , lc.* ';
  135.                 $fullJoin = " LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( uu.`content_id`=lc.`content_id` )";
  136.                 $this->getServicesSql( 'content_load_sql_function', $fullSelect, $fullJoin, $whereSql, $bindVars );
  137.             }
  138.             // uu.`user_id` AS `uu_user_id` is last and aliases to avoid possible column name collisions
  139.             $query = "
  140.                 SELECT uu.*,
  141.                         lf_ava.`file_name` AS `avatar_file_name`,  la_ava.`attachment_id` AS `avatar_attachment_id`, lf_ava.`mime_type` AS `avatar_mime_type`,
  142.                         lf_por.`file_name` AS `portrait_file_name`, ta_por.`attachment_id` AS `portrait_attachment_id`, lf_por.`mime_type` AS `portrait_mime_type`,
  143.                         lf_logo.`file_name` AS `logo_file_name`, ta_logo.`attachment_id` AS `logo_attachment_id`, lf_logo.`mime_type` AS `logo_mime_type`
  144.                       $fullSelect, uu.`user_id` AS `uu_user_id`
  145.                 FROM `".BIT_DB_PREFIX."users_users` uu
  146.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` la_ava ON ( uu.`avatar_attachment_id`=la_ava.`attachment_id` )
  147.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` lf_ava ON ( lf_ava.`file_id`=la_ava.`foreign_id` )
  148.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_por ON ( uu.`portrait_attachment_id`=ta_por.`attachment_id` )
  149.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` lf_por ON ( lf_por.`file_id`=ta_por.`foreign_id` )
  150.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` ta_logo ON ( uu.`logo_attachment_id`=ta_logo.`attachment_id` )
  151.                     LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` lf_logo ON ( lf_logo.`file_id`=ta_logo.`foreign_id` )
  152.                     $fullJoin
  153.                 $whereSql";
  154.             if(( $result = $this->mDb->query( $query, $bindVars )) && $result->numRows() ) {
  155.                 $this->mInfo = $result->fetchRow();
  156.                 $this->mInfo['user']          = $this->mInfo['login'];
  157.                 $this->mInfo['valid']         = @$this->verifyId( $this->mInfo['uu_user_id'] );
  158.                 $this->mInfo['user_id']       = $this->mInfo['uu_user_id'];
  159.                 $this->mInfo['is_registered'] = $this->isRegistered();
  160.                 foreach( array( 'portrait', 'avatar', 'logo' ) as $img ) {
  161.                     $this->mInfo[$img.'_path']   = $this->getSourceFile( array( 'user_id'=>$this->getField( 'user_id' ), 'package'=>liberty_mime_get_storage_sub_dir_name( array( 'mime_type' => $this->getField( $img.'_mime_type' ), 'name' =>  $this->getField( $img.'_file_name' ) ) ), 'file_name' => basename( $this->mInfo[$img.'_file_name'] ), 'sub_dir' =>  $this->getField( $img.'_attachment_id' ), 'mime_type' => $this->getField( $img.'_mime_type' ) ) );
  162.                     $this->mInfo[$img.'_url']    = liberty_fetch_thumbnail_url( array( 'source_file'=>$this->mInfo[$img.'_path'], 'size' => 'small', 'mime_image' => FALSE ));
  163.                 }
  164.  
  165.                 // break the real name into first and last name using the last space as the beginning of the last name
  166.                 // for people who really want to use first and last name fields
  167.                 if( preg_match( '/ /', $this->mInfo['real_name'] ) ) {
  168.                     $this->mInfo['first_name'] = substr( $this->mInfo['real_name'], 0, strrpos($this->mInfo['real_name'], ' ') );
  169.                     $this->mInfo['last_name'] = substr( $this->mInfo['real_name'], strrpos($this->mInfo['real_name'], ' ')+1 );
  170.                 }else{
  171.                     // no spaces assign the real name to the first name
  172.                     $this->mInfo['first_name'] = $this->mInfo['real_name'];
  173.                 }
  174.  
  175.                 $this->mUserId    = $this->mInfo['uu_user_id'];
  176.                 $this->mContentId = $this->mInfo['content_id'];
  177.                 $this->mUsername  = $this->mInfo['login'];
  178.                 // a few random security conscious unset's - SPIDER
  179.                 unset( $this->mInfo['user_password'] );
  180.                 unset( $this->mInfo['hash'] );
  181.  
  182.                 $this->loadPreferences();
  183.                 // Load attachments
  184.                 LibertyMime::load();
  185.                 if( $this->getPreference( 'users_country' ) ) {
  186.                     $this->setPreference( 'flag', $this->getPreference( 'users_country' ) );
  187.                     $this->setPreference( 'users_country', str_replace( '_', ' ', $this->getPreference( 'users_country' ) ) );
  188.                 }
  189.                 if( $pFull ) {
  190.                     $this->mInfo['real_name'] = trim( $this->mInfo['real_name'] );
  191.                     $this->mInfo['display_name'] = (
  192.                         ( !empty( $this->mInfo['real_name'] ) ? $this->mInfo['real_name'] :
  193.                         ( !empty( $this->mUsername) ? $this->mUsername :
  194.                         ( !empty( $this->mInfo['email'] ) ? substr( $this->mInfo['email'], 0, strpos( $this->mInfo['email'],'@' )) :
  195.                         $this->mUserId )))
  196.                     );
  197.                     //print("displayName: ".$this->mInfo['display_name']);
  198.                     $this->defaults();
  199.                     $this->mInfo['publicEmail'] = scramble_email( $this->mInfo['email'], ( $this->getPreference( 'users_email_display' ) ? $this->getPreference( 'users_email_display' ) : NULL ) );
  200.                 }
  201.                 $this->mTicket = substr( md5( session_id() . $this->mUserId ), 0, 20 );
  202.             } else {
  203.                 $this->mUserId = NULL;
  204.             }
  205.         }
  206.         if( !$gBitSystem->isFeatureActive( 'i18n_browser_languages' ) ) {
  207.             global $gBitLanguage, $gBitUser;
  208.             //change language only if if logged user is this user
  209.             //otherwise it's just logged user (lang A) watching other user's page (lang B) and don't change
  210.             if( $this->mUserId && $this->mUserId != ANONYMOUS_USER_ID && $gBitUser === $this) {
  211.                 $gBitLanguage->mLanguage = $this->getPreference( 'bitlanguage', $gBitLanguage->mLanguage );
  212.             } elseif( isset( $_SESSION['bitlanguage'] )) {
  213.                 // users not logged that change the preference
  214.                 $gBitLanguage->mLanguage = $_SESSION['bitlanguage'];
  215.             }
  216.         }
  217.         return( $this->isValid() );
  218.     }
  219.  
  220.     /**
  221.      * defaults set a default set of preferences in mPrefs for new users
  222.      *
  223.      * @access public
  224.      * @return void 
  225.      */
  226.     function defaults() {
  227.         global $gBitSystem, $gBitThemes, $gBitLanguage;
  228.         if( !$this->getPreference( 'users_information' ) ) {
  229.             $this->setPreference( 'users_information', 'public' );
  230.         }
  231.         if( !$this->getPreference( 'messages_allow_messages' ) ) {
  232.             $this->setPreference( 'messages_allow_messages', 'y' );
  233.         }
  234.         if( !$this->getPreference( 'site_display_utc' ) ) {
  235.             $this->setPreference( 'site_display_utc', 'Local' );
  236.         }
  237. /*
  238.  * site_display_timezone is not used for 'Local' time display so daylight saving offset is not available
  239.  * both of these should pick up the 'site default' values
  240.         if( !$this->getPreference( 'site_display_timezone' ) ) {
  241.             $this->setPreference( 'site_display_timezone', 'UTC' );
  242.         }
  243.  */
  244.          if( !$this->getPreference( 'bitlanguage' ) ) {
  245.             $this->setPreference( 'bitlanguage', $gBitLanguage->mLanguage );
  246.         }
  247.         if( !$this->getPreference( 'theme' ) ) {
  248.             $this->setPreference( 'theme', $gBitThemes->getStyle() );
  249.         }
  250.     }
  251.  
  252.     /**
  253.      * verify store hash
  254.      *
  255.      * @param array $pParamHash Data to be verified
  256.      * @access public
  257.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  258.      */
  259.     function verify( &$pParamHash ) {
  260.         global $gBitSystem;
  261.  
  262.         trim_array( $pParamHash );
  263.  
  264.         // DO NOT REMOVE - to allow specific setting of the user_id during the first store.
  265.         // used by ROOT_USER_ID or ANONYMOUS_USER_ID during install.
  266.         if( @$this->verifyId( $pParamHash['user_id'] ) ) {
  267.             $pParamHash['user_store']['user_id'] = $pParamHash['user_id'];
  268.         }
  269.         // require login
  270.         if( !empty( $pParamHash['login'] ) && $pParamHash['login'] != $this->getField( 'login' ) ) {
  271.             $pParamHash['login'] = strip_tags($pParamHash['login']);
  272.             if( $this->userExists( array( 'login' => $pParamHash['login'] ) ) ) {
  273.                 $this->mErrors['login'] = 'The username "'.$pParamHash['login'].'" is already in use';
  274.             } elseif( preg_match( '/[^A-Za-z0-9_.-]/', $pParamHash["login"] ) ) {
  275.                 $this->mErrors['login'] = tra( "Your username can only contain numbers, characters, underscores and hyphens." );
  276.             } else {
  277.                 // LOWER CASE all logins
  278.                 $pParamHash['login'] = strtolower( $pParamHash['login'] );
  279.                 $pParamHash['user_store']['login'] = $pParamHash['login'];
  280.             }
  281.         }
  282.         // some people really like using first and last names
  283.         // push them into real_name
  284.         if( !empty( $pParamHash['first_name'] ) ) {
  285.             $pParamHash['real_name'] = $pParamHash['first_name'];
  286.         }
  287.         if( !empty( $pParamHash['last_name'] ) ) {
  288.             $pParamHash['real_name'] = !empty( $pParamHash['real_name'] )?$pParamHash['real_name']." ":'';
  289.             $pParamHash['real_name'] .= $pParamHash['last_name'];
  290.         }
  291.         // real_name
  292.         if( !empty( $pParamHash['real_name'] ) ) {
  293.             $pParamHash['user_store']['real_name'] = substr( strip_tags($pParamHash['real_name']), 0, 64 );
  294.         }
  295.         // require email
  296.         if( !empty( $pParamHash['email'] ) ) {
  297.             // LOWER CASE all emails
  298.             $pParamHash['email'] = strtolower( $pParamHash['email'] );
  299.             if( $emailResult = $this->verifyEmail( $pParamHash['email'] , $this->mErrors) ) {
  300.                 $pParamHash['verified_email'] = ($emailResult === true);
  301.             }
  302.         }
  303.         // check some new user requirements
  304.         if( !$this->isRegistered() ) {
  305.             if( empty( $pParamHash['login'] ) ) {
  306.                 // choose a login based on the username in the email
  307.                 if( empty($pParamHash['email']) ){
  308.                     // obviously if they didnt enter an email address we cant help them out
  309.                     $this->mErrors['email'] = tra( 'You must enter your email address' );
  310.                 }else{
  311.                     $loginBase = preg_replace( '/[^A-Za-z0-9_]/', '', substr( $pParamHash['email'], 0, strpos( $pParamHash['email'], '@' ) ) );
  312.                     $login = $loginBase;
  313.                     do {
  314.                         if( $loginTaken = $this->userExists( array( 'login' => $login ) ) ) {
  315.                             $login = $loginBase.rand(100,999);
  316.                         }
  317.                     } while( $loginTaken );
  318.                     $pParamHash['login'] = $login;
  319.                 }
  320.             }
  321.             if( empty( $pParamHash['registration_date'] ) ) {
  322.                 $pParamHash['registration_date'] = date( "U" );
  323.             }
  324.             $pParamHash['user_store']['registration_date'] = $pParamHash['registration_date'];
  325.  
  326.             if( !empty( $pParamHash['email'] ) && empty($this->mErrors['email']) ) {
  327.                 $pParamHash['user_store']['email'] = substr( $pParamHash['email'], 0, 200 ) ;
  328.             }elseif( empty($pParamHash['email']) ){
  329.                 $this->mErrors['email'] = tra( 'You must enter your email address' );
  330.             }
  331.  
  332.             if( $gBitSystem->isFeatureActive( 'users_validate_user' ) ) {
  333.                 $pParamHash['user_store']['provpass'] = md5(BitSystem::genPass());
  334.                 $pParamHash['pass_due'] = 0;
  335.             } elseif( empty( $pParamHash['password'] ) ) {
  336.                 $this->mErrors['password'] = tra( 'Your password should be at least '.$gBitSystem->getConfig( 'users_min_pass_length', 4 ).' characters long' );
  337.             }
  338.         } elseif( $this->isValid() ) {
  339.             // Prevent losing user info on save
  340.             if( empty( $pParamHash['edit'] ) ) {
  341.                 $pParamHash['edit'] = $this->mInfo['data'];
  342.             }
  343.         }
  344.  
  345.         if( isset( $pParamHash['password'] ) ) {
  346.             if( isset( $pParamHash["password2"] ) && $pParamHash["password"] != $pParamHash["password2"] ) {
  347.                 $passwordErrors['password2'] = tra("The passwords didn't match");
  348.             }
  349.             if( ( !$this->isValid() || isset( $pParamHash['password'] ) ) && $error = $this->verifyPasswordFormat( $pParamHash['password'] ) ) {
  350.                 $passwordErrors['password'] = $error;
  351.             }
  352.             if( !empty( $passwordErrors ) ) {
  353.                 $this->mErrors = array_merge( $this->mErrors,$passwordErrors );
  354.             } else {
  355.                 // Generate a unique hash
  356.                 //$pParamHash['user_store']['hash'] = md5( strtolower( (!empty($pParamHash['login'])?$pParamHash['login']:'') ).$pPassword.$pParamHash['email'] );
  357.                 $pParamHash['user_store']['hash'] = md5( $pParamHash['password'] );
  358.                 $now = $gBitSystem->getUTCTime();
  359.                 // set password due date
  360.                 // if no pass_due and no user_pass_due value user will never have to update the password
  361.                 if( empty( $pParamHash['pass_due'] ) && $gBitSystem->getConfig('users_pass_due') ) {
  362.                     // renew password according to config value
  363.                     $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $gBitSystem->getConfig('users_pass_due') );
  364.                 } elseif( !empty( $pParamHash['pass_due'] ) ) {
  365.                     // renew password only next half year ;)
  366.                     $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $pParamHash['pass_due']);
  367.                 }
  368.                 if( $gBitSystem->isFeatureActive( 'users_clear_passwords' ) || !empty( $pParamHash['user_store']['provpass'] ) ) {
  369.                     $pParamHash['user_store']['user_password'] = $pParamHash['password'];
  370.                 }
  371.             }
  372.         }
  373.  
  374.         // if we have an error we get them all by checking parent classes for additional errors
  375.         if( count( $this->mErrors ) > 0 ){
  376.             parent::verify( $pParamHash );
  377.         }
  378.  
  379.         return ( count($this->mErrors) == 0 );
  380.     }
  381.  
  382.     /**
  383.      * answerCaptcha
  384.      *
  385.      * Determine if the submitted answer for the captcha is valid
  386.      */
  387.     function answerCaptcha( $pParamHash ) {
  388.         global $gBitSystem;
  389.         // require catpcha
  390.         // novalidation is set to yes if a user confirms his email is correct after tiki fails to validate it
  391.         if( $gBitSystem->isFeatureActive( 'users_random_number_reg' ) ) {
  392.             if( ( empty( $pParamHash['novalidation'] ) || $pParamHash['novalidation'] != 'yes' )
  393.                 &&( !isset( $_SESSION['captcha'] ) || $_SESSION['captcha'] != md5( $pParamHash['captcha'] ) ) )
  394.             {
  395.                 $this->mErrors['captcha'] = "Wrong Answer";
  396.             }
  397.         }
  398.  
  399.         if( $gBitSystem->isFeatureActive( 'users_register_recaptcha' ) && (empty( $pParamHash['novalidation'] ) || $pParamHash['novalidation'] != 'yes') ) {
  400.             require_once( USERS_PKG_PATH.'classes/recaptchalib.php' );
  401.             if( !empty( $pParamHash["recaptcha_challenge_field"] ) && !empty( $pParamHash["recaptcha_response_field"] ) ) {
  402.                 $resp = recaptcha_check_answer ( $gBitSystem->getConfig( 'users_register_recaptcha_private_key' ), $_SERVER["REMOTE_ADDR"], $pParamHash["recaptcha_challenge_field"], $pParamHash["recaptcha_response_field"] );
  403.                 if( !$resp->is_valid ) {
  404.                     $this->mErrors['recaptcha'] = $resp->error;
  405.                 }
  406.             } else {
  407.                 $this->mErrors['recaptcha'] = 'Wrong Answer';
  408.             }
  409.         }
  410.  
  411.         if( $gBitSystem->isFeatureActive( 'users_register_smcaptcha' ) && (empty( $pParamHash['novalidation'] ) || $pParamHash['novalidation'] != 'yes') ) {
  412.             require_once( USERS_PKG_PATH.'classes/solvemedialib.php' );
  413.             if( !empty( $pParamHash['adcopy_challenge'] ) && !empty( $pParamHash['adcopy_response'] ) ) {
  414.                 $solvemediaResponse = solvemedia_check_answer($gBitSystem->getConfig( 'users_register_smcaptcha_v_key' ), $_SERVER["REMOTE_ADDR"], $pParamHash["adcopy_challenge"], $pParamHash["adcopy_response"], $gBitSystem->getConfig( 'users_register_smcaptcha_h_key' ) );
  415.                 if( !$solvemediaResponse->is_valid ) {
  416.                     $this->mErrors['smcaptcha'] = $solvemediaResponse->error;
  417.                 }
  418.             } else {
  419.                 $this->mErrors['smcaptcha'] = 'Wrong Answer';
  420.             }
  421.         }
  422.  
  423.         return ( count($this->mErrors) == 0 );
  424.     }
  425.  
  426.     /**
  427.      * preRegisterVerify
  428.      *
  429.      * A collection of values to verify before a user can register
  430.      * Separated from BitUser::verify so that import verification can
  431.      * be processed with less rigor than user submitted requests
  432.      */
  433.     function preRegisterVerify( &$pParamHash ) {
  434.         global $gBitSystem;
  435.  
  436.         $this->answerCaptcha( $pParamHash );
  437.  
  438.         // require passcode
  439.         if( $gBitSystem->isFeatureActive( 'users_register_require_passcode' ) ) {
  440.             if( $pParamHash["passcode"] != $gBitSystem->getConfig( "users_register_passcode",md5( $this->genPass() ) ) ) {
  441.                 $this->mErrors['passcode'] = 'Wrong passcode! You need to know the passcode to register at this site';
  442.             }
  443.         }
  444.         return ( count($this->mErrors) == 0 );
  445.     }
  446.  
  447.     /**
  448.      * verifyPasswordFormat
  449.      *
  450.      * @param array $pPassword 
  451.      * @param array $pPassword2 
  452.      * @access public
  453.      * @return FALSE on success, Error string on failure
  454.      */
  455.     function verifyPasswordFormat( $pPassword, $pPassword2=NULL ) {
  456.         global $gBitSystem;
  457.  
  458.         $minPassword = $gBitSystem->getConfig( 'users_min_pass_length', 4 );
  459.         if( strlen( $pPassword ) < $minPassword ) {
  460.             return ( tra( 'Your password should be at least '.$minPassword.' characters long' ));
  461.         }
  462.         if( !empty( $pPassword2 ) && $pPassword != $pPassword2 ) {
  463.             return( tra( 'The passwords do not match' ));
  464.         }
  465.         if( $gBitSystem->isFeatureActive( 'users_pass_chr_num' ) && ( !preg_match_all( "/[0-9]+/",$pPassword,$foo ) || !preg_match_all( "/[A-Za-z]+/",$pPassword,$foo ))) {
  466.             return ( tra( 'Password must contain both letters and numbers' ));
  467.         }
  468.  
  469.         return FALSE;
  470.     }
  471.  
  472.     /**
  473.      * getSmtpResponse
  474.      *
  475.      * @param array $pConnect 
  476.      * @access public
  477.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  478.      */
  479.     function getSmtpResponse( &$pConnect ) {
  480.         $out = "";
  481.         while( 1 ) {
  482.             $work = fgets( $pConnect, 1024 );
  483.             $out .= $work;
  484.             if( !preg_match( '/^\d\d\d-/',$work )) {
  485.                 break;
  486.             }
  487.         }
  488.         return $out;
  489.     }
  490.  
  491.     /**
  492.      * verifyEmail
  493.      *
  494.      * @param array $pEmail 
  495.      * @return TRUE on success, FALSE on failure, or -1 if verifyMX had a connection failure - mErrors will contain reason for failure
  496.      */
  497.     public function verifyEmail( $pEmail , &$pErrors ) {
  498.         global $gBitSystem;
  499.  
  500.         // check for existing user first, so root@localhost doesn't get attempted to re-register
  501.         if( !empty( $this ) && is_object( $this ) && $this->userExists( array( 'email' => $pEmail ) ) ) {
  502.             $pErrors['email'] = 'The email address "'.$pEmail.'" has already been registered.';
  503.         // during install we have some <user>@localhost as email address. we won't cause problems on those
  504.         } elseif( $pEmail == 'root@localhost' || $pEmail == 'guest@localhost' ) {
  505.             // nothing to do
  506.         } elseif( !validate_email_syntax( $pEmail ) ) {
  507.             $pErrors['email'] = 'The email address "'.$pEmail.'" is invalid.';
  508.         } elseif( $gBitSystem->isFeatureActive( 'users_validate_email' ) ) {
  509.             $mxErrors;
  510.             $ret = $this->verifyMX( $pEmail, $mxErrors ) ;
  511.             if ($ret === false)    {
  512.                 bit_error_log('INVALID EMAIL : '.$pEmail.' by '. $_SERVER['REMOTE_ADDR'] .' for '. $mxErrors['email']);
  513.                 $pErrors = array_merge( $pErrors, $mxErrors );
  514.             }
  515.         }
  516.  
  517.         if( !isset( $ret ) ) {
  518.             $ret = ( count( $pErrors ) == 0 )  ;
  519.         }
  520.  
  521.         return $ret;
  522.     }
  523.  
  524.     /**
  525.      * verifyAnonEmail
  526.      *
  527.      * @param array $pEmail 
  528.      * @return TRUE on success, FALSE on failure, or -1 if verifyMX had a connection failure - mErrors will contain reason for failure
  529.      */
  530.     public static function verifyAnonEmail( $pEmail , &$pErrors ) {
  531.         global $gBitSystem;
  532.  
  533.         // check for existing user first, so root@localhost doesn't get attempted to re-register
  534.         if( $pEmail == 'root@localhost' || $pEmail == 'guest@localhost' ) {
  535.             // nothing to do
  536.         } elseif( !validate_email_syntax( $pEmail ) ) {
  537.             $pErrors['email'] = 'The email address "'.$pEmail.'" is invalid.';
  538.         }
  539.  
  540.         if( !isset( $ret ) ) {
  541.             $ret = ( count( $pErrors ) == 0 )  ;
  542.         }
  543.  
  544.         return $ret;
  545.     }
  546.  
  547.     /**
  548.      * verifyMX
  549.      *
  550.      * @param array $pEmail 
  551.      * @param array $pValidate 
  552.      * @access public
  553.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  554.      */
  555.     function verifyMX( $pEmail, &$pErrors ) {
  556.  
  557.         global $gBitSystem, $gDebug;
  558.  
  559.         $HTTP_HOST=$_SERVER['SERVER_NAME'];
  560.         $ret = false;
  561.  
  562.         if( validate_email_syntax( $pEmail ) ){
  563.             list ( $Username, $domain ) = preg_split ("/@/",$pEmail);
  564.             //checkdnsrr will check to see if there are any MX records for the domain
  565.             if( !is_windows() and checkdnsrr ( $domain, "MX" ) )  {
  566.                 bitdebug( "Confirmation : MX record for {$domain} exists." );
  567.  
  568.                 $MXWeights = array();
  569.  
  570.                 getmxrr ( $domain, $MXHost, $MXWeights );
  571.                 $hosts = array();
  572.  
  573.                 //create an array that combines the MXWeights with their associated hosts
  574.                 for( $i = 0; $i < count( $MXHost ); $i++ ) {
  575.                     $hosts[$MXHost[$i]] = $MXWeights[$i];
  576.                 }
  577.  
  578.                 //sorts the hosts by weight
  579.                 asort($hosts);
  580.  
  581.                 if( !empty($hosts)) {    //hosts shouldn't be empty here, since we passed the checkdnsrr check, but the server COULD have died between the first and second check.
  582.                     $Connect = '' ;
  583.                     foreach ($hosts as $host=>$priority){
  584.  
  585.                         $Connect = @fsockopen ( $host, 25, $errNo, $errStr, 10 ); // 10 second timeout to open each MX server, seems adequate to me, increase as necessary
  586.                         // Success in fsockopen
  587.  
  588.                         if( $Connect ) {
  589.                             bitdebug( "Connection succeeded to {$host} SMTP." );
  590.  
  591.  
  592.                             stream_set_timeout( $Connect, 30 );
  593.                             $out = $this->getSmtpResponse( $Connect );
  594.  
  595.                             // Judgment is that a service preparing to begin a transaction will send a 220 string after a succesful handshake
  596.                             if( preg_match ( "/^220/", $out ) ) {
  597.                                 // Inform client's reaching to server who connect.
  598.                                 if( $gBitSystem->hasValidSenderEmail() ) {
  599.                                     $senderEmail = $gBitSystem->getConfig( 'site_sender_email' );
  600.                                     fputs( $Connect, "HELO $HTTP_HOST\r\n" );
  601.                                     bitdebug( "Run : HELO $HTTP_HOST" );
  602.                                     // Receive server's answering cord.
  603.                                     $out = $this->getSmtpResponse( $Connect );
  604.  
  605.                                     // Inform sender's address to server.
  606.                                     fputs ( $Connect, "MAIL FROM: <{$senderEmail}>\r\n" );
  607.                                     bitdebug( "Run : MAIL FROM: &lt;{$senderEmail}&gt;" );
  608.                                     // Receive server's answering cord.
  609.                                     $from = $this->getSmtpResponse( $Connect );
  610.  
  611.                                     // Inform listener's address to server.
  612.                                     fputs ( $Connect, "RCPT TO: <{$pEmail}>\r\n" );
  613.                                     bitdebug( "Run : RCPT TO: &lt;{$pEmail}&gt;" );
  614.                                     // Receive server's answering cord.
  615.                                     $to = $this->getSmtpResponse( $Connect );
  616.  
  617.                                     // Finish connection.
  618.                                     fputs( $Connect, "QUIT\r\n" );
  619.                                     bitdebug( "Run : QUIT" );
  620.                                     fclose( $Connect );
  621.  
  622.                                     //Checks if we received a 250 OK from the server. If we did not, the server is telling us that this address is not a valid mailbox.
  623.                                     if( !preg_match ( "/^250/", $from ) || ( !preg_match ( "/^250/", $to ) && !preg_match( "/Please use your ISP relay/", $to ))) {
  624.                                         $pErrors['email']   = $pEmail." is not recognized by the mail server. Try double checking the address for typos." ;
  625.                                         bit_error_log("INVALID EMAIL : ".$pEmail." SMTP FROM : ".$from." SMTP TO: ".$to);
  626.                                         $ret = false;
  627.                                         break; //break out of foreach and fall through to the end of function
  628.                                     }else{
  629.                                         $ret = true;//address has been verified by the server, no more checking necessary
  630.                                         break;
  631.                                     }
  632.                                 }
  633.                             } elseif( preg_match ( "/^420/", $out ) ) {
  634.                                 // Yahoo has a bad, bad habit of issuing 420's
  635.                                 bit_error_log("UNKNOWN EMAIL : ".$pEmail." SMTP response: ".$out);
  636.                                 $ret = true;
  637.                             } else {
  638.                                 $pErrors['email'] = 'Connection rejected by MX server';
  639.                                 bit_error_log("INVALID EMAIL : ".$pEmail." SMTP response: ".$out);
  640.                                 $ret = false;
  641.                             }
  642.                         } else {
  643.                             //fsockopen failed
  644.                             if(!$gBitSystem->getConfig('users_validate_email_role')){ //will ONLY stuff mErrors if you have not set a default role for verifiable emails, otherwise this is not a game breaking case
  645.                                 $pErrors['email'] = "One or more mail servers not responding";
  646.                             }
  647.                             $ret = -1; //-1 implies ambiguity, MX servers found, but unable to be reached.
  648.                         }
  649.                     }
  650.                 }else{
  651.                     $pErrors['email'] = "Mail server not found";
  652.                     $ret = false;
  653.                 }
  654.             } else {
  655.                 $pErrors['email'] = "Mail server not found";
  656.                 $ret = false;
  657.             }
  658.         } else {
  659.             $pErrors['email'] = "Invalid email syntax";
  660.             $ret = false;
  661.         }
  662.         return $ret;
  663.     }
  664.  
  665.     /**
  666.      * register - will handle everything necessary for registering a user and sending appropriate emails, etc.
  667.      *
  668.      * @access public
  669.      * @author Christian Fowler<spider@viovio.com>
  670.      * @return TRUE on success, FALSE on failure
  671.      */
  672.     function register( &$pParamHash, $pNotifyRegistrant=TRUE ) {
  673.         global $notificationlib, $gBitSmarty, $gBitSystem;
  674.         $ret = FALSE;
  675.         if( !empty( $_FILES['user_portrait_file'] ) && empty( $_FILES['user_avatar_file'] ) ) {
  676.             $pParamHash['user_auto_avatar'] = TRUE;
  677.         }
  678.         if( $this->verify( $pParamHash )) {
  679.             for( $i = 0; $i < BaseAuth::getAuthMethodCount(); $i++ ) {
  680.                 $instance = BaseAuth::init( $i );
  681.                 if( $instance && $instance->canManageAuth() ) {
  682.                     if( $userId = $instance->createUser( $pParamHash )) {
  683.                         $this->mUserId = $userId;
  684.                         break;
  685.                     } else {
  686.                         $this->mErrors = array_merge( $this->mErrors, $instance->mErrors );
  687.                         return FALSE;
  688.                     }
  689.                 }
  690.             }
  691.  
  692.             if( !empty( $pParamHash['verified_email'] ) && $pParamHash['verified_email'] && $gBitSystem->getConfig('users_validate_email_role') ) {
  693.                 RolePermUser::addUserToRole( $this->mUserId, $gBitSystem->getConfig('users_validate_email_role') );
  694.             }
  695.  
  696.             $this->mLogs['register'] = 'New user registered.';
  697.             $ret = TRUE;
  698.  
  699.             $this->load( FALSE, $pParamHash['login'] );
  700.  
  701.             require_once( KERNEL_PKG_PATH.'notification_lib.php' );
  702.             $notificationlib->post_new_user_event( $pParamHash['login'] );
  703.  
  704.             // set local time zone as default when registering
  705.             $this->storePreference( 'site_display_utc', 'Local' );
  706.  
  707.             if( !empty( $_REQUEST['CUSTOM'] ) ) {
  708.                 foreach( $_REQUEST['CUSTOM'] as $field=>$value ) {
  709.                     $this->storePreference( $field, $value );
  710.                 }
  711.             }
  712.  
  713.             // Handle optional user preferences that may be collected during registration
  714.             if( !empty( $pParamHash['prefs'] ) ) {
  715.                 foreach( array_keys( $pParamHash['prefs'] ) as $key ) {
  716.                     $this->storePreference( $key, $pParamHash['prefs'][$key] );
  717.                 }
  718.             }
  719.  
  720.             // Send notification
  721.             if( $pNotifyRegistrant ) {
  722.                 $siteName = $gBitSystem->getConfig('site_title', $_SERVER['HTTP_HOST'] );
  723.                 $gBitSmarty->assign( 'siteName',$_SERVER["SERVER_NAME"] );
  724.                 $gBitSmarty->assign( 'mail_site',$_SERVER["SERVER_NAME"] );
  725.                 $gBitSmarty->assign( 'mail_user',$pParamHash['login'] );
  726.                 if( $gBitSystem->isFeatureActive( 'users_validate_user' ) ) {
  727.                     // $apass = addslashes(substr(md5($gBitSystem->genPass()),0,25));
  728.                     $apass = $pParamHash['user_store']['provpass'];
  729.                     $foo  = parse_url( $_SERVER["REQUEST_URI"] );
  730.                     $foo1 = str_replace( "register", "confirm", $foo["path"] );
  731.                     $machine = httpPrefix().$foo1;
  732.  
  733.                     // Send the mail
  734.                     $gBitSmarty->assign( 'msg',tra( 'You will receive an email with information to login for the first time into this site' ));
  735.                     $gBitSmarty->assign( 'mail_machine',$machine );
  736.                     $gBitSmarty->assign( 'mailUserId',$this->mUserId );
  737.                     $gBitSmarty->assign( 'mailProvPass',$apass );
  738.                     $mail_data = $gBitSmarty->fetch( 'bitpackage:users/user_validation_mail.tpl' );
  739.                     mail( $pParamHash["email"], $siteName.' - '.tra( 'Your registration information' ), $mail_data, "From: ".$gBitSystem->getConfig('site_sender_email')."\nContent-type: text/plain;charset=utf-8\n" );
  740.                     $gBitSmarty->assign( 'showmsg', 'y' );
  741.  
  742.                     $this->mLogs['confirm'] = 'Validation email sent.';
  743.                 } elseif( $gBitSystem->isFeatureActive( 'send_welcome_email' ) ) {
  744.                     // Send the welcome mail
  745.                     $gBitSmarty->assign( 'mailPassword',$pParamHash['password'] );
  746.                     $gBitSmarty->assign( 'mailEmail',$pParamHash['email'] );
  747.                     $mail_data = $gBitSmarty->fetch( 'bitpackage:users/welcome_mail.tpl' );
  748.                     mail( $pParamHash["email"], tra( 'Welcome to' ).' '.$siteName, $mail_data, "From: ".$gBitSystem->getConfig( 'site_sender_email' )."\nContent-type: text/plain;charset=utf-8\n" );
  749.  
  750.                     $this->mLogs['welcome'] = 'Welcome email sent.';
  751.                 }
  752.             }
  753.             $logHash['action_log']['title'] = $pParamHash['login'];
  754.             $this->storeActionLog( $logHash );
  755.         }
  756.         return( $ret );
  757.     }
  758.  
  759.     /**
  760.      * verifyCaptcha
  761.      *
  762.      * @param array $pCaptcha 
  763.      * @access public
  764.      * @return TRUE on success, FALSE on failure
  765.      */
  766.     function verifyCaptcha( $pCaptcha = NULL ) {
  767.         if( $this->hasPermission( 'p_users_bypass_captcha' ) || ( !empty( $_SESSION['captcha_verified'] ) && $_SESSION['captcha_verified'] === TRUE ) ) {
  768.             return TRUE;
  769.         } else {
  770.             if( empty( $pCaptcha ) || empty( $_SESSION['captcha'] ) || $_SESSION['captcha'] != md5( $pCaptcha ) ) {
  771.                 return FALSE;
  772.             } else {
  773.                 $_SESSION['captcha_verified'] = TRUE;
  774.                 return TRUE;
  775.             }
  776.         }
  777.     }
  778.  
  779.  
  780.     /**
  781.      * store
  782.      *
  783.      * @param array $pParamHash 
  784.      * @access public
  785.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  786.      */
  787.     function store( &$pParamHash ) {
  788.         if( $this->verify( $pParamHash ) ) {
  789.             $this->mDb->StartTrans();
  790.             $pParamHash['content_type_guid'] = BITUSER_CONTENT_TYPE_GUID;
  791.  
  792.             if( !empty( $pParamHash['user_store'] ) && count( $pParamHash['user_store'] ) ) {
  793.                 if( $this->isValid() ) {
  794.                     $userId = array ( "user_id" => $this->mUserId );
  795.                     $result = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'], $userId );
  796.                 } else {
  797.                     if( empty( $pParamHash['user_store']['user_id'] ) ) {
  798.                         $pParamHash['user_store']['user_id'] = $this->mDb->GenID( 'users_users_user_id_seq' );
  799.                     }
  800.                     $this->mUserId = $pParamHash['user_store']['user_id'];
  801.                     $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'] );
  802.                 }
  803.             }
  804.  
  805.             // Prevent liberty from assuming ANONYMOUS_USER_ID while storing
  806.             $pParamHash['user_id'] = $this->mUserId;
  807.             // Don't let LA snarf these now so we can do extra things.
  808.             $pParamHash['_files_override'] = array();
  809.             if( LibertyMime::store( $pParamHash ) ) {
  810.  
  811.                 if( empty( $this->mInfo['content_id'] ) || ($pParamHash['content_id'] != $this->mInfo['content_id']) ) {
  812.                     $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `content_id`=? WHERE `user_id`=?";
  813.                     $result = $this->mDb->query( $query, array( $pParamHash['content_id'], $this->mUserId ) );
  814.                     $this->mInfo['content_id'] = $pParamHash['content_id'];
  815.                 }
  816.             }
  817.  
  818.             $this->mDb->CompleteTrans();
  819.  
  820.             $this->load( TRUE );
  821.         }
  822.         return( count( $this->mErrors ) == 0 );
  823.     }
  824.  
  825.     /**
  826.      * Imports a user record from csv file
  827.      * This is a admin specific function
  828.      *
  829.      * @param $pParamHash an array with user data
  830.      * @return TRUE if import succeed
  831.      ***/
  832.     function importUser( &$pParamHash ) {
  833.         global $gBitUser;
  834.  
  835.         if( ! $gBitUser->hasPermission( 'p_users_admin' ) ) {
  836.             return FALSE;
  837.         }
  838.         if( $this->verifyUserImport( $pParamHash ) ) {
  839.             $this->mDb->StartTrans();
  840.             $pParamHash['content_type_guid'] = BITUSER_CONTENT_TYPE_GUID;
  841.             if( !empty( $pParamHash['user_store'] ) && count( $pParamHash['user_store'] ) ) {
  842.                 // lookup and asign the default role for user
  843.                 $defaultRoles = RolePermUser::getDefaultRole();
  844.                 if( !empty( $defaultRoles ) ) {
  845.                     $pParamHash['user_store']['default_role_id'] = key( $defaultRoles );
  846.                 }
  847.                 if( $this->isValid() ) {
  848.                     $userId = array ( "user_id" => $this->mUserId );
  849.                     $result = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'], $userId );
  850.                 } else {
  851.                     if( empty( $pParamHash['user_store']['user_id'] ) ) {
  852.                         $pParamHash['user_store']['user_id'] = $this->mDb->GenID( 'users_users_user_id_seq' );
  853.                     }
  854.                     $this->mUserId = $pParamHash['user_store']['user_id'];
  855.                     $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_users', $pParamHash['user_store'] );
  856.                 }
  857.                 // make sure user is added into the default role map
  858.                 if( !empty( $pParamHash['user_store']['default_role_id'] ) ) {
  859.                     RolePermUser::addUserToRole( $pParamHash['user_store']['user_id'],$pParamHash['user_store']['default_role_id'] );
  860.                 }
  861.  
  862.             }
  863.             // Prevent liberty from assuming ANONYMOUS_USER_ID while storing
  864.             $pParamHash['user_id'] = $this->mUserId;
  865.             if( LibertyContent::store( $pParamHash )) {
  866.                 if( empty( $this->mInfo['content_id'] ) || $pParamHash['content_id'] != $this->mInfo['content_id'] )  {
  867.                     $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `content_id`=? WHERE `user_id`=?";
  868.                     $result = $this->mDb->query( $query, array( $pParamHash['content_id'], $this->mUserId ) );
  869.                     $this->mInfo['content_id'] = $pParamHash['content_id'];
  870.                 }
  871.             }
  872.  
  873.             $this->mDb->CompleteTrans();
  874.  
  875.             // store any uploaded images
  876.             $this->storeImages( $pParamHash );
  877.  
  878.             $this->load( TRUE );
  879.         }
  880.         return( count( $this->mErrors ) == 0 );
  881.     }
  882.  
  883.     /**
  884.      * Verify and validate the data when
  885.      * importing a user record from csv file
  886.      * This is a admin specific function
  887.      *
  888.      * @param $pParamHash an array with user data
  889.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  890.      ***/
  891.     function verifyUserImport( &$pParamHash ) {
  892.         global $gBitSystem, $gBitUser;
  893.  
  894.         if( ! $gBitUser->hasPermission( 'p_users_admin' ) ) {
  895.             return FALSE;
  896.         }
  897.  
  898.         trim_array( $pParamHash );
  899.  
  900.         // perhaps someone is importing users and *knows* what they are doing
  901.         if( @$this->verifyId( $pParamHash['user_id'] ) ) {
  902.             // only import user_id if it doesn't exist or overwrite is set.
  903.             if( !$this->userExists( array( 'user_id' => $pParamHash['user_id'] ) ) || !empty( $_REQUEST['overwrite'] ) ) {
  904.                 $pParamHash['user_store']['user_id'] = $pParamHash['user_id'];
  905.             } else {
  906.                 unset( $pParamHash['user_id'] );
  907.             }
  908.         }
  909.         if( !empty( $pParamHash['login'] ) ) {
  910.             $ret = $this->userExists( array( 'login' => $pParamHash['login'] ) );
  911.             if( !empty( $ret ) ) {
  912.                 // On batch import admin can overwrite existing user, so don't error if set
  913.                 // however, prevent overwrite of a mix of user records
  914.                 if( !empty( $_REQUEST['overwrite'] ) && (!isset($pParamHash['user_store']['user_id']) || $pParamHash['user_store']['user_id'] == $ret ) ) {
  915.                     $pParamHash['user_id'] = $ret;
  916.                     $pParamHash['user_store']['user_id'] = $pParamHash['user_id'];
  917.                 } else {
  918.                     $this->mErrors['login'] = 'The username "'.$pParamHash['login'].'" is already in use';
  919.                 }
  920.             } elseif( preg_match( '/[^A-Za-z0-9_.-]/', $pParamHash["login"] ) ) {
  921.                 $this->mErrors['login'] = tra( "Your username can only contain numbers, characters, underscores and hyphens." );
  922.             }
  923.  
  924.             if( !isset($this->mErrors['login']) ) {
  925.                 // LOWER CASE all logins
  926.                 $pParamHash['login'] = strtolower($pParamHash['login']);
  927.                 $pParamHash['user_store']['login'] = $pParamHash['login'];
  928.             }
  929.         } else {
  930.             $this->mErrors['login'] = 'Value for username is missing';
  931.         }
  932.         if( !empty( $pParamHash['real_name'] ) ) {
  933.             $pParamHash['user_store']['real_name'] = substr( $pParamHash['real_name'], 0, 64 );
  934.         }
  935.         if( !empty( $pParamHash['email'] ) ) {
  936.             // LOWER CASE all emails admin_verify_email
  937.             $pParamHash['email'] = strtolower( $pParamHash['email'] );
  938.             if( validate_email_syntax( $pParamHash['email'] ) ) {
  939.                 $ret = $this->userExists( array( 'email' => $pParamHash['email'] ) );
  940.                 if( !empty($ret) ) {
  941.                     if( !empty( $_REQUEST['overwrite'] ) && (!isset($pParamHash['user_store']['user_id']) || $pParamHash['user_store']['user_id'] == $ret ) ) {
  942.                         $pParamHash['user_id'] = $ret;
  943.                         $pParamHash['user_store']['user_id'] = $pParamHash['user_id'];
  944.                     } else {
  945.                         $this->mErrors['email'] = 'The email address "'.$pParamHash['email'].'" has already been registered.';
  946.                     }
  947.                 }
  948.                 if( !empty( $_REQUEST['admin_verify_email'] ) ) {
  949.                     if( !$this->verifyMX( $pParamHash['email'] ) ) {
  950.                         $this->mErrors['email'] = 'Cannot find a valid mail server';
  951.                     }
  952.                 }
  953.                 if( !isset($this->mErrors['email']) ) {
  954.                     $pParamHash['user_store']['email'] = strtolower( substr( $pParamHash['email'], 0, 200 ) );
  955.                 }
  956.             } else {
  957.                 $this->mErrors['email'] = 'The email address "'.$pParamHash['email'].'" has an invalid syntax.';
  958.             }
  959.         } else {
  960.             $this->mErrors['email'] = tra( 'You must enter your email address' );
  961.         }
  962.  
  963.         // check some new user requirements
  964.         if( !$this->isRegistered() ) {
  965.             if( isset($pParamHash['user_store']['user_id']) && !empty( $_REQUEST['overwrite'] ) ) {
  966.                 $this->mUserId = $this->userExists( array( 'user_id' => $pParamHash['user_store']['user_id'] ) );
  967.             }
  968.             if( empty( $pParamHash['registration_date'] ) ) {
  969.                 $pParamHash['registration_date'] = date( "U" );
  970.             }
  971.             $pParamHash['user_store']['registration_date'] = $pParamHash['registration_date'];
  972.  
  973.             if( !empty($pParamHash['hash'] ) ) {
  974.                 unset( $pParamHash['password'] );
  975.                 if($gBitSystem->isFeatureActive( 'users_clear_passwords' ) ) {
  976.                     $this->mErrors['password'] = tra( 'You cannot import a password hash when setting to stor password in plan text is set.' );
  977.                 } elseif( strlen( $pParamHash['hash'] ) <> 32 ) {
  978.                     $this->mErrors['password'] = tra( 'When importing a MD5 password hash it needto have a length of 32 bytes.' );
  979.                 }
  980.             } else {
  981.                 if( !empty( $_REQUEST['admin_verify_user'] ) ) {
  982.                     $pParamHash['user_store']['provpass'] = md5(BitSystem::genPass());
  983.                     $pParamHash['user_store']['hash'] = '';
  984.                     $pParamHash['pass_due'] = 0;
  985.                     unset( $pParamHash['password'] );
  986.                 } elseif( empty($pParamHash['password'] ) ) {
  987.                     $pParamHash['password'] = $gBitSystem->genPass();
  988.                 }
  989.             }
  990.         } elseif( $this->isValid() ) {
  991.             // Prevent loosing user info on save
  992.             if( empty( $pParamHash['edit'] ) ) {
  993.                 $pParamHash['edit'] = $this->mInfo['data'];
  994.             }
  995.         }
  996.  
  997.         if( isset( $pParamHash['password'] ) ) {
  998.             if (!$this->isValid() || isset($pParamHash['password']) ) {
  999.                 $passswordError = $this->verifyPasswordFormat( $pParamHash['password'] );
  1000.             }
  1001.             if( !empty( $passswordError ) ) {
  1002.                 $this->mErrors['password'] = $passswordError;
  1003.             } else {
  1004.                 // Generate a unique hash
  1005.                 //$pParamHash['user_store']['hash'] = md5( strtolower( (!empty($pParamHash['login'])?$pParamHash['login']:'') ).$pPassword.$pParamHash['email'] );
  1006.                 $pParamHash['user_store']['hash'] = md5( $pParamHash['password'] );
  1007.                 $now = $gBitSystem->getUTCTime();
  1008.                 if( !isset( $pParamHash['pass_due'] ) && $gBitSystem->getConfig('users_pass_due') ) {
  1009.                     $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $gBitSystem->getConfig('users_pass_due') );
  1010.                 } elseif( isset( $pParamHash['pass_due'] ) ) {
  1011.                     // renew password only next half year ;)
  1012.                     $pParamHash['user_store']['pass_due'] = $now + (60 * 60 * 24 * $pParamHash['pass_due']);
  1013.                 }
  1014.                 if( $gBitSystem->isFeatureActive( 'users_clear_passwords' ) ) {
  1015.                     $pParamHash['user_store']['user_password'] = $pParamHash['password'];
  1016.                 } else {
  1017.                     $pParamHash['user_store']['user_password'] = '';
  1018.                 }
  1019.             }
  1020.         } elseif( !empty( $pParamHash['hash'] )) {
  1021.             $pParamHash['user_store']['hash'] = $pParamHash['hash'];
  1022.             $now = $gBitSystem->getUTCTime();
  1023.             if( !isset( $pParamHash['pass_due'] ) && $gBitSystem->getConfig( 'users_pass_due' )) {
  1024.                 $pParamHash['user_store']['pass_due'] = $now + ( 60 * 60 * 24 * $gBitSystem->getConfig( 'users_pass_due' ));
  1025.             } elseif( isset( $pParamHash['pass_due'] ) ) {
  1026.                 // renew password only next half year ;)
  1027.                 $pParamHash['user_store']['pass_due'] = $now + ( 60 * 60 * 24 * $pParamHash['pass_due'] );
  1028.             }
  1029.         }
  1030.         return ( count( $this->mErrors ) == 0 );
  1031.     }
  1032.  
  1033.     /**
  1034.      * expunge removes user and associated private data
  1035.      *
  1036.      * @access public
  1037.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1038.      */
  1039.     function expunge( $pExpungeContent = NULL ) {
  1040.         global $gBitSystem;
  1041.         $this->mDb->StartTrans();
  1042.  
  1043.         if( !empty( $pExpungeContent ) ) {
  1044.             if( $pExpungeContent == 'all' ) {
  1045.                 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 ) ) ) {
  1046.                     foreach( $userContent as $contentId=>$contentTypeGuid ) {
  1047.                         if( $delContent = LibertyBase::getLibertyObject( $contentId, $contentTypeGuid ) ) {
  1048.                             $delContent->expunge();
  1049.                         }
  1050.                     }
  1051.                 }
  1052.             }
  1053.         }
  1054.  
  1055.         if( $this->mUserId != ANONYMOUS_USER_ID ) {
  1056.             $this->purgeImage( 'avatar' );
  1057.             $this->purgeImage( 'portrait' );
  1058.             $this->purgeImage( 'logo' );
  1059.             $this->invokeServices( 'users_expunge_function' );
  1060.             $userTables = array(
  1061.                 'users_cnxn',
  1062.                 'users_watches',
  1063.                 'users_favorites_map',
  1064.                 'users_users',
  1065.             );
  1066.             foreach( $userTables as $table ) {
  1067.                 $query = "DELETE FROM `".BIT_DB_PREFIX.$table."` WHERE `user_id` = ?";
  1068.                 $result = $this->mDb->query( $query, array( $this->mUserId ) );
  1069.             }
  1070.  
  1071.             parent::expunge();
  1072.  
  1073.             $logHash['action_log']['title'] = $this->mInfo['login'];
  1074.             $this->mLogs['user_del'] = 'User deleted';
  1075.             $this->storeActionLog( $logHash );
  1076.             $this->mDb->CompleteTrans();
  1077.             return TRUE;
  1078.         } else {
  1079.             $this->mDb->RollbackTrans();
  1080.             $gBitSystem->fatalError( tra( 'The anonymous user cannot be deleted' ) );
  1081.         }
  1082.     }
  1083.  
  1084.     // {{{ ==================== Sessions and logging in and out methods ====================
  1085.     /**
  1086.      * updateSession
  1087.      *
  1088.      * @param array $pSessionId 
  1089.      * @access public
  1090.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1091.      */
  1092.     function updateSession( $pSessionId ) {
  1093.         global $gLightWeightScan;
  1094.         if ( !$this->isDatabaseValid() ) return true;
  1095.         global $gBitSystem, $gBitUser;
  1096.         $update['last_get'] = $gBitSystem->getUTCTime();
  1097.         $update['current_view'] = $_SERVER['SCRIPT_NAME'];
  1098.  
  1099.         if( empty( $gLightWeightScan ) ) {
  1100.             $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 ) );
  1101.             if( $gBitUser->isRegistered() ) {
  1102.                 $update['user_id'] = $gBitUser->mUserId;
  1103.             }
  1104.             if( $row ) {
  1105.                 if( empty( $row['ip'] ) || $row['ip'] != $_SERVER['REMOTE_ADDR'] ) {
  1106.                     $update['ip'] = $_SERVER['REMOTE_ADDR'];
  1107.                 }
  1108.                 if( !empty( $_SERVER['HTTP_USER_AGENT'] ) && (empty( $row['user_agent'] ) || $row['user_agent'] != $_SERVER['HTTP_USER_AGENT']) ) {
  1109.                     $update['user_agent'] = (string)substr( $_SERVER['HTTP_USER_AGENT'], 0, 128 );
  1110.                 }
  1111.                 $update['get_count'] = $row['get_count'] + 1;
  1112.                 $ret = $this->mDb->associateUpdate( BIT_DB_PREFIX.'users_cnxn', $update, array( 'cookie' => $pSessionId ) );
  1113.             } else {
  1114.                 if( $this->isRegistered() ) {
  1115.                     $update['user_id'] = $this->mUserId;
  1116.                     $update['ip'] = $_SERVER['REMOTE_ADDR'];
  1117.                     // truncate length & cast substr to (string) to prevent insert fatals if substr returns false
  1118.                     $update['user_agent'] = (string)substr( $_SERVER['HTTP_USER_AGENT'], 0, 128 );
  1119.                     $update['get_count'] = 1;
  1120.                     $update['connect_time'] = $update['last_get'];
  1121.                     $update['cookie'] = $pSessionId;
  1122.                     $result = $this->mDb->associateInsert( BIT_DB_PREFIX.'users_cnxn', $update );
  1123.                 }
  1124.             }
  1125.             // Delete old connections nightly during the hour of 3 am
  1126.             // This needs moving to an event that is known to happen
  1127.             if( date( 'H' ) == '03' && date( 'i' ) > 0 &&  date( 'i' ) < 2 ) {
  1128.                 // Default to 30 days history
  1129.                 $oldy = $update['last_get'] - ($gBitSystem->getConfig( 'users_cnxn_history_days', 30 ) * 24 * 60 * 60);
  1130.                 $query = "DELETE from `".BIT_DB_PREFIX."users_cnxn` where `connect_time` < ?";
  1131.                 $result = $this->mDb->query($query, array($oldy));
  1132.             }
  1133.         }
  1134.         return true;
  1135.     }
  1136.  
  1137.     /**
  1138.      * countSessions
  1139.      *
  1140.      * @param array $pActive 
  1141.      * @access public
  1142.      * @return count of sessions
  1143.      */
  1144.     function countSessions( $pActive = FALSE ) {
  1145.         $query = "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_cnxn`";
  1146.         if( $pActive ) {
  1147.             $query .=" WHERE `cookie` IS NOT NULL";
  1148.         }
  1149.         return $this->mDb->getOne( $query,array() );
  1150.     }
  1151.  
  1152.     /**
  1153.      * logout
  1154.      *
  1155.      * @access public
  1156.      * @return void 
  1157.      */
  1158.     function logout() {
  1159.         // This must come first
  1160.         $this->clearFromCache();
  1161.  
  1162.         $this->sendSessionCookie( FALSE );
  1163.  
  1164.         session_destroy();
  1165.         $this->mUserId = NULL;
  1166.         // ensure Guest default page is loaded if required
  1167.         $this->mInfo['default_role_id'] = -1;
  1168.     }
  1169.  
  1170.     function sendSessionCookie( $pCookie=TRUE ) {
  1171.         global $gBitSystem;
  1172.  
  1173.         $siteCookie = static::getSiteCookieName();
  1174.         $cookieTime = 0;
  1175.         $cookiePath = BIT_ROOT_URL;
  1176.         $cookieDomain = '';
  1177.  
  1178.         if( $pCookie === TRUE ) {
  1179.             $pCookie = session_id();
  1180.         } elseif( $pCookie==FALSE ) {
  1181.             $pCookie = ''; // unset the cookie, eg logout
  1182.             if( !empty( $_COOKIE[$siteCookie] ) ) {
  1183.                 $this->mDb->query( "UPDATE `".BIT_DB_PREFIX."users_cnxn` SET `cookie`=NULL WHERE `cookie`=?", array( $_COOKIE[$siteCookie] ) );
  1184.                 unset( $_COOKIE[$siteCookie] );
  1185.             }
  1186.         }
  1187.  
  1188.         if( !empty( $pCookie ) ) {
  1189.             // Now if the remember me feature is on and the user checked the user_remember_me checkbox then ...
  1190.             if( $gBitSystem->isFeatureActive( 'users_remember_me' ) && isset( $_REQUEST['rme'] ) && $_REQUEST['rme'] == 'on' ) {
  1191.                 $cookieTime = (int)( time() + (int)$gBitSystem->getConfig( 'users_remember_time', 86400 ));
  1192.                 $cookiePath = $gBitSystem->getConfig( 'cookie_path', $cookiePath );
  1193.                 $cookieDomain = $gBitSystem->getConfig( 'cookie_domain', $cookieDomain );
  1194.             }
  1195.         }
  1196.         setcookie( $siteCookie, $pCookie, $cookieTime , $cookiePath, $cookieDomain );
  1197.         $_COOKIE[$siteCookie] = $pCookie;
  1198.     }
  1199.  
  1200.     public static function getSiteCookieName() {
  1201.         global $gBitSystem;
  1202.  
  1203.         $cookie_site = strtolower( preg_replace( "/[^a-zA-Z0-9]/", "", $gBitSystem->getConfig( 'site_title', 'bitweaver' )));
  1204.         return( 'bit-user-'.$cookie_site );
  1205.     }
  1206.  
  1207.     /**
  1208.      * verifyTicket
  1209.      *
  1210.      * @param array $pFatalOnError 
  1211.      * @param array $pForceCheck 
  1212.      * @access public
  1213.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1214.      */
  1215.     function verifyTicket( $pFatalOnError=TRUE, $pForceCheck=TRUE ) {
  1216.         global $gBitSystem, $gBitUser;
  1217.         $ret = FALSE;
  1218.         if( $pForceCheck == TRUE || !empty( $_REQUEST['tk'] ) ) {
  1219.             if( empty( $_REQUEST['tk'] ) || (!($ret = $_REQUEST['tk'] == $this->mTicket ) && $pFatalOnError) ) {
  1220.                 $userString = $gBitUser->isRegistered() ? "\nUSER ID: ".$gBitUser->mUserId.' ( '.$gBitUser->getField( 'email' ).' ) ' : '';
  1221.                 error_log( tra( "Security Violation" )."$userString ".$_SERVER['REMOTE_ADDR']."\nURI: $_SERVER[REQUEST_URI] \nREFERER: $_SERVER[HTTP_REFERER] " );
  1222.                 $gBitSystem->fatalError( tra( "Security Violation" ));
  1223.             }
  1224.         }
  1225.         return $ret;
  1226.     }
  1227.     // }}}
  1228.  
  1229.     // {{{ ==================== Banning ====================
  1230.     /**
  1231.      * ban sets the user account status to -201 suspended
  1232.      *
  1233.      * @access public
  1234.      * @return TRUE on success, Display error message on failure
  1235.      */
  1236.     function ban(){
  1237.         global $gBitSystem;
  1238.         if( $this->mUserId == ANONYMOUS_USER_ID || $this->mUserId == ROOT_USER_ID || $this->isAdmin()) {
  1239.             $gBitSystem->fatalError( tra( 'You cannot ban the user' )." ".$this->mInfo['login'] );
  1240.         } else {
  1241.             $this->storeStatus( -201 );
  1242.             return TRUE;
  1243.         }
  1244.     }
  1245.  
  1246.     /**
  1247.      * ban unban the user
  1248.      *
  1249.      * @access public
  1250.      * @return TRUE on success
  1251.      */
  1252.     function unban(){
  1253.         global $gBitSystem;
  1254.         $this->storeStatus( 50 );
  1255.         return TRUE;
  1256.     }
  1257.     // }}}
  1258.  
  1259.     /**
  1260.      * genPass generate random password
  1261.      *
  1262.      * @param array $pLength Length of final password
  1263.      * @access public
  1264.      * @return password 
  1265.      */
  1266.     function genPass( $pLength=NULL ) {
  1267.         global $gBitSystem;
  1268.         $vocales = "AaEeIiOoUu13580";
  1269.         $consonantes = "BbCcDdFfGgHhJjKkLlMmNnPpQqRrSsTtVvWwXxYyZz24679";
  1270.         $ret = '';
  1271.         if( empty( $pLength ) || !is_numeric( $pLength ) ) {
  1272.             $pLength = $gBitSystem->getConfig( 'users_min_pass_length', 4 );
  1273.         }
  1274.         for( $i = 0; $i < $pLength; $i++ ) {
  1275.             if( $i % 2 ) {
  1276.                 $ret .= $vocales{rand( 0, strlen( $vocales ) - 1 )};
  1277.             } else {
  1278.                 $ret .= $consonantes{rand( 0, strlen( $consonantes ) - 1 )};
  1279.             }
  1280.         }
  1281.         return $ret;
  1282.     }
  1283.  
  1284.     /**
  1285.      * generateChallenge
  1286.      *
  1287.      * @access public
  1288.      * @return md5 string
  1289.      */
  1290.     function generateChallenge() {
  1291.         return( md5( BitSystem::genPass() ));
  1292.     }
  1293.  
  1294.     /**
  1295.      * login
  1296.      *
  1297.      * @param array $pLogin 
  1298.      * @param array $pPassword 
  1299.      * @param array $pChallenge 
  1300.      * @param array $pResponse 
  1301.      * @access public
  1302.      * @return URL the user should be sent to after login
  1303.      */
  1304.     function login( $pLogin, $pPassword, $pChallenge=NULL, $pResponse=NULL ) {
  1305.         global $gBitSystem;
  1306.         $isvalid = false;
  1307.  
  1308.         $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login';
  1309.  
  1310.         $this->mDb->StartTrans();
  1311.         // Verify user is valid
  1312.         if( $this->validate( $pLogin, $pPassword, $pChallenge, $pResponse )) {
  1313.             $userInfo = $this->getUserInfo( array( $loginCol => $pLogin ));
  1314.  
  1315.             // If the password is valid but it is due then force the user to change the password by
  1316.             // sending the user to the new password change screen without letting him use
  1317.             // The user must re-nter the old password so no secutiry risk here
  1318.             if( $this->isPasswordDue() ) {
  1319.                 // Redirect the user to the screen where he must change his password.
  1320.                 // Note that the user is not logged in he's just validated to change his password
  1321.                 // The user must re-enter his old password so no secutiry risk involved
  1322.                 $url = USERS_PKG_URL.'change_password.php?user_id='.$userInfo['user_id'];
  1323.  
  1324.             } elseif( $userInfo['user_id'] != ANONYMOUS_USER_ID ) {
  1325.                 // User is valid and not due to change pass..
  1326.                 $this->mUserId = $userInfo['user_id'];
  1327.                 $this->load();
  1328.                 $this->loadPermissions( TRUE );
  1329.  
  1330.                 // set post-login url
  1331.                 // if role home is set for this user we get that
  1332.                 // default to general post-login
  1333.                 // @see BitSystem::getIndexPage
  1334.                 $indexType = 'my_page';
  1335.                 // getHomeRole is RolePermUser method
  1336.                 if( method_exists( $this, 'getHomeRole' ) &&
  1337.                     (( @$this->verifyId( $this->mInfo['default_role_id'] ) && ( $role_home = $this->getHomeRole( $this->mInfo['default_role_id'] ) ) ) ||
  1338.                     ( $gBitSystem->getConfig( 'default_home_role' ) && ( $role_home = $this->getHomeRole( $gBitSystem->getConfig( 'default_home_role' ) ) ) )) ){
  1339.                     $indexType = 'role_home';
  1340.                 }
  1341.  
  1342.                 $url = isset($_SESSION['loginfrom']) ? $_SESSION['loginfrom'] : $gBitSystem->getIndexPage( $indexType );
  1343.                 unset( $_SESSION['loginfrom'] );
  1344.  
  1345.                 $sessionId = session_id();
  1346.                 $this->sendSessionCookie( $sessionId );
  1347.                 $this->updateSession( $sessionId );
  1348.             }
  1349.         } else {
  1350.             // before we give up lets see if the user exists and if the password is expired
  1351.             $query = "select `email`, `user_id`, `user_password` from `".BIT_DB_PREFIX."users_users` where " . $this->mDb->convertBinary(). " $loginCol = ?";
  1352.             $result = $this->mDb->getRow( $query, array( $pLogin ) );
  1353.             if( !empty( $result['user_id'] ) && $this->isPasswordDue( $result['user_id'] ) ) {
  1354.                 // user needs email password reset so send it and let them know
  1355.                 $url = USERS_PKG_URL.'remind_password.php?remind=y&required=y&username='.$pLogin;
  1356.             }else{
  1357.                 $this->mUserId = ANONYMOUS_USER_ID;
  1358.                 unset( $this->mInfo );
  1359.                 $this->mErrors['login'] = tra( 'Invalid username or password' );
  1360.                 $url = USERS_PKG_URL.'login.php?error=' . urlencode( $this->mErrors['login'] );
  1361.             }
  1362.         }
  1363.         $this->mDb->CompleteTrans();
  1364.  
  1365.         // check for HTTPS mode and redirect back to non-ssl when not requested, or a  SSL login was forced
  1366.         if( isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on' ) {
  1367.             $refererSsl = isset( $_SERVER['HTTP_REFERER'] ) && substr( $_SERVER['HTTP_REFERER'], 0, 5 ) == 'https';
  1368.             if( ($gBitSystem->getConfig( 'site_https_login_required' ) && !$refererSsl) ) {
  1369.                 // start setting up the URL redirect without SSL
  1370.                 $prefix = 'http://' . $gBitSystem->getConfig( 'site_http_domain', $_SERVER['HTTP_HOST'] );
  1371.  
  1372.                 // add port to prefix if needed
  1373.                 $port   = $gBitSystem->getConfig( 'site_http_port', 80 );
  1374.                 if( $port != 80 ) {
  1375.                     $prefix .= ':'.$port;
  1376.                 }
  1377.                 $prefix .= $gBitSystem->getConfig( 'site_http_prefix', BIT_ROOT_URL );
  1378.                 if( strrpos( $prefix, '/' ) == (strlen( $prefix )  - 1) ) {
  1379.                     $prefix = substr( $prefix, 0, strlen( $prefix ) - 1 );
  1380.                 }
  1381.                 // join prefix and URL
  1382.                 $url = $prefix.$url;
  1383.             }
  1384.         }
  1385.         return( $url );
  1386.     }
  1387.  
  1388.     /**
  1389.      * validate
  1390.      *
  1391.      * @param array $pUser 
  1392.      * @param array $pPass 
  1393.      * @param array $pChallenge 
  1394.      * @param array $pResponse 
  1395.      * @access public
  1396.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1397.      * @todo rewrite this mess. this is horrible stuff. - xing - Thursday Oct 16, 2008   09:47:20 CEST
  1398.      */
  1399.     function validate( $pUser, $pPass, $pChallenge, $pResponse ) {
  1400.         global $gBitSystem;
  1401.         // these will help us keep tabs of what is going on
  1402.         $authValid = $authPresent = FALSE;
  1403.         $createAuth = ( $gBitSystem->getConfig( "users_create_user_auth", "n" ) == "y" );
  1404.  
  1405.         for( $i = 0; $i < BaseAuth::getAuthMethodCount(); $i++ ) {
  1406.             $instance = BaseAuth::init( $i );
  1407.             if( $instance ) {
  1408.                 $result = $instance->validate( $pUser, $pPass, $pChallenge, $pResponse );
  1409.                 switch( $result ) {
  1410.                     case USER_VALID:
  1411.                         unset($this->mErrors['login']);
  1412.                         $authPresent = TRUE;
  1413.                         $authValid = TRUE;
  1414.                         break;
  1415.                     case PASSWORD_INCORRECT:
  1416.                         // 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
  1417.                         // This might have broken other auth, but at this point, bw auth was TOTALLY busted. If you need to fix, please come find me.
  1418.                         $this->mErrors['login'] = 'Password incorrect';
  1419.                         $authPresent = TRUE;
  1420.                         break;
  1421.                     case USER_NOT_FOUND:
  1422.                         break;
  1423.                 }
  1424.  
  1425.                 if( $authValid ) {
  1426.                     if( empty( $instance->mInfo['email'] )) {
  1427.                         $instance->mInfo['email'] = $pUser;
  1428.                     }
  1429.  
  1430.                     //If we're given a user_id then the user is already in the database:
  1431.                     if( !empty( $instance->mInfo['user_id'] )) {
  1432.                         $this->mUserId = $instance->mInfo['user_id'];
  1433.  
  1434.                     //Is the user already in the database:
  1435.                     } elseif( $this->mDb->getOne( "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_users` WHERE `login` = ?", array( $instance->mLogin )) > 0 ) {
  1436.                         // Update Details
  1437.                         $authUserInfo = array(
  1438.                             'login'     => $instance->mInfo['login'],
  1439.                             'password'  => $instance->mInfo['password'],
  1440.                             'real_name' => $instance->mInfo['real_name'],
  1441.                             'email'     => $instance->mInfo['email']
  1442.                         );
  1443.                         $userInfo = $this->getUserInfo( array( 'login' => $pUser ));
  1444.                         $this->mUserId = $userInfo['user_id'];
  1445.                         $this->store( $authUserInfo );
  1446.                         $this->mErrors = array();
  1447.  
  1448.                     } else {
  1449.                         $authUserInfo = array(
  1450.                             'login'     => $instance->mInfo['login'],
  1451.                             'password'  => $instance->mInfo['password'],
  1452.                             'real_name' => $instance->mInfo['real_name'],
  1453.                             'email'     => $instance->mInfo['email']
  1454.                         );
  1455.                         // TODO somehow, mUserId gets set to -1 at this point - no idea how
  1456.                         // set to NULL to prevent overwriting Guest user - wolff_borg
  1457.                         $this->mUserId = NULL;
  1458.                         $this->store( $authUserInfo );
  1459.                     }
  1460.  
  1461.                     if( $createAuth && $i > 0 ) {
  1462.                         // if the user was logged into this system and we should progate users down other auth methods
  1463.                         for( $j = $i; $i >= 0; $j-- ) {
  1464.                             $probMethodName = $gBitSystem->getConfig( "users_auth_method_$j", $default );
  1465.                             if( !empty( $probMethodName )) {
  1466.                                 $probInstance = BaseAuth::init( $probMethodName );
  1467.                                 if( $probInstance && $probInstance->canManageAuth() ) {
  1468.                                     $result = $probInstance->validate( $pUser, $pPass, $pChallenge, $pResponse );
  1469.                                     if( $result == USER_VALID || $result == PASSWORD_INCORRECT ) {
  1470.                                         // see if we can create a new account
  1471.                                         $userattr = $instance->getUserData();
  1472.                                         if( empty( $userattr['login'] )) {
  1473.                                             $userattr['login'] = $pUser;
  1474.                                         }
  1475.                                         if( empty( $userattr['password'] )) {
  1476.                                             $userattr['password'] = $pPass;
  1477.                                         }
  1478.                                         $probInstance->createUser( $userattr );
  1479.                                     }
  1480.                                 }
  1481.                                 $this->mErrors = array_merge( $this->mErrors, $probInstance->mErrors );
  1482.                             }
  1483.                         }
  1484.                     }
  1485.                     $this->mAuth = $instance;
  1486.                     break;
  1487.                 }
  1488.                 $this->mErrors = array_merge( $this->mErrors,$instance->mErrors );
  1489.             }
  1490.         }
  1491.         if( $this->mUserId != ANONYMOUS_USER_ID ) {
  1492.             $this->load();
  1493.             //on first time login we run the users registation service
  1494.             if( $this->mInfo['last_login'] == NULL ) {
  1495.                 $this->invokeServices( 'users_register_function' );
  1496.             }
  1497.             $this->updateLastLogin( $this->mUserId );
  1498.         }
  1499.         return( count( $this->mErrors ) == 0 );
  1500.     }
  1501.  
  1502.     /**
  1503.      * updateLastLogin
  1504.      *
  1505.      * @param array $pUserId 
  1506.      * @access public
  1507.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1508.      */
  1509.     function updateLastLogin( $pUserId ) {
  1510.         $ret = FALSE;
  1511.         if( @$this->verifyId( $pUserId ) ) {
  1512.             global $gBitSystem;
  1513.             $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `last_login` = `current_login`, `current_login` = ? WHERE `user_id` = ?";
  1514.             $result = $this->mDb->query( $query, array( $gBitSystem->getUTCTime(), $pUserId ));
  1515.             $ret = TRUE;
  1516.         }
  1517.         return $ret;
  1518.     }
  1519.  
  1520.     /**
  1521.      * confirmRegistration
  1522.      *
  1523.      * @param array $pUserId 
  1524.      * @param array $pProvpass 
  1525.      * @access public
  1526.      * @return registered user, empty array on failure
  1527.      */
  1528.     function confirmRegistration( $pUserId, $pProvpass ) {
  1529.         global $gBitSystem;
  1530.         $query = "
  1531.             SELECT `user_id`, `provpass`, `user_password`, `login`, `email` FROM `".BIT_DB_PREFIX."users_users`
  1532.             WHERE `user_id`=? AND `provpass`=? AND ( `provpass_expires` IS NULL OR `provpass_expires` > ?)";
  1533.         return( $this->mDb->getRow( $query, array( $pUserId, $pProvpass, $gBitSystem->getUTCTime() )));
  1534.     }
  1535.  
  1536.     /**
  1537.      * changeUserEmail
  1538.      *
  1539.      * @param array $pUserId 
  1540.      * @param array $pEmail 
  1541.      * @access public
  1542.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1543.      */
  1544.     function changeUserEmail( $pUserId, $pEmail ) {
  1545.         if( !validate_email_syntax( $pEmail ) ) {
  1546.             $this->mErrors['bad_mail'] = tra( "The email address provided does not have recognised valid syntax." );
  1547.         } elseif( $this->userExists( array( 'email' => $pEmail ))) {
  1548.             $this->mErrors['duplicate_mail'] = tra( "The email address you selected already exists." );
  1549.         } else {
  1550.             $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `email`=? WHERE `user_id`=?";
  1551.             $result = $this->mDb->query( $query, array( $pEmail, $pUserId ) );
  1552.             $query = "UPDATE `".BIT_DB_PREFIX."users_watches` SET `email`=? WHERE `user_id`=?";
  1553.             $result = $this->mDb->query( $query, array( $pEmail, $pUserId ) );
  1554.  
  1555.             // update value in hash
  1556.             $this->mInfo['email'] = $pEmail;
  1557.         }
  1558.         return( count( $this->mErrors ) == 0 );
  1559.     }
  1560.  
  1561.     /**
  1562.      * lookupHomepage
  1563.      *
  1564.      * @param array $iHomepage 
  1565.      * @access public
  1566.      * @return user_id that can be used to point to users homepage
  1567.      */
  1568.     function lookupHomepage( $iHomepage ) {
  1569.         $ret = NULL;
  1570.         if( @$this->verifyId( $iHomepage )) {
  1571.             // iHomepage is the user_id for the user...
  1572.             $key = 'user_id';
  1573.             // force to proper integer to get things like "007." to properly query
  1574.             $iHomepage = (integer)$iHomepage;
  1575.         } elseif( substr( $iHomepage, 0, 7 ) == 'mailto:' ) {
  1576.             // iHomepage is the email address of the user...
  1577.             $key = 'email';
  1578.         } else {
  1579.             // iHomepage is the 'login' of the user...
  1580.             $key = 'login';
  1581.         }
  1582.         $tmpUser = $this->getUserInfo( array( $key => $iHomepage ));
  1583.         if( @$this->verifyId( $tmpUser['user_id'] )) {
  1584.             $ret = $tmpUser['user_id'];
  1585.         }
  1586.         return $ret;
  1587.     }
  1588.  
  1589.     /**
  1590.      * getUserPreference
  1591.      *
  1592.      * @param array $pPrefName 
  1593.      * @param array $pPrefDefault 
  1594.      * @param array $pUserId 
  1595.      * @access public
  1596.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1597.      */
  1598.     static function getUserPreference( $pPrefName, $pPrefDefault, $pUserId ) {
  1599.         // Alternate to LibertyContent::getPreference when all you have is a user_id and a pref_name, and you need a value...
  1600.         global $gBitDb;
  1601.         $ret = NULL;
  1602.  
  1603.         if( BitBase::verifyId( $pUserId ) ) {
  1604.             $query = "
  1605.                 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`)
  1606.                 WHERE uu.`user_id` = ? AND lcp.`pref_name` = ?";
  1607.             if( !$ret = $gBitDb->getOne( $query, array( $pUserId, $pPrefName ))) {
  1608.                 $ret = $pPrefDefault;
  1609.             }
  1610.         }
  1611.         return $ret;
  1612.     }
  1613.  
  1614.     /**
  1615.      * getUserInfo will fetch the user info of a given user
  1616.      *
  1617.      * @param array $pUserMixed hash key can be any column in users_users table e.g.: 'login', 'user_id', 'email', 'content_id'
  1618.      * @access public
  1619.      * @return user info on success, NULL on failure
  1620.      */
  1621.     function getUserInfo( $pUserMixed ) {
  1622.         $ret = NULL;
  1623.         if( is_array( $pUserMixed ) ) {
  1624.             if( $val =  current( $pUserMixed ) ) {
  1625.                 if( !is_numeric( $val ) ) {
  1626.                     $col = "UPPER( uu.`".key( $pUserMixed )."` ) ";
  1627.                     $val = strtoupper( $val );
  1628.                 } else {
  1629.                     $col = " uu.`".key( $pUserMixed )."` ";
  1630.                     if( $val > 0x1FFFFFFF ) {
  1631.                         // 32 bit overflow, set to zero to avoid fatal error in databases with 32 bit signed integer columns
  1632.                         $val = 0;
  1633.                     }
  1634.                 }
  1635.                 $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 = ?";
  1636.                 $ret = $this->mDb->getRow( $query, array( $val ));
  1637.             }
  1638.         }
  1639.         return $ret;
  1640.     }
  1641.  
  1642.     /**
  1643.      * isUserPublic Determine if an arbitrary user can be viewed by non-permissioned users.
  1644.      *
  1645.      * @param array $pUserId user_id of user to query visibility, if NULL will use this object
  1646.      * @access public
  1647.      * @return boolean if user is publically visible
  1648.      */
  1649.     function isUserPrivate( $pUserId=NULL ) {
  1650.         $infoPref = NULL;
  1651.         if( BitBase::verifyId( $pUserId ) ) {
  1652.             $infoPref = BitUser::getUserPreference( 'users_information', NULL, $pUserId );
  1653.         } elseif( isset( $this ) && $this->isValid() ) {
  1654.             $infoPref = $this->getPreference( 'users_information' );
  1655.         }
  1656.  
  1657.         return $infoPref == 'private';
  1658.     }
  1659.  
  1660.     /**
  1661.      * getByHash get user from cookie hash
  1662.      *
  1663.      * @param array $pHash 
  1664.      * @access public
  1665.      * @return user info
  1666.      */
  1667.     function getUserIdFromCookieHash( $pHash ) {
  1668.         $query = "SELECT `user_id` FROM `".BIT_DB_PREFIX."users_cnxn` WHERE `cookie` = ?";
  1669.         return $this->mDb->getOne( $query, array( $pHash ));
  1670.     }
  1671.  
  1672.     /**
  1673.      * isPasswordDue work out if a user has to change their password
  1674.      *
  1675.      * @access public
  1676.      * @return TRUE when the password is due, FALSE if it isn't, NULL when no due time is set
  1677.      * @note NULL password due means *no* expiration
  1678.      */
  1679.     function isPasswordDue( $pUserId = NULL ) {
  1680.         global $gBitSystem;
  1681.         $ret = FALSE;
  1682.         if( empty( $pUserId) && $this->isRegistered() ) {
  1683.             $pUserId = $this->mUserId;
  1684.         }
  1685.         if( !empty( $pUserId ) ){
  1686.             // get user_id to avoid NULL and zero confusion
  1687.             $query = "
  1688.                 SELECT `user_id`, `pass_due`
  1689.                 FROM `".BIT_DB_PREFIX."users_users`
  1690.                 WHERE `pass_due` IS NOT NULL AND `user_id`=? ";
  1691.             $due = $this->mDb->getRow( $query, array( $pUserId ) );
  1692.             if( @$this->verifyId( $due['user_id'] ) && !empty( $due['pass_due'] ) ) {
  1693.                 $ret = $due['pass_due'] <= $gBitSystem->getUTCTime();
  1694.             }
  1695.         }
  1696.         return $ret;
  1697.     }
  1698.  
  1699.     /**
  1700.      * createTempPassword
  1701.      *
  1702.      * @param array $pLogin 
  1703.      * @param array $pPass 
  1704.      * @access public
  1705.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1706.      */
  1707.     function createTempPassword( $pLogin, $pPass ) {
  1708.         global $gBitSystem;
  1709.         $ret = array( '', '' );
  1710.  
  1711.         if( empty( $pLogin ) ) {
  1712.             $pLogin = $this->getField( 'email' );
  1713.         }
  1714.  
  1715.         if( !empty( $pLogin )) {
  1716.             $pass = BitSystem::genPass();
  1717.             $provpass = md5( $pass );
  1718.             $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login';
  1719.  
  1720.             #temp passwords good for 3 days -- prob should be an config option
  1721.             $passDue = $gBitSystem->getUTCTime() + ( 60 * 60 * 24 * 3 );
  1722.             $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `provpass` = ?, `provpass_expires` = ? WHERE `".$loginCol."` = ?";
  1723.             $result = $this->mDb->query( $query, array( $provpass, $passDue, $pLogin ));
  1724.             $ret = array( $pass, $provpass );
  1725.         }
  1726.         return $ret;
  1727.     }
  1728.  
  1729.     /**
  1730.      * storePassword
  1731.      *
  1732.      * @param array $pPass 
  1733.      * @param array $pLogin 
  1734.      * @access public
  1735.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1736.      */
  1737.     function storePassword( $pPass, $pLogin=NULL ) {
  1738.         global $gBitSystem;
  1739.         $ret = FALSE;
  1740.  
  1741.         if( empty( $pLogin ) ) {
  1742.             $pLogin = $this->getField( 'email' );
  1743.         }
  1744.  
  1745.         if( !empty( $pLogin )) {
  1746.             $ret = TRUE;
  1747.             $hash = md5( $pPass );
  1748.             // if renew password config is set then set - otherwise set null to respect no pass due
  1749.             $passDue = NULL;
  1750.             if( $gBitSystem->getConfig('users_pass_due') ) {
  1751.                 $now = $gBitSystem->getUTCTime();;
  1752.                 // renew password according to config value
  1753.                 $passDue = $now + ( 60 * 60 * 24 * $gBitSystem->getConfig( 'users_pass_due' ));
  1754.             }
  1755.             if( !$gBitSystem->isFeatureActive( 'users_clear_passwords' )) {
  1756.                 $pPass = NULL;
  1757.             }
  1758.             $loginCol = strpos( $pLogin, '@' ) ? 'email' : 'login';
  1759.             $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `provpass`= NULL, `provpass_expires` = NULL,`hash`=? ,`user_password`=? ,`pass_due`=? WHERE `".$loginCol."`=?";
  1760.             $result = $this->mDb->query( $query, array( $hash, $pPass, $passDue, $pLogin ));
  1761.         }
  1762.         return $ret;
  1763.     }
  1764.  
  1765.     /**
  1766.      * getUserActivity
  1767.      *
  1768.      * @param array $pListHash 
  1769.      * @access public
  1770.      * @return array of users and what they have been up to
  1771.      */
  1772.     function getUserActivity( &$pListHash ) {
  1773.         $bindVars = array();
  1774.         if( empty( $pListHash['sort_mode'] ) ) {
  1775.             $pListHash['sort_mode'] = 'last_get_desc';
  1776.         }
  1777.         LibertyContent::prepGetList( $pListHash );
  1778.  
  1779.         $whereSql = '';
  1780.         if( !empty( $pListHash['last_get'] ) ) {
  1781.             $whereSql .= ' AND uc.`last_get` > ? ';
  1782.             $bindVars[] = time() - $pListHash['last_get'];
  1783.         }
  1784.  
  1785.         if( @BitBase::verifyId( $pListHash['user_id'] ) ) {
  1786.             $whereSql .= ' AND uc.`user_id` = ? ';
  1787.             $bindVars[] = $pListHash['user_id'];
  1788.         }
  1789.  
  1790.         if( !empty( $pListHash['ip'] ) ) {
  1791.             $ips = split( ',', $pListHash['ip'] );
  1792.             $whereSql .= ' AND ( ';
  1793.             do {
  1794.                 $ip = array_pop( $ips );
  1795.                 $whereSql .= ' uc.`ip` = ? ';
  1796.                 $bindVars[] = $ip;
  1797.                 if( !empty( $ips ) ) {
  1798.                     $whereSql .= ' OR ';
  1799.                 }
  1800.             } while( $ips );
  1801.             $whereSql .= ' ) ';
  1802.         }
  1803.  
  1804.         if( !empty( $pListHash['online'] ) ) {
  1805.             $whereSql .= ' AND uc.`cookie` IS NOT NULL ';
  1806.         }
  1807.  
  1808.         $query = "
  1809.             SELECT DISTINCT uc.`user_id`, `login`, `real_name`, `connect_time`, `ip`, `user_agent`, `last_get`, uu.`content_id`
  1810.             FROM `".BIT_DB_PREFIX."users_cnxn` uc
  1811.                 INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON (uc.`user_id` = uu.`user_id`)
  1812.             WHERE uc.`user_id` IS NOT NULL $whereSql
  1813.             ORDER BY ".$this->mDb->convertSortmode( $pListHash['sort_mode'] );
  1814.         $result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] );
  1815.         $ret = array();
  1816.         while( $res = $result->fetchRow() ) {
  1817.             $res['users_information'] =     $this->getPreference( 'users_information', 'public', $res['content_id'] );
  1818.             $ret[] = $res;
  1819.         }
  1820.  
  1821.         $countSql = "
  1822.             SELECT COUNT( uc.`user_id` )
  1823.             FROM `".BIT_DB_PREFIX."users_cnxn` uc
  1824.                 INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON (uc.`user_id` = uu.`user_id`)
  1825.             WHERE uc.`user_id` IS NOT NULL $whereSql";
  1826.         $pListHash['cant'] = $this->mDb->GetOne( $countSql, $bindVars );
  1827.         $this->postGetList( $pListHash );
  1828.         return $ret;
  1829.     }
  1830.  
  1831.     /**
  1832.      * getUserDomain
  1833.      *
  1834.      * @param array $pLogin 
  1835.      * @access public
  1836.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1837.      */
  1838.     function getUserDomain( $pLogin ) {
  1839.         $ret = array();
  1840.         if( $pLogin == $this->getField( 'login' ) && $this->getPreference( 'domain_style' ) ) {
  1841.             $ret = $this->mInfo;
  1842.             $ret['style'] = $this->getPreference( 'domain_style' );
  1843.         } else {
  1844.             $sql = "
  1845.                 SELECT uu.*, lcp.`pref_value` AS `style`
  1846.                 FROM `".BIT_DB_PREFIX."users_users` uu
  1847.                     INNER JOIN `".BIT_DB_PREFIX."liberty_content_prefs` lcp ON( uu.`content_id` = lcp.`content_id` )
  1848.                 WHERE uu.`login` = ? AND lcp.`pref_name` = ?";
  1849.             $ret = $this->mDb->getRow( $sql,  array( $pLogin, 'domain_style' ) );
  1850.         }
  1851.         return( $ret );
  1852.     }
  1853.  
  1854.     /**
  1855.      * getDomain
  1856.      *
  1857.      * @param array $pContentId 
  1858.      * @access public
  1859.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1860.      */
  1861.     function getDomain( $pContentId ) {
  1862.         $ret = array();
  1863.         if( $this->verifyId( $pContentId ) ) {
  1864.             $ret['content_id'] = $pContentId;
  1865.             $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' ));
  1866.         }
  1867.         return( $ret );
  1868.     }
  1869.  
  1870.     /**
  1871.      * canCustomizeTheme check if a user can customise their theme
  1872.      *
  1873.      * @access public
  1874.      * @return TRUE on success, FALSE on failure
  1875.      */
  1876.     function canCustomizeTheme() {
  1877.         global $gBitSystem;
  1878.         return( $this->hasPermission( 'p_tidbits_custom_home_theme' ) || $gBitSystem->getConfig( 'users_themes' ) == 'y' || $gBitSystem->getConfig( 'users_themes' ) == 'h' || $gBitSystem->getConfig( 'users_themes' ) == 'u' );
  1879.     }
  1880.  
  1881.     /**
  1882.      * canCustomizeLayout  check if a user can customise their layout
  1883.      *
  1884.      * @access public
  1885.      * @return TRUE on success, FALSE on failure
  1886.      */
  1887.     function canCustomizeLayout() {
  1888.         global $gBitSystem;
  1889.         return( $this->hasPermission( 'p_tidbits_custom_home_layout' ) || $gBitSystem->getConfig( 'users_layouts' ) == 'y' || $gBitSystem->getConfig( 'users_layouts' ) == 'h' || $gBitSystem->getConfig( 'users_layouts' ) == 'u' );
  1890.     }
  1891.  
  1892.  
  1893.  
  1894.     // {{{ ==================== image and file functions ====================
  1895.     /**
  1896.      * getThumbnailUrl
  1897.      *
  1898.      * @param string $pSize 
  1899.      * @param array $pInfoHash 
  1900.      * @access public
  1901.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1902.      */
  1903.     function getThumbnailUrl( $pSize = 'small', $pInfoHash = NULL, $pSecondaryId = NULL, $pDefault=TRUE ) {
  1904.         $ret = '';
  1905.         if( $pInfoHash ) {
  1906.             // do some stuff if we are passed a hash-o-crap, not implemented currently
  1907.         } elseif( $this->isValid() ) {
  1908.             $ret = $this->getField( 'avatar_url' );
  1909.         }
  1910.         return $ret;
  1911.     }
  1912.  
  1913.     /**
  1914.      * storeImages will store any user images - please note that uploaded files have to be in predefined keys in $_FILES
  1915.      *     $_FILES['user_portrait_file']
  1916.      *     $_FILES['user_auto_avatar']
  1917.      *     $_FILES['user_logo_file']
  1918.      *
  1919.      * @param array $pParamHash array of options
  1920.      * @param boolean $pParamHash['user_auto_avatar'] automatically create avatar from portrait
  1921.      * @access public
  1922.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1923.      */
  1924.     function storeImages( $pParamHash ) {
  1925.         if( isset( $_FILES['user_portrait_file'] ) && is_uploaded_file( $_FILES['user_portrait_file']['tmp_name'] ) && $_FILES['user_portrait_file']['size'] > 0 ) {
  1926.             $portraitHash                          = $pParamHash;
  1927.             $portraitHash['user_id']               = $this->mUserId;
  1928.             $portraitHash['content_id']            = $this->mContentId;
  1929.             $portraitHash['upload']                = $_FILES['user_portrait_file'];
  1930.             $portraitHash['upload']['source_file'] = $_FILES['user_portrait_file']['tmp_name'];
  1931.             $this->storePortrait( $portraitHash, !empty( $portraitHash['user_auto_avatar'] ));
  1932.         }
  1933.  
  1934.         if( isset( $_FILES['user_avatar_file'] ) && is_uploaded_file( $_FILES['user_avatar_file']['tmp_name'] ) && $_FILES['user_avatar_file']['size'] > 0 ) {
  1935.             $avatarHash                          = $pParamHash;
  1936.             $avatarHash['user_id']               = $this->mUserId;
  1937.             $avatarHash['content_id']            = $this->mContentId;
  1938.             $avatarHash['upload']                = $_FILES['user_avatar_file'];
  1939.             $avatarHash['upload']['source_file'] = $_FILES['user_avatar_file']['tmp_name'];
  1940.             $this->storeAvatar( $avatarHash );
  1941.         }
  1942.  
  1943.         if( isset( $_FILES['user_logo_file'] ) && is_uploaded_file( $_FILES['user_logo_file']['tmp_name'] ) && $_FILES['user_logo_file']['size'] > 0 ) {
  1944.             $logoHash                          = $pParamHash;
  1945.             $logoHash['user_id']               = $this->mUserId;
  1946.             $logoHash['content_id']            = $this->mContentId;
  1947.             $logoHash['upload']                = $_FILES['user_logo_file'];
  1948.             $logoHash['upload']['source_file'] = $_FILES['user_logo_file']['tmp_name'];
  1949.             $this->storeLogo( $logoHash );
  1950.         }
  1951.     }
  1952.  
  1953.     /**
  1954.      * storePortrait
  1955.      *
  1956.      * @param array $pStorageHash 
  1957.      * @param array $pGenerateAvatar 
  1958.      * @access public
  1959.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1960.      */
  1961.     function storePortrait( &$pStorageHash, $pGenerateAvatar = FALSE ) {
  1962.         if( $this->isValid() && count( $pStorageHash )) {
  1963.             // make a copy before the uploaded file disappears
  1964.             if( $pGenerateAvatar ) {
  1965.                 $avatarHash = $pStorageHash;
  1966.                 $avatarHash['upload']['tmp_name'] = $pStorageHash['upload']['tmp_name'].'.av';
  1967.                 copy( $pStorageHash['upload']['tmp_name'], $pStorageHash['upload']['tmp_name'].'.av' );
  1968.             }
  1969.  
  1970.             if( $this->storeUserImage( $pStorageHash, 'portrait' ) && $pGenerateAvatar ) {
  1971.                 $this->storeAvatar( $avatarHash );
  1972.                 // nuke copy of image
  1973.                 @unlink( $pStorageHash['upload']['tmp_name'].'.av' );
  1974.             }
  1975.         }
  1976.  
  1977.         return( count( $this->mErrors ) == 0 );
  1978.     }
  1979.  
  1980.     /**
  1981.      * storeAvatar
  1982.      *
  1983.      * @param array $pStorageHash 
  1984.      * @access public
  1985.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1986.      */
  1987.     function storeAvatar( &$pStorageHash ) {
  1988.         return( $this->storeUserImage( $pStorageHash, 'avatar' ));
  1989.     }
  1990.  
  1991.     /**
  1992.      * storeLogo
  1993.      *
  1994.      * @param array $pStorageHash 
  1995.      * @access public
  1996.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  1997.      */
  1998.     function storeLogo( &$pStorageHash ) {
  1999.         return( $this->storeUserImage( $pStorageHash, 'logo' ));
  2000.     }
  2001.  
  2002.     /**
  2003.      * storeUserImage
  2004.      *
  2005.      * @param array $pStorageHash 
  2006.      * @param string $pType 
  2007.      * @access public
  2008.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  2009.      */
  2010.     function storeUserImage( &$pStorageHash, $pType = 'portrait' ) {
  2011.         if( $this->isValid() && count( $pStorageHash ) ) {
  2012.             // don't do the content thing
  2013.             $pStorageHash['skip_content_store'] = TRUE;
  2014.  
  2015.             // setup the hash for central storage functions
  2016.             $pStorageHash['no_perm_check'] = TRUE;
  2017.             $pStorageHash['_files_override'][$pType] = $pStorageHash['upload'];
  2018.             $pStorageHash['_files_override'][$pType]['max_width']     = constant( strtoupper( $pType )."_MAX_DIM" );
  2019.             $pStorageHash['_files_override'][$pType]['max_height']    = constant( strtoupper( $pType )."_MAX_DIM" );
  2020.             $pStorageHash['_files_override'][$pType]['attachment_id'] = !empty( $this->mInfo["{$pType}_attachment_id"] ) ? $this->mInfo["{$pType}_attachment_id"] : NULL;
  2021.             $pStorageHash['_files_override'][$pType]['user_id']       = $this->mUserId;
  2022.             if( LibertyMime::store( $pStorageHash )) {
  2023.                 $file = $pStorageHash['upload_store']['files'][$pType];
  2024.                 if( empty( $this->mInfo["{$pType}_attachment_id"] ) || $this->mInfo["{$pType}_attachment_id"] != $file['attachment_id'] ) {
  2025.                     $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `{$pType}_attachment_id` = ? WHERE `user_id`=?";
  2026.                     $result = $this->mDb->query( $query, array( $file['attachment_id'], $this->mUserId ) );
  2027.                     $this->mInfo["{$pType}_attachment_id"] = $file['attachment_id'];
  2028.                     $pStorageHash["{$pType}_file_name"] = $file['upload']['dest_branch'];
  2029.                 }
  2030.             } else {
  2031.                 $this->mErrors["{$pType}_file"] = 'File '.$pStorageHash['upload_store']['files'][$pType]['name'].' could not be stored.';
  2032.             }
  2033.         }
  2034.         return( count( $this->mErrors ) == 0 );
  2035.     }
  2036.  
  2037.     /**
  2038.      * purgeImage
  2039.      *
  2040.      * @param array $pType 
  2041.      * @access public
  2042.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  2043.      */
  2044.     function purgeImage( $pType ) {
  2045.         if( $this->isValid() && @$this->verifyId( $this->mInfo[$pType.'_attachment_id'] ) ) {
  2046.             $this->mDb->StartTrans();
  2047.             $query = "UPDATE `".BIT_DB_PREFIX."users_users` SET `".$pType."_attachment_id` = NULL WHERE `user_id`=?";
  2048.             $result = $this->mDb->query( $query, array( $this->mUserId ) );
  2049.             if( $this->expungeAttachment( $this->getField( $pType.'_attachment_id' ) ) ) {
  2050.                 unset( $this->mInfo[$pType.'_file_name'] );
  2051.                 unset( $this->mInfo[$pType.'_attachment_id'] );
  2052.                 unset( $this->mInfo[$pType.'_url'] );
  2053.             }
  2054.             $this->mDb->CompleteTrans();
  2055.             return TRUE;
  2056.         }
  2057.     }
  2058.  
  2059.     /**
  2060.      * purgePortrait
  2061.      *
  2062.      * @access public
  2063.      * @return TRUE on success, FALSE on failure
  2064.      */
  2065.     function purgePortrait() {
  2066.         return $this->purgeImage( 'portrait' );
  2067.     }
  2068.  
  2069.  
  2070.     /**
  2071.      * purgeAvatar
  2072.      *
  2073.      * @access public
  2074.      * @return TRUE on success, FALSE on failure
  2075.      */
  2076.     function purgeAvatar() {
  2077.         return $this->purgeImage( 'avatar' );
  2078.     }
  2079.  
  2080.  
  2081.     /**
  2082.      * purgeLogo
  2083.      *
  2084.      * @access public
  2085.      * @return TRUE on success, FALSE on failure
  2086.      */
  2087.     function purgeLogo() {
  2088.         return $this->purgeImage( 'logo' );
  2089.     }
  2090.     // }}}
  2091.  
  2092.     // {{{ ==================== Watches ====================
  2093.     // TODO: clean up all watch functions. these are old and messy. - xing - Thursday Oct 16, 2008   11:07:55 CEST
  2094.     /**
  2095.      * storeWatch
  2096.      *
  2097.      * @param array $pEvent 
  2098.      * @param array $pObject 
  2099.      * @param array $pType 
  2100.      * @param array $pTitle 
  2101.      * @param array $pUrl 
  2102.      * @access public
  2103.      * @return TRUE on success, FALSE on failure
  2104.      */
  2105.     function storeWatch( $pEvent, $pObject, $pType, $pTitle, $pUrl ) {
  2106.         global $userlib;
  2107.         if( $this->isValid() ) {
  2108.             $hash = md5( uniqid( '.' ));
  2109.             $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?";
  2110.             $this->mDb->query($query,array( $this->mUserId, $pEvent, $pObject ) );
  2111.             $query = "INSERT INTO `".BIT_DB_PREFIX."users_watches`(`user_id` ,`event` ,`object` , `email`, `hash`, `watch_type`, `title`, `url`) VALUES(?,?,?,?,?,?,?,?)";
  2112.             $this->mDb->query( $query, array( $this->mUserId, $pEvent, $pObject, $this->mInfo['email'], $hash, $pType, $pTitle, $pUrl ) );
  2113.             return TRUE;
  2114.         }
  2115.     }
  2116.  
  2117.     /**
  2118.      * getWatches
  2119.      *
  2120.      * @param string $pEvent 
  2121.      * @access public
  2122.      * @return TRUE on success, FALSE on failure
  2123.      */
  2124.     function getWatches( $pEvent = '' ) {
  2125.         $ret = NULL;
  2126.         if( $this->isValid() ) {
  2127.             $mid = '';
  2128.             $bindvars=array( $this->mUserId );
  2129.             if ($pEvent) {
  2130.                 $mid = " and `event`=? ";
  2131.                 $bindvars[]=$pEvent;
  2132.             }
  2133.  
  2134.             $query = "select * from `".BIT_DB_PREFIX."users_watches` where `user_id`=? $mid";
  2135.             $result = $this->mDb->query($query,$bindvars);
  2136.             $ret = array();
  2137.  
  2138.             while ($res = $result->fetchRow()) {
  2139.                 $ret[] = $res;
  2140.             }
  2141.         }
  2142.         return $ret;
  2143.     }
  2144.  
  2145.     /**
  2146.      * getEventWatches
  2147.      *
  2148.      * @param array $pEvent 
  2149.      * @param array $object 
  2150.      * @access public
  2151.      * @return TRUE on success, FALSE on failure
  2152.      */
  2153.     function getEventWatches( $pEvent, $pObject ) {
  2154.         $ret = NULL;
  2155.         if( $this->isValid() ) {
  2156.             $query = "SELECT * FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?";
  2157.             $result = $this->mDb->query($query,array( $this->mUserId, $pEvent, $pObject ) );
  2158.             if ( $result->numRows() ) {
  2159.                 $ret = $result->fetchRow();
  2160.             }
  2161.         }
  2162.         return $ret;
  2163.     }
  2164.  
  2165.     /**
  2166.      * get_event_watches
  2167.      *
  2168.      * @param array $pEvent 
  2169.      * @param array $pObject 
  2170.      * @access public
  2171.      * @return TRUE on success, FALSE on failure
  2172.      */
  2173.     function get_event_watches( $pEvent, $pObject ) {
  2174.         $ret = array();
  2175.  
  2176.         $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`=?";
  2177.         $result = $this->mDb->query( $query,array( $pEvent,$pObject ));
  2178.  
  2179.         if( !$result->numRows() ) {
  2180.             return $ret;
  2181.         }
  2182.  
  2183.         while ($res = $result->fetchRow()) {
  2184.             $ret[] = $res;
  2185.         }
  2186.  
  2187.         return $ret;
  2188.     }
  2189.  
  2190.     /**
  2191.      * remove_user_watch_by_hash
  2192.      *
  2193.      * @param array $pHash 
  2194.      * @access public
  2195.      * @return TRUE on success, FALSE on failure
  2196.      */
  2197.     function remove_user_watch_by_hash( $pHash ) {
  2198.         $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `hash`=?";
  2199.         $this->mDb->query( $query,array( $pHash ));
  2200.     }
  2201.  
  2202.     /**
  2203.      * expungeWatch
  2204.      *
  2205.      * @param array $pEvent 
  2206.      * @param array $pObject 
  2207.      * @access public
  2208.      * @return TRUE on success, FALSE on failure
  2209.      */
  2210.     function expungeWatch( $pEvent, $pObject ) {
  2211.         if( $this->isValid() ) {
  2212.             $query = "DELETE FROM `".BIT_DB_PREFIX."users_watches` WHERE `user_id`=? AND `event`=? AND `object`=?";
  2213.             $this->mDb->query( $query, array( $this->mUserId, $pEvent, $pObject ));
  2214.         }
  2215.     }
  2216.  
  2217.     /**
  2218.      * get_watches_events
  2219.      *
  2220.      * @access public
  2221.      * @return TRUE on success, FALSE on failure
  2222.      */
  2223.     function get_watches_events() {
  2224.         $query = "select distinct `event` from `".BIT_DB_PREFIX."users_watches`";
  2225.         $result = $this->mDb->query($query,array());
  2226.         $ret = array();
  2227.         while ($res = $result->fetchRow()) {
  2228.             $ret[] = $res['event'];
  2229.         }
  2230.         return $ret;
  2231.     }
  2232.     // }}}
  2233.  
  2234.     /**
  2235.      * getUserAttachments
  2236.      *
  2237.      * @param array $pListHash 
  2238.      * @access public
  2239.      * @return list of attachments
  2240.      */
  2241.     function getUserAttachments( &$pListHash ) {
  2242.         $pListHash['user_id'] = $this->mUserId;
  2243.         $mime = new LibertyMime();
  2244.         return $mime->getAttachmentList( $pListHash );
  2245.     }
  2246.  
  2247.     // {{{ ==================== Favorites ====================
  2248.     /**
  2249.      * storeFavorite
  2250.      *
  2251.      * @param array $pContentId 
  2252.      * @access public
  2253.      * @return TRUE on success, FALSE on failure
  2254.      */
  2255.     function storeFavorite( $pContentId ) {
  2256.         $ret = FALSE;
  2257.         if( $this->isValid() && $this->verifyId( $pContentId )) {
  2258.             if( !$this->hasFavorite( $pContentId ) ){
  2259.                 $this->mDb->query( "INSERT INTO `".BIT_DB_PREFIX."users_favorites_map` ( `user_id`, `favorite_content_id` ) VALUES (?,?)", array( $this->mUserId, $pContentId ) );
  2260.             }
  2261.             $ret = TRUE;
  2262.         }
  2263.         return( $ret );
  2264.     }
  2265.  
  2266.     function expungeFavorite( $pContentId ) {
  2267.         $ret = FALSE;
  2268.         if( $this->isValid() && $this->verifyId( $pContentId ) ) {
  2269.             $this->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."users_favorites_map` WHERE `user_id`=? AND `favorite_content_id`=?", array( $this->mUserId, $pContentId ) );
  2270.             $ret = TRUE;
  2271.         }
  2272.         return( $ret );
  2273.     }
  2274.  
  2275.     function hasFavorite( $pContentId ) {
  2276.         $ret = FALSE;
  2277.         $rslt = $this->mDb->getOne( "SELECT `favorite_content_id` FROM `".BIT_DB_PREFIX."users_favorites_map` WHERE `user_id`=? AND `favorite_content_id`=?", array( $this->mUserId, $pContentId ) );
  2278.         if( !is_null( $rslt ) ){
  2279.             $ret = TRUE;
  2280.         }
  2281.         return $ret;
  2282.     }
  2283.  
  2284.     /**
  2285.      * getFavorites
  2286.      *
  2287.      * @see LibertyContent::getContentList
  2288.      * @return Array of content data
  2289.      */
  2290.     function getFavorites(){
  2291.         $ret = NULL;
  2292.         if( $this->isRegistered() ){
  2293.             $listHash['user_favs'] = TRUE;
  2294.             $listHash['order_table'] = 'ufm.';
  2295.             $listHash['sort_mode'] = 'map_position_desc';
  2296.             $ret = $this->getContentList( $listHash );
  2297.         }
  2298.         return $ret;
  2299.     }
  2300.     // }}}
  2301.  
  2302.     /**
  2303.      * getUserId
  2304.      *
  2305.      * @access public
  2306.      * @return user id of currently loaded user
  2307.      */
  2308.     function getUserId() {
  2309.         return( $this->isValid() ? $this->mUserId : ANONYMOUS_USER_ID );
  2310.     }
  2311.  
  2312.     /**
  2313.      * getDisplayUrl
  2314.      *
  2315.      * @param array $pUserName 
  2316.      * @param array $pMixed 
  2317.      * @access public
  2318.      * @return URL to users homepage
  2319.      */
  2320.     public static function getDisplayUrlFromHash( &$pParamHash ) {
  2321.         if( function_exists( 'override_user_url' ) ) {
  2322.             $ret = override_user_url( $pParamHash );
  2323.         } else {
  2324.             global $gBitSystem;
  2325.  
  2326.             $rewrite_tag = $gBitSystem->isFeatureActive( 'pretty_urls_extended' ) ? 'view/':'';
  2327.  
  2328.             if ($gBitSystem->isFeatureActive( 'pretty_urls' )
  2329.             || $gBitSystem->isFeatureActive( 'pretty_urls_extended' ) ) {
  2330.                 $ret =  USERS_PKG_URL . $rewrite_tag;
  2331.                 $ret .= urlencode( $pParamHash['login'] );
  2332.             } else {
  2333.                 $ret =  USERS_PKG_URL . 'index.php?home=';
  2334.                 $ret .= urlencode( $pParamHash['login'] );
  2335.             }
  2336.         }
  2337.         return $ret;
  2338.     }
  2339.  
  2340.     /**
  2341.      * getDisplayLink
  2342.      *
  2343.      * @param array $pUserName 
  2344.      * @param array $pDisplayHash 
  2345.      * @access public
  2346.      * @return get a link to the the users homepage
  2347.      */
  2348.     public function getDisplayLink( $pLinkText=NULL, $pMixed=NULL, $pAnchor=NULL ) {
  2349.         return BitUser::getDisplayNameFromHash( TRUE, $pMixed );
  2350.     }
  2351.  
  2352.     /**
  2353.      * getTitle
  2354.      *
  2355.      * @param array $pHash 
  2356.      * @access public
  2357.      * @return get the users display name
  2358.      */
  2359.     public function getTitle( $pHash = NULL, $pDefault=TRUE ) {
  2360.         if( empty( $pHash ) && $this && $this->isValid() ) {
  2361.             $pHash = $this->mInfo;
  2362.         }
  2363.         return BitUser::getDisplayNameFromHash( FALSE, $pHash );
  2364.     }
  2365.  
  2366.     /**
  2367.      * Get user information for a particular user
  2368.      *
  2369.      * @param pUseLink return the information in the form of a url that links to the users information page
  2370.      * @param pHash todo - need explanation on how to use this...
  2371.      * @return display name or link to user information page
  2372.      ***/
  2373.     public static function getDisplayNameFromHash( $pUseLink=FALSE, $pHash ) {
  2374.         global $gBitSystem, $gBitUser;
  2375.         if( !empty( $pHash )) {
  2376.             if( !empty( $pHash['real_name'] ) && $gBitSystem->getConfig( 'users_display_name', 'real_name' ) == 'real_name' ) {
  2377.                 $displayName = $pHash['real_name'];
  2378.             } elseif( !empty( $pHash['user'] )) {
  2379.                 $displayName = $pHash['user'];
  2380.             } elseif( !empty( $pHash['login'] )) {
  2381.                 $displayName = $pHash['login'];
  2382.             } elseif( !empty( $pHash['email'] )) {
  2383.                 $displayName = substr( $pHash['email'], 0, strpos( $pHash['email'], '@' ));
  2384.             } else {
  2385.                 $displayName = $pHash['user_id'];
  2386.             }
  2387.  
  2388.             if( !empty( $pHash['user'] )) {
  2389.                 $iHomepage = $pHash['user'];
  2390.             } elseif( !empty( $pHash['login'] )) {
  2391.                 // user of 'login' is deprecated and eventually should go away!
  2392.                 $iHomepage = $pHash['login'];
  2393.             } elseif( BitBase::verifyId( $pHash['user_id'] )) {
  2394.                 $iHomepage = $pHash['user_id'];
  2395.             } elseif( !empty( $pHash['email'] )) {
  2396.                 $iHomepage = $pHash['email'];
  2397.             } else {
  2398.                 // this won't work right now, we need to alter userslib::interpret_home() to interpret a real name
  2399.                 $iHomepage = $pHash['real_name'];
  2400.             }
  2401.             if( empty( $pHash['users_information'] ) ) {
  2402.                 $pHash['users_information'] = $gBitSystem->mDb->getOne( "SELECT pref_value FROM liberty_content_prefs lcp INNER JOIN users_users uu ON (lcp.content_id=uu.content_id) WHERE uu.login=? AND pref_name='users_information'", array( $pHash['login'] ), 1, NULL, 86400 );
  2403.             }
  2404.  
  2405.             if( $pUseLink && $gBitUser->hasPermission( 'p_users_view_user_homepage' ) && (empty( $pHash['users_information'] ) || $pHash['users_information'] == 'public') ) {
  2406.                 $ret = '<a class="username" title="'.( !empty( $pHash['link_title'] ) ? $pHash['link_title'] : tra( 'Profile for' ).' '.htmlspecialchars( $displayName ))
  2407.                     .'" href="'.BitUser::getDisplayUrlFromHash( $pHash ).'">'
  2408.                     . htmlspecialchars( isset( $pHash['link_label'] ) ? $pHash['link_label'] : $displayName )
  2409.                     .'</a>';
  2410.             } else {
  2411.                 $ret = htmlspecialchars( $displayName );
  2412.             }
  2413.         } else {
  2414.             $ret = tra( "Anonymous" );
  2415.         }
  2416.  
  2417.         return $ret;
  2418.     }
  2419.  
  2420.     /**
  2421.      * Get user information for a particular user
  2422.      *
  2423.      * @param pUseLink return the information in the form of a url that links to the users information page
  2424.      * @param pHash todo - need explanation on how to use this...
  2425.      * @return display name or link to user information page
  2426.      ***/
  2427.     function getDisplayName( $pUseLink=FALSE, $pHash=NULL ) {
  2428.         $ret = NULL;
  2429.         if( empty( $pHash ) && !empty( $this ) && !empty( $this->mInfo )) {
  2430.             $pHash = &$this->mInfo;
  2431.         }
  2432.         return self::getDisplayNameFromHash( $pUseLink, $pHash );
  2433.     }
  2434.  
  2435.     /**
  2436.      * getRenderFile Returns include file that will
  2437.      *
  2438.      * @access public
  2439.      * @return the fully specified path to file to be included
  2440.      */
  2441.     function getRenderFile() {
  2442.         return USERS_PKG_PATH."display_bituser_inc.php";
  2443.     }
  2444.  
  2445.     /**
  2446.      * getSelectionList get a list of users that can be used in dropdown lists in forms to choose from
  2447.      *
  2448.      * @access public
  2449.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  2450.      */
  2451.     function getSelectionList() {
  2452.         $query = "
  2453.             SELECT uu.`user_id`, uu.`login`, uu.`real_name`
  2454.             FROM `".BIT_DB_PREFIX."users_users` uu
  2455.             ORDER BY uu.`login`";
  2456.         $result = $this->mDb->query( $query );
  2457.         $ret = array();
  2458.         while( $res = $result->fetchRow()) {
  2459.             $ret[$res['user_id']] = $res['login'].(( !empty( $res['real_name'] ) && $res['real_name'] != $res['login'] ) ? ' - '.$res['real_name'] : '' );
  2460.         }
  2461.  
  2462.         return $ret;
  2463.     }
  2464.  
  2465.     /**
  2466.      * getList get a list of users
  2467.      *
  2468.      * @param array $pParamHash 
  2469.      * @access public
  2470.      * @return array of users
  2471.      */
  2472.     function getList( &$pParamHash ) {
  2473.         global $gBitSystem, $gBitUser;
  2474.         if( empty( $pParamHash['sort_mode'] )) {
  2475.             $pParamHash['sort_mode'] = 'registration_date_desc';
  2476.         }
  2477.  
  2478.         LibertyContent::prepGetList( $pParamHash );
  2479.  
  2480.         $selectSql = $joinSql = $whereSql = '';
  2481.         $bindVars = array();
  2482.         array_push( $bindVars, 'bituser' );
  2483.         $this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, NULL, $pParamHash );
  2484.  
  2485.         // limit search to users with a specific language
  2486.         if( !empty( $pParamHash['lang_code'] ) ) {
  2487.             $joinSql .= " INNER JOIN `".BIT_DB_PREFIX."liberty_content_prefs` lcp ON ( lcp.`content_id`=uu.`content_id` AND lcp.`pref_name`='bitlanguage' )";
  2488.             $whereSql .= " AND lcp.`pref_value`=? ";
  2489.             $bindVars[] = $pParamHash['lang_code'];
  2490.         }
  2491.  
  2492.         if( !$gBitUser->hasPermission( 'p_users_admin' ) ) {
  2493.             $joinSql .= " LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content_prefs` lcp2 ON ( lcp2.`content_id`=uu.`content_id` AND lcp2.`pref_name`='users_information' )";
  2494.             $whereSql .= " AND (lcp2.`pref_value` IS NULL OR lcp2.`pref_value`='public')";
  2495.         }
  2496.  
  2497.         // limit search to users with a specific IP
  2498.         if( !empty( $pParamHash['ip'] ) ) {
  2499.             $joinSql .= " LEFT OUTER JOIN `".BIT_DB_PREFIX."users_cnxn` uc ON ( uu.`user_id`=uc.`user_id`) ";
  2500.             $ips = explode( ',', $pParamHash['ip'] );
  2501.             $whereSql .= ' AND ( ';
  2502.             do {
  2503.                 $ip = array_pop( $ips );
  2504.                 if( strpos( $ip, '%' ) ) {
  2505.                     $whereSql .= " uc.`ip` LIKE ? ";
  2506.                 } else {
  2507.                     $whereSql .= " uc.`ip`=? ";
  2508.                 }
  2509.                 $bindVars[] = $ip;
  2510.                 if( !empty( $ips ) ) {
  2511.                     $whereSql .= ' OR ';
  2512.                 }
  2513.             } while( $ips );
  2514.             $whereSql .= ' ) ';
  2515.         }
  2516.  
  2517.         // limit to registrations over a time period like 'YYYY-MM-DD' or 'Y \Week W' or anything convertible by SQLDate
  2518.         if( !empty( $pParamHash['period'] ) ) {
  2519.             $sqlPeriod = $this->mDb->SQLDate( $this->mDb->getPeriodFormat( $pParamHash['period'] ), $this->mDb->SQLIntToTimestamp( 'registration_date' ));
  2520.             $whereSql .= ' AND '.$sqlPeriod.'=?';
  2521.             $bindVars[] = $pParamHash['timeframe'];
  2522.         }
  2523.  
  2524.         // lets search for a user
  2525.         if ( $pParamHash['find'] ) {
  2526.             $whereSql .= " AND ( UPPER( uu.`login` ) LIKE ? OR UPPER( uu.`real_name` ) LIKE ? OR UPPER( uu.`email` ) LIKE ? ) ";
  2527.             $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%';
  2528.             $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%';
  2529.             $bindVars[] = '%'.strtoupper( $pParamHash['find'] ).'%';
  2530.         }
  2531.  
  2532.         if( $gBitSystem->isPackageActive( 'stats' ) ) {
  2533.             $joinSql .= " LEFT OUTER JOIN `".BIT_DB_PREFIX."stats_referer_users_map` srum ON (srum.`user_id`=uu.`user_id`)
  2534.                           LEFT OUTER JOIN `".BIT_DB_PREFIX."stats_referer_urls` sru ON (srum.`referer_url_id`=sru.`referer_url_id`)";
  2535.             $selectSql .= ", sru.`referer_url`";
  2536.             if( !empty( $pParamHash['referer'] ) ) {
  2537.                 if( $pParamHash['referer'] == 'none' ) {
  2538.                     $whereSql .= " AND `referer_url` IS NULL";
  2539.                 } else {
  2540.                     $whereSql .= " AND `referer_url` LIKE ?";
  2541.                     $bindVars[] = '%'.strtolower( $pParamHash['find'] ).'%';
  2542.                 }
  2543.             }
  2544.         }
  2545.  
  2546.         // Return an array of users indicating name, email, last changed pages, versions, last_login
  2547.         $query = "
  2548.             SELECT uu.*, lc.`content_status_id`, lf_ava.`file_name` AS `avatar_file_name`, lf_ava.`mime_type` AS `avatar_mime_type`, la_ava.`attachment_id` AS `avatar_attachment_id` $selectSql
  2549.             FROM `".BIT_DB_PREFIX."users_users` uu
  2550.                 INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (uu.`content_id`=lc.`content_id`)
  2551.                 $joinSql
  2552.                 LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content_hits` lch ON ( lc.`content_id` = lch.`content_id` )
  2553.                 LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_attachments` la_ava ON ( uu.`avatar_attachment_id`=la_ava.`attachment_id` )
  2554.                 LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_files` lf_ava ON ( lf_ava.`file_id`=la_ava.`foreign_id` )
  2555.             WHERE lc.`content_type_guid` = ? $whereSql ORDER BY ".$this->mDb->convertSortmode( $pParamHash['sort_mode'] );
  2556.         $result = $this->mDb->query( $query, $bindVars, $pParamHash['max_records'], $pParamHash['offset'] );
  2557.  
  2558.         $ret = array();
  2559.         while( $res = $result->fetchRow() ) {
  2560.             // Used for pulling out dead/empty/spam accounts
  2561.             if( isset( $pParamHash['max_content_count'] ) && is_numeric( $pParamHash['max_content_count'] ) ) {
  2562.                 $contentCount = $this->mDb->getOne( "SELECT COUNT(*) FROM  `".BIT_DB_PREFIX."liberty_content` lc INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON ( lc.`user_id`=uu.`user_id` ) WHERE uu.`user_id`=? AND `content_type_guid` != 'bituser'", array( $res['user_id'] ) );
  2563.                 if( $contentCount >  $pParamHash['max_content_count'] ) {
  2564.                     continue;
  2565.                 }
  2566.             }
  2567.  
  2568.             // Used for pulling out non-idle accounts or pigs
  2569.             if( isset( $pParamHash['min_content_count'] ) && is_numeric( $pParamHash['min_content_count'] ) ) {
  2570.                 $contentCount = $this->mDb->getOne( "SELECT COUNT(*) FROM  `".BIT_DB_PREFIX."liberty_content` lc INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON ( lc.`user_id`=uu.`user_id` ) WHERE uu.`user_id`=? AND `content_type_guid` != 'bituser'", array( $res['user_id'] ) );
  2571.                 if( $contentCount <  $pParamHash['min_content_count'] ) {
  2572.                     continue;
  2573.                 }
  2574.             }
  2575.  
  2576.             if( !empty( $res['referer_url'] )) {
  2577.                 if ( $gBitSystem->isPackageActive('stats') ) {
  2578.                     $res['short_referer_url'] = stats_referer_display_short($res['referer_url']);
  2579.                 }
  2580.             }
  2581.             if( !empty( $res['avatar_file_name'] )) {
  2582.                 $res['avatar_url'] = $this->getSourceUrl( array( 'attachment_id'=>$res['avatar_attachment_id'], 'mime_type'=>$res['avatar_mime_type'], 'file_name'=>$res['avatar_file_name'] ) );
  2583.                 $res['thumbnail_url'] = liberty_fetch_thumbnail_url( array(
  2584.                     'source_file' => $this->getSourceFile( array( 'sub_dir'=>$res['avatar_attachment_id'], 'user_id' => $res['user_id'], 'file_name'=>$res['avatar_file_name'], 'mime_type'=>$res['avatar_mime_type'], 'package'=>liberty_mime_get_storage_sub_dir_name( array( 'mime_type'=>$res['avatar_mime_type'], 'name'=>$res['avatar_file_name'] ) ) ) ),
  2585.                     'file_name' => $res['avatar_url'],
  2586.                     // TODO: Make this a preference
  2587.                     'size'         => 'avatar'
  2588.                 ));
  2589.             }
  2590.             $res["roles"] = $this->getRoles( $res['user_id'] );
  2591.             $ret[$res['user_id']] = $res;
  2592.         }
  2593.         $retval = array();
  2594.  
  2595.         $query = "
  2596.             SELECT COUNT(*) FROM `".BIT_DB_PREFIX."users_users` uu
  2597.                 INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (uu.`content_id`=lc.`content_id`) $joinSql
  2598.             WHERE lc.`content_type_guid` = ? $whereSql";
  2599.         $pParamHash["cant"] = $this->mDb->getOne( $query, $bindVars );
  2600.  
  2601.         LibertyContent::postGetList( $pParamHash );
  2602.  
  2603.         return $ret;
  2604.     }
  2605.  
  2606.     /**
  2607.      * getRoles
  2608.      *
  2609.      * @param array $pUserId 
  2610.      * @param array $pForceRefresh 
  2611.      * @access public
  2612.      * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  2613.      */
  2614.     function getRoles( $pUserId=NULL, $pForceRefresh = FALSE ) {
  2615.         $pUserId = !empty( $pUserId ) ? $pUserId : $this->mUserId;
  2616.         if( !isset( $this->cUserRoles[$pUserId] ) || $pForceRefresh ) {
  2617.             $query = "
  2618.                 SELECT ur.`role_id`, ur.`role_name`, ur.`user_id` as role_owner_user_id
  2619.                 FROM `".BIT_DB_PREFIX."users_roles_map` urm INNER JOIN `".BIT_DB_PREFIX."users_roles` ur ON (ur.`role_id`=urm.`role_id`)
  2620.                 WHERE urm.`user_id`=? OR urm.`role_id`=".ANONYMOUS_TEAM_ID;
  2621.             $ret = $this->mDb->getAssoc( $query, array(( int )$pUserId ));
  2622.             if( $ret ) {
  2623.                 foreach( array_keys( $ret ) as $roleId ) {
  2624.                     $res = array();
  2625.                     foreach( $res as $key=>$val) {
  2626.                         $ret[$key] = array( 'role_name' => $val );
  2627.                     }
  2628.                 }
  2629.             }
  2630.             // cache it
  2631.             $this->cUserRoles[$pUserId] = $ret;
  2632.             return $ret;
  2633.         } else {
  2634.             return $this->cUserRoles[$pUserId];
  2635.         }
  2636.     }
  2637.  
  2638.     /**
  2639.      * isValid
  2640.      *
  2641.      * @access public
  2642.      * @return TRUE if user is loaded
  2643.      */
  2644.     function isValid() {
  2645.         return ( $this->verifyId( $this->mUserId ) );
  2646.     }
  2647.  
  2648.     /**
  2649.      * isAdmin "PURE VIRTUAL BASE FUNCTION";
  2650.      *
  2651.      * @access public
  2652.      * @return FALSE 
  2653.      */
  2654.     function isAdmin() {
  2655.         return FALSE;
  2656.     }
  2657.  
  2658.     /**
  2659.      * isRegistered
  2660.      *
  2661.      * @access public
  2662.      * @return TRUE if user is registered, FALSE otherwise
  2663.      */
  2664.     function isRegistered() {
  2665.         return ( $this->mUserId > ANONYMOUS_USER_ID );
  2666.     }
  2667.  
  2668.     /**
  2669.      * verifyRegistered
  2670.      *
  2671.      * @access public
  2672.      * @return TRUE if user is registered, otherwise a login dialog is displayed
  2673.      */
  2674.     function verifyRegistered( $pMsg = "" ) {
  2675.         global $gBitSystem;
  2676.         if( !$this->isRegistered() ) {
  2677.             $gBitSystem->fatalPermission( "", $pMsg );
  2678.         }
  2679.         return TRUE;
  2680.     }
  2681.  
  2682.     /**
  2683.      * userExists
  2684.      *
  2685.      * @param array $pUserMixed 
  2686.      * @access public
  2687.      * @return TRUE on success, FALSE on failure
  2688.      */
  2689.     function userExists( $pUserMixed ) {
  2690.         $ret = FALSE;
  2691.         if ( is_array( $pUserMixed ) ) {
  2692.             if( $cur = current( $pUserMixed ) ) {
  2693.                 if( is_numeric( $cur ) ) {
  2694.                     $conditionSql = " `".key( $pUserMixed )."` ";
  2695.                 } else {
  2696.                     $conditionSql = " UPPER(`".key( $pUserMixed )."`)";
  2697.                 }
  2698.                 $query = "SELECT `user_id` FROM `".BIT_DB_PREFIX."users_users` WHERE $conditionSql = ?";
  2699.                 $ret = $this->mDb->getOne( $query, array( strtoupper( $cur ) ) );
  2700.             }
  2701.         }
  2702.         return $ret;
  2703.     }
  2704.  
  2705.     /**
  2706.      * Create an export hash from the data
  2707.      *
  2708.      * @access public
  2709.      * @return export data
  2710.      */
  2711.     function exportHash() {
  2712.         global $gBitSystem;
  2713.         $ret = array();
  2714.         if( $this->isValid() ) {
  2715.             $ret = array(
  2716.                 'user_id'         => $this->mUserId,
  2717.                 'content_id'     => $this->mContentId,
  2718.                 'real_name'     => $this->getField( 'real_name' ),
  2719.                 'email'             => $this->getField( 'email' ),
  2720.                 'uri'            => $this->getDisplayUri(),
  2721.                 'registration_date'     => date( DateTime::W3C, $this->getField('registration_date') ),
  2722.                 'last_login' => date( DateTime::W3C, $this->getField('last_login') ),
  2723.             );
  2724.             $ret['content_count'] = get_user_content_count( $this->mUserId );
  2725.             if( $gBitSystem->isPackageActive( 'stats' ) ) {
  2726.                 $ret['referer'] = $this->mDb->getOne( "SELECT sru.`referer_url` FROM `".BIT_DB_PREFIX."stats_referer_urls` sru INNER JOIN `".BIT_DB_PREFIX."stats_referer_users_map` srum ON (srum.`referer_url_id`=sru.`referer_url_id`) WHERE `user_id`=?", $this->mUserId );
  2727.             }
  2728.             $ret['ips'] = implode( ',', $this->mDb->getCol( "SELECT DISTINCT(`ip`) FROM `".BIT_DB_PREFIX."users_cnxn` uc WHERE `user_id`=?", $this->mUserId ) );
  2729.         }
  2730.         return $ret;
  2731.     }
  2732.  
  2733.     /**
  2734.      * userCollection
  2735.      *
  2736.      * @param array $pInput 
  2737.      * @param array $pReturn 
  2738.      * @access public
  2739.      * @return $pReturn 
  2740.      */
  2741.     public static function userCollection( $pInput, &$pReturn ) {
  2742.         global $gQueryUserId;
  2743.  
  2744.         if( empty( $pReturn['user_id'] )) {
  2745.             if( !empty( $gQueryUserId )) {
  2746.                 $pReturn['user_id'] = $gQueryUserId;
  2747.             } elseif( isset( $pInput['user_id'] ) ) {
  2748.                 $pReturn['user_id'] = $pInput['user_id'];
  2749.             }
  2750.         }
  2751.         if( @BitBase::verifyId( $pInput['role_id'] ) ) {
  2752.             $pReturn['role_id'] = $pInput['role_id'];
  2753.         }
  2754.         return;
  2755.     }
  2756.  
  2757.     public static function getUserObject( $pUserId ) {
  2758.         global $gBitSystem;
  2759.         $userClass = $gBitSystem->getConfig( 'user_class', 'BitPermUser' );
  2760.         if( $ret = new $userClass( $pUserId ) ) {
  2761.             $ret->load();
  2762.         }
  2763.         return $ret;
  2764.     }
  2765. }
  2766.  
  2767. function get_user_content_count( $pUserId ) {
  2768.     global $gBitDb;
  2769.     if( BitBase::verifyId( $pUserId ) ) {
  2770.         return $gBitDb->getOne( "SELECT COUNT(`content_id`) FROM `".BIT_DB_PREFIX."liberty_content` lc WHERE lc.`content_type_guid`!='bituser' AND lc.`user_id`=?", array( $pUserId ) );
  2771.     }
  2772. }
  2773.  
  2774.  
  2775. // {{{ ==================== Services ====================
  2776. function users_favs_content_list_sql( &$pObject, $pParamHash=NULL ){
  2777.     $ret = array();
  2778.     if( !empty( $pParamHash['user_favs'] ) ){
  2779.         // $ret['select_sql'] = "";
  2780.         $ret['join_sql'] = " INNER JOIN `".BIT_DB_PREFIX."users_favorites_map` ufm ON ( ufm.`favorite_content_id`=lc.`content_id` )";
  2781.         $ret['where_sql'] = " AND ufm.`user_id` = ?";
  2782.         $ret['bind_vars'][] = $pObject->mUserId;
  2783.     }
  2784.     return $ret;
  2785. }
  2786.  
  2787. function users_collection_sql( &$pObject, $pParamHash=NULL ){
  2788.     $ret = array();
  2789.     if( !empty( $pParamHash['role_id'] ) and BitBase::verifyId( $pParamHash['role_id'] ) ){
  2790.         // $ret['select_sql'] = "";
  2791.         $ret['join_sql'] = " INNER JOIN `".BIT_DB_PREFIX."users_roles_map` urm ON (ugm.`user_id`=uu.`user_id`)";
  2792.         $ret['where_sql'] = ' AND urm.`role_id` = ? ';
  2793.         $ret['bind_vars'][] = $pParamHash['role_id'];
  2794.     }
  2795.     return $ret;
  2796. }
  2797. // }}}
  2798.  
  2799. /* vim: :set fdm=marker : */

Documentation generated on Wed, 29 Jul 2015 13:57:25 +0000 by phpDocumentor 1.5.0-lsces