root/trunk/wifidog-auth/wifidog/classes/User.php @ 1421

Revision 1421, 48.6 KB (checked in by benoitg, 4 years ago)

Update my email address

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2
3
4/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5
6// +-------------------------------------------------------------------+
7// | WiFiDog Authentication Server                                     |
8// | =============================                                     |
9// |                                                                   |
10// | The WiFiDog Authentication Server is part of the WiFiDog captive  |
11// | portal suite.                                                     |
12// +-------------------------------------------------------------------+
13// | PHP version 5 required.                                           |
14// +-------------------------------------------------------------------+
15// | Homepage:     http://www.wifidog.org/                             |
16// | Source Forge: http://sourceforge.net/projects/wifidog/            |
17// +-------------------------------------------------------------------+
18// | This program is free software; you can redistribute it and/or     |
19// | modify it under the terms of the GNU General Public License as    |
20// | published by the Free Software Foundation; either version 2 of    |
21// | the License, or (at your option) any later version.               |
22// |                                                                   |
23// | This program is distributed in the hope that it will be useful,   |
24// | but WITHOUT ANY WARRANTY; without even the implied warranty of    |
25// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     |
26// | GNU General Public License for more details.                      |
27// |                                                                   |
28// | You should have received a copy of the GNU General Public License |
29// | along with this program; if not, contact:                         |
30// |                                                                   |
31// | Free Software Foundation           Voice:  +1-617-542-5942        |
32// | 59 Temple Place - Suite 330        Fax:    +1-617-542-2652        |
33// | Boston, MA  02111-1307,  USA       gnu@gnu.org                    |
34// |                                                                   |
35// +-------------------------------------------------------------------+
36
37/**
38 * @package    WiFiDogAuthServer
39 * @author     Benoit Grégoire <benoitg@coeus.ca>
40 * @copyright  2005-2006 Benoit Grégoire, Technologies Coeus inc.
41 * @version    Subversion $Id$
42 * @link       http://www.wifidog.org/
43 */
44
45/**
46 * Load required classes
47 */
48require_once ('classes/Network.php');
49require_once ('classes/InterfaceElements.php');
50require_once ('classes/ProfileTemplate.php');
51require_once ('classes/Profile.php');
52require_once ('classes/Permission.php');
53/**
54 * Abstract a User
55 *
56 * @package    WiFiDogAuthServer
57 * @author     Benoit Grégoire <benoitg@coeus.ca>
58 * @copyright  2005-2006 Benoit Grégoire, Technologies Coeus inc.
59 */
60class User implements GenericObject {
61    private $_row;
62    private $id;
63    /** Object cache for the object factory (getObject())*/
64    private static $instanceArray = array();
65
66    /** Instantiate a user object
67     * @param $id The user id of the requested user
68     * @return a User object, or null if there was an error
69     */
70    public static function &getObject($id) {
71        if(!isset(self::$instanceArray[$id]))
72        {
73            self::$instanceArray[$id] = new self($id);
74        }
75        return self::$instanceArray[$id];
76    }
77
78    static function createNewObject() {
79        echo "<h1>Use User::createUser() instead</h1>";
80    }
81    /** Get an interface to create a new object.
82     * @return html markup
83     */
84    public static function getCreateNewObjectUI() {
85        return null;
86    }
87
88    /** Process the new object interface.
89     *  Will       return the new object if the user has the credentials
90     * necessary (Else an exception is thrown) and and the form was fully
91     * filled (Else the object returns null).
92     * @return the node object or null if no new node was created.
93     */
94    static function processCreateNewObjectUI() {
95        return self :: createNewObject();
96    }
97    /**
98     * Instantiate the current user
99     *
100     * @return mixed A User object, or null if there was an error
101
102     */
103    public static function getCurrentUser() {
104        require_once ('classes/Session.php');
105        $session = Session::getObject();
106        $sessCurrentUserId = $session->get(SESS_USER_ID_VAR);
107        $user = null;
108        if(!empty($sessCurrentUserId)){
109            try {
110                $user = self :: getObject($sessCurrentUserId);
111                //$user = new User($session->get(SESS_USER_ID_VAR));
112            } catch (Exception $e) {
113                /**If any problem occurs, the user should be considered logged out*/
114                $session->set(SESS_USER_ID_VAR, null);
115            }
116        }
117        return $user;
118    }
119
120    /**
121     * Associates the user passed in parameter with the session
122     *
123     * This should NOT be called by anything except the Authenticators
124     *
125     * @param object $user User a user object, or null
126     *
127     * @return bool True if everything went well setting the session
128
129     */
130    public static function setCurrentUser($user) {
131
132        if (get_class($user) == 'User'){
133            $userId = $user->getId();
134            $passwordHash = $user->getPasswordHash();
135        }
136        else {
137            $userId = null;
138            $passwordHash = null;
139        }
140
141        try {
142            $session = Session::getObject();
143            $session->set(SESS_USER_ID_VAR, $userId);
144            $session->set(SESS_PASSWORD_HASH_VAR, $passwordHash);
145            return true;
146        } catch (Exception $e) {
147            return false;
148        }
149    }
150
151    /** Instantiate a user object
152     * @param $username The username of the user
153     * @param $account_origin Network:  The account origin
154     * @param &$errMsg An error message will be appended to this if the username is not empty, but the user doesn't exist.
155     * @return a User object, or null if there was an error
156     */
157    public static function getUserByUsernameAndOrigin($username, Network $account_origin, &$errMsg = null) {
158        $db = AbstractDb::getObject();
159        $object = null;
160
161        $username_str = $db->escapeString($username);
162        $account_origin_str = $db->escapeString($account_origin->getId());
163        $db->execSqlUniqueRes("SELECT user_id FROM users WHERE username = '$username_str' AND account_origin = '$account_origin_str'", $user_info, false);
164
165        if ($user_info != null) {
166            $object = self::getObject($user_info['user_id']);
167        }
168        else if (!empty($username)) {
169            $errMsg .= sprintf(_("There is no user with username %s"),$username);
170        }
171        return $object;
172    }
173
174    /** Instantiate a user object
175     * @param $usernameOrEmail The username or the email address of the user
176     * @param &$errMsg An error message will be appended to this if the username is not empty, but the user doesn't exist.
177     * @return a User object, or null if there was an error
178     */
179    public static function getUserByUsernameOrEmail($usernameOrEmail, &$errMsg = null) {
180        $db = AbstractDb::getObject();
181        $object = null;
182
183        $usernameOrEmail_str = $db->escapeString($usernameOrEmail);
184        $db->execSqlUniqueRes("SELECT user_id FROM users WHERE username ILike '$usernameOrEmail_str' OR email ILike '$usernameOrEmail_str'", $user_info, false);
185
186        if ($user_info != null) {
187            $object = self::getObject($user_info['user_id']);
188        }
189        else if (!empty($usernameOrEmail)) {
190            $errMsg .= sprintf(_("There is no user with username or email %s"),$usernameOrEmail);
191        }
192        return $object;
193    }
194 
195
196   /** Instantiate a user object
197     * @param $url The OpenId url
198     * @return a User object, or null if none matched
199     */
200    public static function getUserByOpenIdUrl($url) {
201        $db = AbstractDb::getObject();
202        $object = null;
203
204        $url_str = $db->escapeString($url);
205        $db->execSqlUniqueRes("SELECT user_id FROM users WHERE open_id_url = '$url_str'", $user_rows, false);
206
207        if ($user_rows != null) {
208            $object = self::getObject($user_rows[0]['user_id']);
209        }
210        return $object;
211    }
212    /** Instantiate a user object
213     * @param $email The email of the user
214     * @param $account_origin Network:  The account origin
215     * @return a User object, or null if there was an error
216     */
217    public static function getUserByEmailAndOrigin($email, Network $account_origin) {
218        $db = AbstractDb::getObject();
219        $object = null;
220
221        $email_str = $db->escapeString($email);
222        $account_origin_str = $db->escapeString($account_origin->getId());
223        $db->execSqlUniqueRes("SELECT user_id FROM users WHERE email = '$email_str' AND account_origin = '$account_origin_str'", $user_info, false);
224
225        if ($user_info != null)
226        $object = self::getObject($user_info['user_id']);
227        return $object;
228    }
229
230    /** Returns the hash of the password suitable for storing or comparing in the database.  This hash is the same one as used in NoCat
231     * @return The 32 character hash.
232     */
233    public static function passwordHash($password) {
234        /**
235         * utf8_decode is used for backward compatibility with old passwords
236         * containing special characters.
237         * Conversion from UTF-8 to ISO-8859-1 is done to match the MD5 hash
238         */
239        return base64_encode(pack("H*", md5(utf8_decode($password))));
240    }
241
242    /** Create a new User in the database
243     * @param $id The id to be given to the new user
244     * @return the newly created User object, or null if there was an error
245     */
246    static function createUser($id, $username, Network $account_origin, $email, $password) {
247        $db = AbstractDb::getObject();
248
249        $object = null;
250        $id_str = $db->escapeString($id);
251        $username_str = $db->escapeString($username);
252        $account_origin_str = $db->escapeString($account_origin->getId());
253        $email_str = $db->escapeString($email);
254
255        $password_hash = $db->escapeString(User :: passwordHash($password));
256        $status = ACCOUNT_STATUS_VALIDATION;
257        $token = User :: generateToken();
258
259        $db->execSqlUpdate("INSERT INTO users (user_id,username, account_origin,email,pass,account_status,validation_token,reg_date) VALUES ('$id_str','$username_str','$account_origin_str','$email_str','$password_hash','$status','$token',CURRENT_TIMESTAMP)");
260
261        $object = self::getObject($id);
262        return $object;
263    }
264
265    /*    public static function purgeUnvalidatedUsers($days_since_creation)
266     {
267     $db = AbstractDb::getObject();
268     $days_since_creation = $db->escapeString($days_since_creation);
269
270     //$db->execSqlUpdate("INSERT INTO users (user_id,username, account_origin,email,pass,account_status,validation_token,reg_date) VALUES ('$id_str','$username_str','$account_origin_str','$email_str','$password_hash','$status','$token',CURRENT_TIMESTAMP)");
271     }*/
272
273    /** @param $object_id The id of the user */
274    function __construct($object_id) {
275        $db = AbstractDb::getObject();
276        $this->mDb = & $db;
277        $object_id_str = $db->escapeString($object_id);
278        $sql = "SELECT * FROM users WHERE user_id='{$object_id_str}'";
279        $db->execSqlUniqueRes($sql, $row, false);
280        if ($row == null) {
281            throw new Exception(sprintf(_("User id: %s could not be found in the database"), $object_id_str));
282        }
283        $this->_row = $row;
284        $this->id = $row['user_id'];
285    } //End class
286
287    function getId() {
288        return $this->id;
289    }
290
291    /** Gets the Network to which the user belongs
292     * @return Network object (never returns null)
293     */
294    public function getNetwork() {
295        return Network :: getObject($this->_row['account_origin']);
296    }
297
298    /** Get a user display suitable for a user list.  Will include link to the user profile. */
299    function getListUI() {
300        $html = '';
301        if ($this->isSplashOnlyUser()) {
302            $html .= _("Guest");
303        }
304        else {
305            $nickname = null;
306            $avatar = null;
307            $profile = $this->getAllProfiles();
308            if(!empty($profile)) {
309                // Use the first profile for now
310                $profile = $profile[0];
311
312                $nickname_fields = $profile->getFieldsBySemanticId("foaf:nick");
313                // Try using the first nickname available
314                if(!empty($nickname_fields)) {
315                    $nickname_content = $nickname_fields[0]->getContentField();
316                    if(!empty($nickname_content)) {
317                        // Force non-verbose output
318                        $str = $nickname_content->__toString(false);
319                        if(!empty($str))
320                        $nickname = $str;
321                    }
322                }
323
324                $avatar_fields = $profile->getFieldsBySemanticId("foaf:img");
325                // Try using the first avatar available
326                if(!empty($avatar_fields)) {
327                    $avatar_content = $avatar_fields[0]->getContentField();
328                    if(!empty($avatar_content)) {
329                        $avatar = $avatar_content->getUserUI();
330                    }
331                }
332            }
333
334
335
336            // Display the avatar
337            if(empty($avatar))
338            $html .= Avatar::getDefaultUserUI();
339            else
340            $html .= $avatar;
341
342            // Display the nickname or the username
343            $profiles=$this->getAllProfiles();
344            if($profiles){
345                $html .= "<a href='".BASE_URL_PATH."profile/?user_id=".$this->getId()."' title='".htmlentities(_("View this user's profile."), ENT_QUOTES)."' class='user_nickname'>";
346            }
347            if(empty($nickname))
348            $html .= $this->getUserName();
349            else
350            $html .= $nickname;
351            if($profiles){
352                $html .= "</a>\n";
353            }
354            $profileTemplates = $this->getNetwork()->getAllProfileTemplates();
355            /*if($this==User::getCurrentUser() && $profileTemplates) {
356             $html .= "<div class='user_edit_profile_link'><br/>(<a href='".BASE_SSL_PATH."admin/generic_object_admin.php?object_id=".$this->getId()."&object_class=User&action=edit'>"._("edit profile")."</a>)</div>";
357             }*/
358
359        }
360        return $html;
361    }
362
363    function getOpenIdUrl() {
364        return $this->_row['open_id_url'];
365    }
366
367    function getUsername() {
368        return $this->_row['username'];
369    }
370
371    /** Set the user's username
372     * @param $value The new value
373     * @return true on success, false on failure
374     * @throws exception if the user tries to set a duplicate username
375     */
376    function setUsername($value) {
377        $retval = true;
378        if ($value != $this->getUsername()) {
379            $db = AbstractDb::getObject();
380            $value = $db->escapeString($value);
381            $retval = @ $db->execSqlUpdate("UPDATE users SET username = '{$value}' WHERE user_id='{$this->id}'", false);
382            if (!$retval) {
383                throw new exception(sprintf(_("Sorry, the username %s is not available"), $value));
384            }
385            $this->refresh();
386        }
387        return $retval;
388    }
389
390    /** Add profile template to this user */
391    public function addProfile(Profile $profile) {
392        $db = AbstractDb::getObject();
393        $profile_id = $db->escapeString($profile->getId());
394        $sql = "INSERT INTO user_has_profiles (user_id, profile_id) VALUES ('$this->id','$profile_id')";
395        return $db->execSqlUpdate($sql, false);
396    }
397
398    /** Remove profile template from this user */
399    public function removeProfile(Profile $profile) {
400        $db = AbstractDb::getObject();
401        $profile_id = $db->escapeString($profile->getId());
402        $sql = "DELETE FROM user_has_profiles WHERE user_id='$this->id' AND profile_id='$profile_id'";
403        return $db->execSqlUpdate($sql, false);
404    }
405
406    /**Get an array of all Profiles linked to this user
407     * @return an array of Profile or an empty arrray */
408    public function getAllProfiles() {
409        $db = AbstractDb::getObject();
410        $retval = array ();
411        $profile_rows = null;
412        $sql = "SELECT profile_id FROM user_has_profiles NATURAL JOIN profiles WHERE user_id='$this->id' ORDER BY creation_date";
413        $db->execSql($sql, $profile_rows, false);
414        if ($profile_rows != null) {
415            foreach ($profile_rows as $profile_row) {
416                $retval[] = Profile :: getObject($profile_row['profile_id']);
417            }
418        }
419        return $retval;
420    }
421
422    public function getEmail() {
423        return $this->_row['email'];
424    }
425
426    public function setEmail($email) {
427        $email_str = $this->mDb->escapeString($email);
428        if (!($update = $this->mDb->execSqlUpdate("UPDATE users SET email='{$email_str}' WHERE user_id='{$this->id}'"))) {
429            throw new Exception(_("Could not update email address."));
430        }
431        $this->_row['email'] = $email; // unescaped
432    }
433
434    function setIsInvisible($value) {
435        $retval = true;
436        if ($value != $this->isAdvertised()) {
437            $db = AbstractDb::getObject();
438            $value ? $value = 'TRUE' : $value = 'FALSE';
439            $retval = $db->execSqlUpdate("UPDATE users SET is_invisible = {$value} WHERE user_id = '{$this->getId()}'", false);
440            $this->refresh();
441        }
442        return $retval;
443    }
444
445    public function isInvisible() {
446        return (($this->_row['is_invisible'] == 't') ? true : false);
447    }
448
449    /**What locale (language) does the user prefer? */
450    public function getPreferedLocale() {
451        $session = Session::getObject();
452        $locale = $this->_row['prefered_locale'];
453        if (empty ($locale) && !empty ($session))
454        $locale = $session->get(SESS_LANGUAGE_VAR);
455        if (empty ($locale))
456        $locale = DEFAULT_LANG;
457        return $locale;
458    }
459
460    public function setPreferedLocale($locale) {
461        $locale_str = $this->mDb->escapeString($locale);
462        if (!($update = $this->mDb->execSqlUpdate("UPDATE users SET prefered_locale='{$locale_str}' WHERE user_id='{$this->id}'"))) {
463            throw new Exception(_("Could not update username locale."));
464        }
465        $this->_row['prefered_locale'] = $locale;
466    }
467
468    /** get the hashed password stored in the database */
469    public function getPasswordHash() {
470        return $this->_row['pass'];
471    }
472
473    /** Get the account status.
474     * @return Possible values are listed in common.php
475     */
476    function getAccountStatus() {
477        return $this->_row['account_status'];
478    }
479
480    function setAccountStatus($status) {
481        $db = AbstractDb::getObject();
482        if($status != $this->getAccountStatus()) {
483            $status_str = $db->escapeString($status);
484            if (!($update = $db->execSqlUpdate("UPDATE users SET account_status='{$status_str}' WHERE user_id='{$this->id}'"))) {
485                throw new Exception(_("Could not update status."));
486            }
487            $this->_row['account_status'] = $status;
488        }
489    }
490
491    /** Is the user valid?  Valid means that the account is validated or hasn't exhausted it's validation period.
492     $errmsg: Returs the reason why the account is or isn't valid */
493    function isUserValid(& $errmsg = null) {
494        global $account_status_to_text;
495        $db = AbstractDb::getObject();
496        $retval = false;
497        $account_status = $this->getAccountStatus();
498        if ($account_status == ACCOUNT_STATUS_ALLOWED) {
499            $retval = true;
500        } else
501        if ($account_status == ACCOUNT_STATUS_VALIDATION) {
502            $sql = "SELECT CASE WHEN ((CURRENT_TIMESTAMP - reg_date) > networks.validation_grace_time) THEN true ELSE false END AS validation_grace_time_expired, EXTRACT(EPOCH FROM networks.validation_grace_time) as validation_grace_time FROM users  JOIN networks ON (users.account_origin = networks.network_id) WHERE (user_id='{$this->id}')";
503            $db->execSqlUniqueRes($sql, $user_info, false);
504
505            if ($user_info['validation_grace_time_expired'] == 't') {
506                $errmsg = sprintf(_("Sorry, your %.0f minutes grace period to retrieve your email and validate your account has now expired. You will have to connect to the internet and validate your account from another location."), $user_info['validation_grace_time']/60);
507                $retval = false;
508            } else {
509                $errmsg = _("Your account is currently valid.");
510                $retval = true;
511            }
512        } else {
513            $errmsg = _("Sorry, your account is not valid: ") . $account_status_to_text[$account_status];
514            $retval = false;
515        }
516        return $retval;
517    }
518
519    public function DEPRECATEDisSuperAdmin() {
520        $db = AbstractDb::getObject();
521        //$this->session->dump();
522
523        $db->execSqlUniqueRes("SELECT * FROM users JOIN server_stakeholders USING (user_id) WHERE (users.user_id='$this->id')", $user_info, false);
524        if (!empty ($user_info)) {
525            return true;
526        } else {
527            return false;
528        }
529
530    }
531
532    /** Is this user the Splash Only User() */
533    public function isSplashOnlyUser() {
534        if ($this->_row['username'] == "SPLASH_ONLY_USER") {
535            return true;
536        } else {
537            return false;
538        }
539    }
540
541    function getValidationToken() {
542        return $this->_row['validation_token'];
543    }
544
545    /** Retrieves the connection history necessary for abuse control
546
547    * @return false if abuse control is disabled */
548
549    static function getAbuseControlConnectionHistory($user = null, $mac = null, $node = null) {
550        if (!$user) {
551            $user = User::getCurrentUser();
552        }
553        if (!$node) {
554            $node = Node::getCurrentNode();//Maybe this should be getCurrentRealNode, but it would make debuging harder
555        }
556        $network = $node->getNetwork();
557
558        $db = AbstractDb::getObject();
559
560        if ($network->getConnectionLimitWindow()) {
561            //$sql =  " SELECT * from connections \n";//For debugging
562            $sql =  " SELECT \n";
563            $sql .= " SUM (incoming+outgoing) AS network_total_bytes, \n";
564            $sql .= " SUM (CASE WHEN node_id = '".$node->getId()."' THEN (incoming+outgoing) END) AS node_total_bytes, \n";
565            $sql .= " SUM (COALESCE(timestamp_out,last_updated) - timestamp_in) AS network_duration, \n";
566            $sql .= " SUM (CASE WHEN node_id = '".$node->getId()."' THEN (COALESCE(timestamp_out,last_updated) - timestamp_in) END) AS node_duration \n";//For real //The coalesce is to make sure the substraction returns a value for active conections, since active connections do not yet have a timestamp_out.  Do NOT coalesce with CURRENT_TIMESTAMP, it could cause real problems for users in case of gateway crash.
567            $sql .= " FROM connections \n";//For real
568            $sql .= " JOIN nodes USING (node_id) \n";
569            $sql .= " JOIN networks USING (network_id) \n";
570            $sql .= " JOIN tokens ON (tokens.token_id = connections.token_id) \n";
571            $sql .= " WHERE 1=1 \n";
572
573            if ($mac) {
574                //Catch some cheaters
575                $mac = $db->escapeString($mac);
576                $mac_sql_or = " OR connections.user_mac = '$mac' ";
577            }
578            else {
579                $mac_sql_or = null;
580            }
581            $sql .= " AND (connections.user_id = '".$user->getId()."' $mac_sql_or ) \n";
582
583            $sql .= " AND (timestamp_in > CURRENT_TIMESTAMP - networks.connection_limit_window OR tokens.token_status = '".TOKEN_INUSE."')";  //Get every connection within the window plus any still active connection, even if it started before the window
584
585            $subselect = $sql;
586            $sql =  " SELECT subselect.*, \n";
587            $sql .= " networks.connection_limit_window, \n";
588            $sql .= " networks.connection_limit_network_max_total_bytes, COALESCE(network_total_bytes>networks.connection_limit_network_max_total_bytes, false) AS network_total_bytes_exceeded_limit, \n";
589            $sql .= " networks.connection_limit_node_max_total_bytes, COALESCE(node_total_bytes>networks.connection_limit_node_max_total_bytes, false) AS node_total_bytes_exceeded_limit, \n";
590            $sql .= " networks.connection_limit_network_max_usage_duration, COALESCE(network_duration>networks.connection_limit_network_max_usage_duration, false) AS network_duration_exceeded_limit, \n";
591            $sql .= " networks.connection_limit_node_max_usage_duration, COALESCE(node_duration>networks.connection_limit_node_max_usage_duration, false) AS node_duration_exceeded_limit \n";
592
593            $sql .= " FROM ($subselect) AS subselect JOIN networks ON (network_id = '".$network->getId()."')";
594
595            $db->execSqlUniqueRes($sql, $connection_limits_report, false);
596            return $connection_limits_report;
597        }
598        else {
599            return false;
600        }
601    }
602
603    /** Takes the same paramaters as getAbuseControlConnectionHistory, and tells you if the abuse limits are busted
604
605    * @return false if abuse control respected, else a string containing the reason(s) for the bust  */
606
607    static function isAbuseControlViolated($user = null, $mac = null, $node = null) {
608        $retval = false;
609        $abuseControlReport = self::getAbuseControlConnectionHistory($user, $mac, $node);
610        if($abuseControlReport) {
611            if (!$user) {
612                $user = User::getCurrentUser();
613            }
614            //pretty_print_r($abuseControlReport);
615            if($node && Security::hasPermission(Permission::P('NODE_PERM_BYPASS_DYNAMIC_ABUSE_CONTROL'), $node, $user)) {
616                $retval = false;
617            }
618            else {
619                require_once('classes/Content/UIAllowedBandwidth/UIAllowedBandwidth.php');
620                if($abuseControlReport['network_total_bytes_exceeded_limit']=='t') {
621                    $retval .= sprintf(_("During the last %s period, you transfered %s bytes throughout the network, which exceeds the %s bytes limit for the entire network."), $abuseControlReport['connection_limit_window'], UIAllowedBandwidth::formatSize($abuseControlReport['network_total_bytes']), UIAllowedBandwidth::formatSize($abuseControlReport['connection_limit_network_max_total_bytes']));
622                }
623                if($abuseControlReport['node_total_bytes_exceeded_limit']=='t') {
624                    $retval .= sprintf(_("During the last %s period, you transfered %s bytes at this node, which exceeds the %s bytes limit for this node."), $abuseControlReport['connection_limit_window'], UIAllowedBandwidth::formatSize($abuseControlReport['node_total_bytes']), UIAllowedBandwidth::formatSize($abuseControlReport['connection_limit_node_max_total_bytes']));
625                }
626                if($abuseControlReport['network_duration_exceeded_limit']=='t') {
627                    $retval .= sprintf(_("During the last %s period, you were online for a duration of %s throughout the network, which exceeds the %s limit for the entire network."), $abuseControlReport['connection_limit_window'], $abuseControlReport['network_duration'], $abuseControlReport['connection_limit_network_max_usage_duration']);
628                }
629                if($abuseControlReport['node_duration_exceeded_limit']=='t') {
630                    $retval .= sprintf(_("During the last %s period, you were online for a duration of %s at this node, which exceeds the %s limit for this node."), $abuseControlReport['connection_limit_window'], $abuseControlReport['node_duration'], $abuseControlReport['connection_limit_node_max_usage_duration']);
631                }
632            }
633        }
634        return $retval;
635    }
636    /** Generate a token in the connection table so the user can actually use the internet
637    @return true on success, false on failure
638    */
639    function generateConnectionToken() {
640        if ($this->isUserValid()) {
641            $db = AbstractDb::getObject();
642            $session = Session::getObject();
643
644            $token = self :: generateToken();
645            if ($_SERVER['REMOTE_ADDR']) {
646                $node_ip = $db->escapeString($_SERVER['REMOTE_ADDR']);
647            }
648            if ($session && $node_ip && $session->get(SESS_NODE_ID_VAR)) {
649                //echo "$session && $node_ip && {$session->get(SESS_NODE_ID_VAR)}";
650                $node_id = $db->escapeString($session->get(SESS_NODE_ID_VAR));
651                $abuseControlFault = User::isAbuseControlViolated($this, null, Node::getObject($node_id));
652                if($abuseControlFault) {
653                    throw new Exception ($abuseControlFault);
654                }
655                /*
656                 * Delete all unused tokens for this user, so we don't fill the database
657                 * with them
658                 */
659                $sql = "DELETE FROM connections USING tokens "."WHERE tokens.token_id=connections.token_id AND token_status='".TOKEN_UNUSED."' AND user_id = '".$this->getId()."';\n";
660                // TODO:  Try to find a reusable token before creating a brand new one!
661
662                $sql .= "INSERT INTO tokens (token_owner, token_issuer, token_id, token_status) VALUES ('" . $this->getId() . "', '" . $this->getId() . "', '$token', '" . TOKEN_UNUSED . "');\n";
663                $sql .= "INSERT INTO connections (user_id, token_id, timestamp_in, node_id, node_ip, last_updated) VALUES ('" . $this->getId() . "', '$token', CURRENT_TIMESTAMP, '$node_id', '$node_ip', CURRENT_TIMESTAMP)";
664                $db->execSqlUpdate($sql, false);
665                $retval = $token;
666            }
667            else {
668                $retval = false;
669            }
670        }
671        else {
672            $retval = false;
673        }
674        return $retval;
675    }
676
677    function setPassword($password) {
678        $db = AbstractDb::getObject();
679
680        $new_password_hash = User :: passwordHash($password);
681        if (empty($password)) {
682            throw new Exception(_("Password cannot be empty."));
683        }
684
685        if (!($update = $db->execSqlUpdate("UPDATE users SET pass='$new_password_hash' WHERE user_id='{$this->id}'"))) {
686            throw new Exception(_("Could not change user's password."));
687        }
688        $this->_row['pass'] = $password;
689    }
690
691    function getAccountOrigin() {
692        return $this->_row['account_origin'];
693    }
694
695    /** Return all the users
696     */
697    static function getAllUsers() {
698        $db = AbstractDb::getObject();
699
700        $db->execSql("SELECT * FROM users", $objects, false);
701        if ($objects == null) {
702            throw new Exception(_("No users could not be found in the database"));
703        }
704        return $objects;
705    }
706
707    function sendLostUsername() {
708        $network = $this->getNetwork();
709        require_once ('classes/Mail.php');
710        $mail = new Mail();
711        $mail->setSenderName(_("Registration system"));
712        $mail->setSenderEmail($network->getValidationEmailFromAddress());
713        $mail->setRecipientEmail($this->getEmail());
714        $mail->setMessageSubject($network->getName() . _(" lost username request"));
715        $mail->setMessageBody(_("Hello,\nYou have requested that the authentication server send you your username:\nUsername: ") . $this->getUsername() . _("\n\nHave a nice day,\nThe Team"));
716        $mail->send();
717    }
718
719    function sendValidationEmail() {
720        if ($this->getAccountStatus() != ACCOUNT_STATUS_VALIDATION) {
721            throw new Exception(_("The user is not in validation period."));
722        } else {
723            if ($this->getValidationToken() == "") {
724                throw new Exception(_("The validation token is empty."));
725            } else {
726                $network = $this->getNetwork();
727                require_once ('classes/Mail.php');
728                $mail = new Mail();
729                $mail->setSenderName(_("Registration system"));
730                $mail->setSenderEmail($network->getValidationEmailFromAddress());
731                $mail->setRecipientEmail($this->getEmail());
732                $mail->setMessageSubject($network->getName() . _(" new user validation"));
733                $url = BASE_SSL_PATH . "validate.php?user_id=" . $this->getId() . "&token=" . $this->getValidationToken();
734                $mail->setMessageBody(_("Hello,\nPlease follow the link below to validate your account.\n") . $url . _("\n\nThank you,\nThe Team."));
735                $mail->send();
736            }
737        }
738    }
739
740    function sendLostPasswordEmail() {
741        $network = $this->getNetwork();
742        $new_password = $this->randomPass();
743        $this->setPassword($new_password);
744        require_once ('classes/Mail.php');
745        $mail = new Mail();
746        $mail->setSenderName(_("Registration system"));
747        $mail->setSenderEmail($network->getValidationEmailFromAddress());
748        $mail->setRecipientEmail($this->getEmail());
749        $mail->setMessageSubject($network->getName() . _(" new password request"));
750        $mail->setMessageBody(_("Hello,\nYou have requested that the authentication server send you a new password:\nUsername: ") . $this->getUsername() . _("\nPassword: ") . $new_password . _("\n\nHave a nice day,\nThe Team"));
751        $mail->send();
752    }
753
754    public static function emailExists($id) {
755        $db = AbstractDb::getObject();
756        $id_str = $db->escapeString($id);
757        $sql = "SELECT * FROM users WHERE email='{$id_str}'";
758        $db->execSqlUniqueRes($sql, $row, false);
759        return $row;
760    }
761
762    public static function randomPass() {
763        $rand_pass = ''; // makes sure the $pass var is empty.
764        for ($j = 0; $j < 3; $j++) {
765            $startnend = array (
766            'b',
767            'c',
768            'd',
769            'f',
770            'g',
771            'h',
772            'j',
773            'k',
774            'l',
775            'm',
776            'n',
777            'p',
778            'q',
779            'r',
780            's',
781            't',
782            'v',
783            'w',
784            'x',
785            'y',
786            'z',
787
788            );
789            $id = array (
790            'a',
791            'e',
792            'i',
793            'o',
794            'u',
795            'y',
796
797            );
798            $count1 = count($startnend) - 1;
799            $count2 = count($id) - 1;
800
801            for ($i = 0; $i < 3; $i++) {
802                if ($i != 1) {
803                    $rand_pass .= $startnend[rand(0, $count1)];
804                } else {
805                    $rand_pass .= $id[rand(0, $count2)];
806                }
807            }
808        }
809        return $rand_pass;
810    }
811
812    public static function generateToken() {
813        return md5(uniqid(rand(), 1));
814    }
815
816    /**
817     * Get an interface to add a user to a list
818     *
819     * @param string $user_prefix      A identifier provided by the programmer
820     *                                 to recognise it's generated HTML form
821     * @param string $add_button_name  Name of optional "add" button
822     * @param string $add_button_value Value of optional "add" button
823     *
824     * @return string HTML markup
825
826     */
827    public static function getSelectUserUI($user_prefix, $add_button_name = null, $add_button_value = null) {
828        $db = AbstractDb::getObject();
829        $networkSelector = Network :: getSelectUI($user_prefix);
830        // Check if we need to add an "add" button
831        if ($add_button_name && $add_button_value) {
832            $userSelector = _("Username") . ": " . InterfaceElements :: generateInputText("select_user_" . $user_prefix . "_username", "", "", "input_text", array (
833            "onkeypress" => "if ((event.which ? event.which : event.keyCode) == 13) {form.$add_button_name.click() }"
834            ));
835            $userSelector .= InterfaceElements :: generateInputSubmit($add_button_name, $add_button_value);
836        } else {
837            $userSelector = _("Search for Username or Email Address") . ": " . InterfaceElements :: generateInputText("select_user_" . $user_prefix . "_username");
838        }
839        $html = "<div class='user_select_user_ui_container'>".$networkSelector . "<br>" . $userSelector . "</div>\n";
840        return $html;
841    }
842
843    /** Get the selected user, IF one was selected and is valid
844     * @param $user_prefix A identifier provided by the programmer to recognise it's generated form
845     * @param &$errMsg An error message will be appended to this is the username is not empty, but the user doesn't exist.
846     * @return the User object, or null if the user is invalid or none was selected
847     */
848    static function processSelectUserUI($user_prefix, &$errMsg) {
849        $object = null;
850        try {
851            $network = Network :: processSelectUI($user_prefix);
852            $name = "select_user_{$user_prefix}_username";
853            if (!empty ($_REQUEST[$name])) {
854                $username = $_REQUEST[$name];
855                return self :: getUserByUsernameOrEmail($username, $errMsg);
856            } else
857            return null;
858        } catch (Exception $e) {
859            return null;
860        }
861    }
862
863    public function getAdminUI() {
864        $db = AbstractDb::getObject();
865        $currentUser = self :: getCurrentUser();
866        $userPreferencesItems = array();
867        $finalHtml = '';
868        if(Security::hasPermission(Permission::P('NETWORK_PERM_VIEW_STATISTICS'), $this->getNetwork())) {
869            /* Statistics */
870            $content = "<a href='".BASE_SSL_PATH."admin/stats.php?Statistics=".$this->getNetwork()->getId()."&distinguish_users_by=user_id&stats_selected_users=".$this->getUsername()."&UserReport=on&user_id=".$this->getId()."&action=generate'>"._("Get user statistics")."</a>\n";
871            $administrationItems[] = InterfaceElements::genSectionItem($content);
872
873            /* Account status */
874            $title = _("Account Status");
875            $help = _("Note that Error is for internal use only");
876            $name = "user_" . $this->getId() . "_accountstatus";
877            global $account_status_to_text;
878            $content = FormSelectGenerator::generateFromKeyLabelArray($account_status_to_text, $this->getAccountStatus(), $name, null, false);
879            $administrationItems[] = InterfaceElements::genSectionItem($content, $title, $help);
880
881            $finalHtml .= InterfaceElements::genSection($administrationItems, _("Administrative options"));
882        }
883
884        if (($this == $currentUser && !$this->isSplashOnlyUser() )|| Security::hasPermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER'), $this->getNetwork())) {
885            /* Username */
886            $title = _("Username");
887            $name = "user_" . $this->getId() . "_username";
888            $content = "<input type='text' name='$name' value='" . htmlentities($this->getUsername()) . "' size=30><br/>\n";
889            $content .= _("Be careful when changing this: it's the username you use to log in!");
890            $userPreferencesItems[] = InterfaceElements::genSectionItem($content, $title);
891
892
893            /* Email */
894            $title = _("Email");
895            $name = "email_" . $this->getId() . "_email";
896            $content = "<input type='text' name='$name' disabled='disabled' value='" . htmlentities($this->getEmail()) . "' size=30><br/>\n";
897            $content .= _("If you wish to change this address, please Email Support!");
898            $userPreferencesItems[] = InterfaceElements::genSectionItem($content, $title);
899
900
901
902            /* Change password */
903            $changePasswordItems=array();
904            if($this == $currentUser) {//Don't enter the old password if changing password for another user
905                $title = _("Your current password");
906                $name = "user_" . $this->getId() . "_oldpassword";
907                $content = "<input type='password' name='$name' size='20'>\n";
908                $changePasswordItems[] = InterfaceElements::genSectionItem($content, $title);
909            }
910
911            $title = _("Your new password");
912            $name = "user_" . $this->getId() . "_newpassword";
913            $content = "<input type='password' name='$name' size='20'>\n";
914            $changePasswordItems[] = InterfaceElements::genSectionItem($content, $title);
915
916            $title = _("Your new password (again)");
917            $name = "user_" . $this->getId() . "_newpassword_again";
918            $content = "<input type='password' name='$name' size='20'>\n";
919            $changePasswordItems[] = InterfaceElements::genSectionItem($content, $title);
920
921            $userPreferencesItems[] = InterfaceElements::genSection($changePasswordItems, _("Change my password"));
922
923            $finalHtml .= InterfaceElements::genSection($userPreferencesItems, _("User preferences"), false, false, get_class($this));
924
925            //N.B: For now, let pretend we have only one profile per use...
926            $profiles = $this->getAllProfiles();
927            $current_profile = null;
928            if(!empty($profiles)) {
929                $current_profile = $profiles[0];
930            }
931
932            if($current_profile != null) {
933                $finalHtml .= $current_profile->getAdminUI();
934                $name = "user_" . $this->getId() . "_delete_profile_".$current_profile->getId();
935                $value = _("Completely delete my public profile");
936                $finalHtml .= "<div class='admin_element_tools'>";
937                $finalHtml .= '<input type="submit" class="submit" name="' . $name . '" value="' . $value . '">';
938                $finalHtml .= "</div>";
939            }
940            else {                    // Get the list of profile templates for the users' network
941                $profile_templates = ProfileTemplate::getAllProfileTemplates($this->getNetwork());
942                if(!empty($profile_templates)) {
943                    $name = "user_" . $this->getId() . "_add_profile";
944                    $value = _("Create my public profile");
945                    $finalHtml .= "<div class='admin_element_tools'>";
946                    $finalHtml .= '<input type="submit" class="submit" name="' . $name . '" value="' . $value . '">';
947                    $finalHtml .= "</div>";
948                }
949            }
950        }
951
952        return $finalHtml;
953    }
954
955    public function processAdminUI() {
956        $db = AbstractDb::getObject();
957        $currentUser = self :: getCurrentUser();
958        if (Security::hasPermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER'), $this->getNetwork())) {
959            /* Account status */
960            $name = "user_" . $this->getId() . "_accountstatus";
961            $status = FormSelectGenerator::getResult($name, null);
962            $this->setAccountStatus($status);
963        }
964
965        if ($this == $currentUser || Security::requirePermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER'), $this->getNetwork())) {
966            /* Username */
967            $name = "user_" . $this->getId() . "_username";
968            $this->setUsername($_REQUEST[$name]);
969
970            /* Change password */
971            $nameOldpassword = "user_" . $this->getId() . "_oldpassword";
972            $nameNewpassword = "user_" . $this->getId() . "_newpassword";
973            $nameNewpasswordAgain = "user_" . $this->getId() . "_newpassword_again";
974            if($_REQUEST[$nameNewpassword]!=null){
975                if ($this == $currentUser && $this->getPasswordHash() != User::passwordHash($_REQUEST[$nameOldpassword])) {
976                    throw new Exception(_("Wrong password."));
977                }
978                if ($_REQUEST[$nameNewpassword] != $_REQUEST[$nameNewpasswordAgain]){
979                    throw new Exception(_("Passwords do not match."));
980                }
981                $this->setPassword($_REQUEST[$nameNewpassword]);
982            }
983
984            // Pretend there is only one
985            $profiles = $this->getAllProfiles();
986            if(!empty($profiles)) {
987                $current_profile = $profiles[0];
988                if($current_profile != null) {
989                    $current_profile->processAdminUI();
990                    $name = "user_" . $this->getId() . "_delete_profile_".$current_profile->getId();
991                    if(!empty($_REQUEST[$name])) {
992                        $errmsg=null;
993                        $current_profile->delete($errmsg);
994                    }
995                }
996            }
997            else {
998                $name = "user_" . $this->getId() . "_add_profile";
999                if(!empty($_REQUEST[$name])) {
1000                    // Get the list of profile templates for the users' network
1001                    $profile_templates = ProfileTemplate::getAllProfileTemplates($this->getNetwork());
1002                    if(!empty($profile_templates)) {
1003                        // Create a blank profile and link it to the user
1004                        $current_profile = Profile::createNewObject(null, $profile_templates[0]);
1005                        $this->addProfile($current_profile);
1006                    }
1007                }
1008
1009            }
1010
1011        }
1012    }
1013
1014    public function delete(& $errmsg) {
1015    }
1016
1017    public function getUserUI() {
1018        $html = "";
1019        $html .= $this->getRealName();
1020
1021        return $html;
1022    }
1023
1024    /** Add content to this user ( subscription ) */
1025    public function addContent(Content $content) {
1026        $db = AbstractDb::getObject();
1027        $content_id = $db->escapeString($content->getId());
1028        $sql = "INSERT INTO user_has_content (user_id, content_id) VALUES ('$this->id','$content_id')";
1029        $db->execSqlUpdate($sql, false);
1030        return true;
1031    }
1032
1033    /** Remove content from this node */
1034    public function removeContent(Content $content) {
1035        $db = AbstractDb::getObject();
1036        $content_id = $db->escapeString($content->getId());
1037        $sql = "DELETE FROM user_has_content WHERE user_id='$this->id' AND content_id='$content_id'";
1038        $db->execSqlUpdate($sql, false);
1039        return true;
1040    }
1041
1042    /**Get an array of all Content linked to this node
1043     * @return an array of Content or an empty arrray */
1044    function getAllContent() {
1045        $db = AbstractDb::getObject();
1046        $retval = array ();
1047        $content_rows = null;
1048        $sql = "SELECT * FROM user_has_content WHERE user_id='$this->id' ORDER BY subscribe_timestamp";
1049        $db->execSql($sql, $content_rows, false);
1050        if ($content_rows != null) {
1051            foreach ($content_rows as $content_row) {
1052                $retval[] = Content :: getObject($content_row['content_id']);
1053            }
1054        }
1055        return $retval;
1056    }
1057
1058    /** Reloads the object from the database.  Should normally be called after a set operation */
1059    protected function refresh() {
1060        $this->__construct($this->id);
1061    }
1062    /** Menu hook function */
1063    static public function hookMenu() {
1064        $items = array();
1065        $network = Network::getCurrentNetwork();
1066        $server = Server::getServer();
1067        if(Security::hasAnyPermission(array(array(Permission::P('NETWORK_PERM_VIEW_ONLINE_USERS'), $network))))
1068        {
1069            $items[] = array('path' => 'users/online_users',
1070            'title' => _("Online Users"),
1071            'url' => BASE_URL_PATH."admin/online_users.php");
1072        }
1073        if(Security::hasPermission(Permission::P('SERVER_PERM_EDIT_SERVER_CONFIG'), $server))
1074        {
1075            $items[] = array('path' => 'users/import_nocat',
1076            'title' => _("Import NoCat user database"),
1077            'url' => BASE_URL_PATH."admin/import_user_database.php"
1078            );
1079        }
1080        if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_EDIT_ANY_USER')))
1081        {
1082            $items[] = array('path' => 'users/user_manager',
1083            'title' => _("User manager"),
1084            'url' => BASE_URL_PATH."admin/user_log.php"
1085            );
1086        }
1087        if(Security::getObjectsWithPermission(Permission::P('NETWORK_PERM_VIEW_STATISTICS')))
1088        {
1089            $items[] = array('path' => 'users/statistics',
1090            'title' => _("Statistics"),
1091            'url' => BASE_URL_PATH."admin/stats.php"
1092            );
1093        }
1094        $items[] = array('path' => 'users',
1095        'title' => _('User administration'),
1096        'type' => MENU_ITEM_GROUPING);
1097        return $items;
1098    }
1099
1100    /** Set Smarty template values.  Standardization routine. */
1101    public static function assignSmartyValues($smarty, $user = null) {
1102        if (!$user)
1103        $user = User :: getCurrentUser();
1104        $session = Session :: getObject();
1105        $smarty->assign('userOriginallyRequestedURL', $session ? $session->get(SESS_ORIGINAL_URL_VAR) : '');
1106        $smarty->assign('userId', $user ? $user->getId() : '');
1107        $smarty->assign('userName', $user ? $user->getUsername() : '');
1108        /**
1109         * Define user security levels for the template
1110         *
1111         * These values are used in the default template of WiFoDog but could be
1112         * used in a customized template to restrict certain links to specific
1113         * user access levels.  Note however that they will all be deprecateb by the
1114         * new roles system.
1115         */
1116        $smarty->assign('userIsValid', $user && !$user->isSplashOnlyUser() ? true : false);
1117        $smarty->assign('userDEPRECATEDisSuperAdmin', $user && $user->DEPRECATEDisSuperAdmin());
1118
1119        if (isset ($_REQUEST['debug_request']) && ($user && $user->DEPRECATEDisSuperAdmin())) {
1120            // Tell Smarty everything it needs to know
1121            $smarty->assign('debugRequested', true);
1122            $smarty->assign('debugOutput', print_r($_REQUEST, true));
1123        }
1124    }
1125}
1126
1127/*
1128 * Local variables:
1129 * tab-width: 4
1130 * c-basic-offset: 4
1131 * c-hanging-comment-ender-p: nil
1132 * End:
1133 */
Note: See TracBrowser for help on using the browser.