Source for file dpuniverse.php
Documentation is available at dpuniverse.php
* Provides 'DpUniverse' class to handle a specific 'universe'
* Defines DpObjects, users, pages, etc. (our 'rules of nature')
* DutchPIPE version 0.4; PHP version 5
* LICENSE: This source file is subject to version 1.0 of the DutchPIPE license.
* If you did not receive a copy of the DutchPIPE license, you can obtain one at
* http://dutchpipe.org/license/1_0.txt or by sending a note to
* license@dutchpipe.org, in which case you will be mailed a copy immediately.
* @author Lennert Stock <ls@dutchpipe.org>
* @copyright 2006, 2007 Lennert Stock
* @license http://dutchpipe.org/license/1_0.txt DutchPIPE License
* @version Subversion: $Id: dpuniverse.php 311 2007-09-03 12:48:09Z ls $
* @link http://dutchpipe.org/manual/package/DutchPIPE
* @see currentdpuserrequest.php, dpserver.php, dpfunctions.php
/* Shows all possible errors */
* The universe object handling the current http user request. Do not access
* this variable directly, use get_current_dpuniverse() instead.
$grCurrentDpUniverse = NULL;
* The object responsible for the current chain of execution. Do not access
* this variable directly, use get_current_dpobject() instead.
$grCurrentDpObject = NULL;
define('_DPUSER_OBJECT', 0); /* Reference to /std/DpUser.php object */
define('_DPUSER_MESSAGES', 1); /* Array of strings with messages */
define('_DPUSER_NAME', 2); /* Name of user behind http request */
define('_DPUSER_COOKIEID', 3); /* Cookie Id used for authorization */
define('_DPUSER_COOKIEPASS', 4); /* Cookie Pass used for authorization */
define('_DPUSER_ISREGISTERED', 5); /* Is it a registered user? */
define('_DPUSER_TIME_LASTREQUEST', 6); /* Last http request UNIX time */
define('_DPUSER_CURRENT_SCRIPTID', 7); /* Script id of current AJAX request */
define('_DPUSER_LAST_SCRIPTID', 8); /* Script id of last AJAX request */
* A DutchPIPE universe, handling objects, users, pages, etc. (rules of nature)
* @author Lennert Stock <ls@dutchpipe.org>
* @copyright 2006, 2007 Lennert Stock
* @license http://dutchpipe.org/license/1_0.txt DutchPIPE License
* @version Release: 0.2.1
* @link http://dutchpipe.org/manual/package/DutchPIPE
* @var array All objects on the site
private $mDpObjects = array();
* @var array Reset queue, first element will reset first
private $mDpObjectResets = array();
* The environment of each object on the site in env => ob pairs.
* @var array The environments of objects
private $mEnvironments = array();
* All user objects on the site + data
* Each element in the array represents a user. A user is stored in another
* array, with the elements as defined by the _DPUNIVERSE_USER_ definitions.
* @var array All user objects on the site + data
private $mDpUsers = array();
* @var array All pending timeouts after use of setTimeout()
private $mTimeouts = array();
* Increasing guest counter used to form unique names for guests, for
* @var int Increasing guest counter for unique guest names
* DpObject counter increased by newDpObject, used to generate unique object
* @var int Used to generate unique object
private $mUniqueDpObjectCnt = 1;
* @var object The server object that called us
* @var array Info on the current HTTP user request
private $mrCurrentDpUserRequest;
* @var array The key of the current users' entry in mDpUsers
private $mCurrentDpUserKey;
* @var boolean Do not tell anything to the current http request?
private $mNoDirectTell = FALSE;
* Error messages for the last new user registration attempt
private $mLastNewUserErrors = array();
* UNIX timestamp when next reset in an object will occur
* UNIX time stamp when cleanup mechanism was last called
private $mLastCleanUpTime = 0;
* Lists of objects listening to certain events
private $mAlertEvents = array();
* Constructs this universe based on a universe ini file
* @param string $iniFile path to settings file for this universe
/* Gets the universe settings */
$this->mGuestCnt = mt_rand(25, 75);
$this->mLastCleanUpTime = time();
/* These functions will be available for all objects */
? 'disabled' : 'enabled') . '.php');
// Clean up guest avatar directory
while (FALSE !== ($entry = readdir($dir))) {
if ('.' != $entry && '..' != $entry)
* Gets an unique suffix to make Guest#x names
* @return integer A unique number, currently a counter
return $this->mGuestCnt++ ;
* Handles user requests passed on from the DutchPIPE server
* Called each time a user's browser does a normal page or AJAX request.
* Several variables are passed which represent their corresponding PHP
* global arrays: $_SERVER, $_COOKIE, etc.
* @param array &$rDpServer Reference to the server object
* @param array &$rServerVars User server variables
* @param array &$rSessionVars User session variables
* @param array &$rCookieVars User cookie variables
* @param array &$rGetVars User get variables
* @param array &$rPostVars User post variables
* @param array &$rFilesVars User files variables
&$rServerVars = NULL, &$rSessionVars = NULL, &$rCookieVars = NULL,
&$rGetVars = NULL, &$rPostVars = NULL, &$rFilesVars = NULL)
$this->mrDpServer = $rDpServer;
$GLOBALS['grCurrentDpUniverse'] = &$this;
* Because we don't use a 'ticks' system or something similar yet, user
* user requests are used to handle some generic cyclic calls
if (isset ($this->mNextResetTime) && $this->mNextResetTime <= time()) {
$this->mrCurrentDpUserRequest = new DpCurrentRequest($this,
$rServerVars, $rSessionVars, $rCookieVars, $rGetVars, $rPostVars,
$this->mrCurrentDpUserRequest->handleRequest();
if (FALSE === $this->mrCurrentDpUserRequest->isRegistered()
&& FALSE === $this->mrCurrentDpUserRequest->getUserAgent()) {
if (!isset ($rGetVars['ajax'])) {
$this->tellCurrentDpUserRequest('<event><div id="dppage">'
. dp_text('Your browser did not report a User Agent string to the server. This is required.<br />')
unset ($this->mrCurrentDpUserRequest);
unset ($this->mCurrentDpUserKey);
if (FALSE === $this->mrCurrentDpUserRequest->isCookieEnabled()) {
$this->tellCurrentDpUserRequest('2');
unset ($this->mrCurrentDpUserRequest);
unset ($this->mCurrentDpUserKey);
if (isset ($this->mCurrentDpUserKey)) {
$user_arr_key = $this->mCurrentDpUserKey;
$user_arr_key = key($this->mDpUsers);
* scriptids are used to detect if a user has multiple browser windows
* open. Each initiated dpclient-js.php sets such a random id
$new_scriptid = is_null($rGetVars) || !isset ($rGetVars['ajax'])
|| !isset ($rGetVars['scriptid']) || 0 === (int) $rGetVars['scriptid']
? FALSE : $rGetVars['scriptid'];
if (FALSE !== $old_scriptid && FALSE !== $new_scriptid
&& $old_scriptid !== $new_scriptid) {
* Because we don't use a 'ticks' system or something similar yet, user
* user requests are used to handle some generic cyclic calls
$this->mrCurrentDpUserRequest->handleUser();
unset ($this->mrCurrentDpUserRequest);
unset ($this->mCurrentDpUserKey);
$this->mLastCleanUpTime = time();
* Adds a new, given user object to the universe
* :WARNING: Should normally only be called from lib/dpcurrentrequest.php.
* @param object &$user a new DpUser object
* @param string $username the user's username
* @param string $cookieID the user's cookie ID
* @param string $cookiePass the user's cookie password
* @param boolean $isRegistered TRUE for registered user, else FALSE
function addDpUser(&$user, $username, $cookieId, $cookiePass,
$this->mDpUsers[] = array($user, array(), $username, $cookieId,
$cookiePass, $isRegistered, 0, FALSE, FALSE);
* Gets pending messages for the current user request
* :WARNING: Should normally only be called from lib/dpcurrentrequest.php.
* @return mixed array with messages of FALSE for no messages
function &getCurrentDpUserMessages()
if (isset ($this->mCurrentDpUserKey)
&& isset ($this->mDpUsers[$this->mCurrentDpUserKey])) {
* Clears pending messages for the current user request
* :WARNING: Should normally only be called from lib/dpcurrentrequest.php.
function clearCurrentDpUserMessages()
* Finds if a user is on this site based on cookie data
* If found, sets $this->mCurrentDpUserKey to the key to the user info in
* the universe's user array.
* :WARNING: Should normally only be called from lib/dpcurrentrequest.php.
* @param string $cookieID the user's cookie ID
* @param string $cookiePass the user's cookie password
* @return mixed array with user info or FALSE if no user found
function &findCurrentDpUser($cookieid, $cookiepass)
foreach ($this->mDpUsers as $user_arr_key => &$u) {
$this->mCurrentDpUserKey = $user_arr_key;
* Checks if people left the site, throws them out of the universe
private function handleLinkdead()
/* Time the user's browser should have done a page or AJAX request */
* Currently, this function is triggered by a user http request. If
* there's one user and he leaves, then if the next user enters, say a
* minute later (this time is defined here), he should not see
* Bots may be visible a bit longer, since they don't have Javacript and
* sometimes do a lot of requests. With a short 'kick' time, users would
* get a lot of 'Bot enters the site' and 'Bot leaves the site'
foreach ($this->mDpUsers as $i => &$u) {
* Throw out people who lost connection or browsed elsewhere. Need
* to think this one out (move them to a void, etc.), something
/* The "linkdeath" check */
if (($ajax_capable && $lastrequest_time < $linkdeath_time
|| $lastrequest_time < $cur_time
|| (!$ajax_capable && $lastrequest_time < $botkick_time)) {
* This method is called before an instance of
* CurrentDpUserRequest is created and the current http request
* is handled. However, the tell() functions operate on the
* current request. Therefore, any sound made should be stored
* for the next cycle, otherwise the people get wrong messages.
* tell() in the User class checks for this variable:
$this->mNoDirectTell = TRUE;
if (($ajax_capable && $lastrequest_time > $showmsg_time)
&& $lastrequest_time > $showbot_time)) {
/* Drop stuff, tell people on the page the user left */
dp_text('%s left the site.<br />'),
dp_text("<span class=\"col2\">%s</span> %s left the site at %s.<br />"),
foreach ($listeners as &$listener) {
if ($listener->getEnvironment() !== $env) {
$listener->tell($listener_msg);
/* Keeps track of all unique user agents */
$this->mNoDirectTell = FALSE;
* Calls '__reset' in each object every DPUNIVERSE_RESET_CYCLE seconds
private function handleReset()
/* Perform a limited number of resets per cycle */
reset($this->mDpObjectResets);
/* Pick up where we were in the reset array */
while (($ob = current($this->mDpObjectResets)) && $max_resets--
/* Checks if the current object ready to reset */
if ($ob->resetTime > time()) {
$this->mNextResetTime = $ob->resetTime;
/* Resets the object, sets next reset time */
$this->mDpObjectResets[] = $ob;
reset($this->mDpObjectResets);
$this->mNextResetTime = current($this->mDpObjectResets)->resetTime;
* Optionally uses experimental PHP runkit module to catch errors in code
if (FALSE !== DPUNIVERSE_RUNKIT) {
if (FALSE === @runkit_lint_file(DPUNIVERSE_PREFIX_PATH
. $this->__GET['location'])) {
if ($this->mrUser->getEnvironment()) {
$this->mrEnvironment = $this->mrUser->getEnvironment();
$this->__GET['location'] =
FALSE === is_null($this->mrEnvironment)
? $this->mrEnvironment->location
: DPUNIVERSE_PAGE_PATH . 'index.php';
$this->mrUser->tell('<location>' . $__GET['location']
$this->mMoveError = dp_text('You notice a disruptance.<br />');
* Handles delayed function calls requested by objects in the universe
private function handleTimeouts()
while (sizeof($this->mTimeouts)) {
foreach ($this->mTimeouts as $i => $to) {
if (isset ($this->mTimeouts[$i][0]->isRemoved)
&& TRUE === $this->mTimeouts[$i][0]->isRemoved) {
unset ($this->mTimeouts[$i][0]);
unset ($this->mTimeouts[$i]);
if ($this->mTimeouts[$i][2] <= $cur_time) {
$method = $this->mTimeouts[$i][1];
if (!isset ($this->mTimeouts[$i][3])) {
$this->mTimeouts[$i][0]->$method();
$method), $this->mTimeouts[$i][3]);
unset ($this->mTimeouts[$i][0]);
unset ($this->mTimeouts[$i]);
* Handles removal of unused object instances
private function handleCleanUps()
echo "HandleCleanUps called\n";
foreach ($this->mDpObjects as $i => &$ob) {
if (!$ob->getEnvironment()
echo "calling handleCleanUp in {$ob->title}\n ";
* Stores user agent information in the database
* Experimental. Used to catch search bots.
* @param string &$user user object
private function saveUserAgent(&$user)
$agent = !isset ($user->_SERVER['HTTP_USER_AGENT'])
|| 0 === dp_strlen($user->_SERVER['HTTP_USER_AGENT'])
: $user->_SERVER['HTTP_USER_AGENT'];
if (isset ($user->isAjaxCapable) && TRUE === $user->isAjaxCapable) {
$result = dp_db_query('SELECT userAgentId from UserAgents '
. 'WHERE userAgentString=' . dp_db_quote($agent, 'text'));
. '(userAgentId,userAgentString) VALUES ('
$remote_address = !isset ($user->_SERVER['REMOTE_ADDR'])
|| 0 === dp_strlen($user->_SERVER['REMOTE_ADDR'])
: $user->_SERVER['REMOTE_ADDR'];
$result = dp_db_query('SELECT userAgentId from UserAgents '
. 'WHERE userAgentString=' . dp_db_quote($agent, 'text')
. ' AND userAgentRemoteAddress='
. '(userAgentId,userAgentString,userAgentRemoteAddress)'
* Stores a message for a user while we wait for another http request
* Called from tell in DpUser.php
* :WARNING: This method should normally only be called the tell method in
* @param object $user recipient user of message
* @param string $data message string
* @param object $binded_env optional binded environment
function storeTell(&$user, $data, &$binded_env = NULL)
foreach ($this->mDpUsers as $i => &$u) {
$this->mDpUsers[$i][1][] = array($data, $binded_env);
* Tells something back to the currently connected user client
* :WARNING: This method should normally only be called from DpUser.php.
* @param string $talkback XML string
function tellCurrentDpUserRequest($talkback)
// echo "Talkback: " . htmlentities($talkback) . "\n";
$this->mrDpServer->tellCurrentDpUserRequest($talkback);
$this->mrCurrentDpUserRequest->setToldSomething();
* Gets db row key of a random CAPTCHA code for the user registration page
* Used to seperate software robots from real people during registration.
* Codes aren't generated on the fly and as such not really random because
* the CPU time penalty is too high. A directory with pre-generated codes
* exists and a database table with info. A cronjob makes one every hour and
* replaces the oldest one in the database. Then we obtain a random entry
* here. This should do it for now, but your milleague might vary and the
* situation might change as spambots get smarter.
* @return int CAPTCHA database row key
$result = dp_db_query('SELECT captchaId FROM Captcha');
* Creates a new object in the universe
* You MUST call this function to create new objects in a DutchPIPE
* universe, don't use the 'new' construct directly.
* The object can have a unique location, given with $pathname, or just be
* based on $pathname, with a sublocation handling multiple objects. For
* example, the URL in uour browser contains the following bit for the
* DutchPIPE "about" page: location=/page/about.php
* It is a unique page with a unique location. The manual however is not
* based on many unique locations, but just one object which spawns pages
* based on the sublocation given in the URL:
* location=/page/manual.php&sublocation=index.html
* @param string $pathname path to code from universe base path
* @param string $sublocation optional sublocation
* @return object The newly created object
echo "Making: $pathname, $sublocation\n";
echo dp_text("Illegal object requested!\n");
$unique_id = $this->mUniqueDpObjectCnt;
$this->mUniqueDpObjectCnt++ ;
if ($pathname && (dp_substr($pathname, 0, 1) != '/'
$ob->setTitle($pathname);
$ob->setBody($pathname, 'file');
$classname = explode("/", $pathname);
$pathname, $sublocation);
$this->__GET['location'] = $pathname;
if (FALSE !== $sublocation) {
$this->__GET['sublocation'] = $sublocation;
$this->mDpObjects[] = & $ob;
$this->mDpObjectResets[] = & $ob;
if (!isset ($this->mNextResetTime)) {
$this->mNextResetTime = $ob->resetTime;
* Moves the object to another environment
* This method only handles the internal storage of environments.
* Most functionality can be found in moveDpObject in DpObject.php.
* :WARNING: This method should normally only be called from DpObject.php.
* @param object &$rItem object to move
* @param mixed &$rDest object to move into to
function moveDpObject(&$rItem, &$rDest)
foreach ($this->mEnvironments as $i => &$pair) {
if ($pair[0] === $rItem) {
unset ($this->mEnvironments[$i]);
$this->mEnvironments[$i][1] = & $rDest;
$this->mEnvironments[] = array(&$rItem, &$rDest);
* The object is destroyed and no longer part of the universe.
* This method only handles the internal storage of objects.
* Most functionality can be found in removeDpObject in DpObject.php.
* :WARNING: This method should normally only be called from DpObject.php.
* @param object &$rTarget object to remove
function removeDpObject(&$rTarget)
global $grCurrentDpObject;
foreach ($this->mDpUsers as $i => &$u) {
foreach ($this->mEnvironments as $i => &$env) {
if ($env[0] === $rTarget) {
if ($env[1] === $rTarget) {
foreach ($this->mDpObjects as $i => &$ob) {
foreach ($this->mDpObjectResets as $i => &$ob) {
foreach ($this->mTimeouts as $i => &$ob) {
if ($ob[0] === $rTarget) {
foreach ($this->mAlertEvents as $event => &$listeners) {
foreach ($listeners as $i => &$ob) {
$del_events[] = array($event, $i);
unset ($this->mDpUsers[$del_user]);
unset ($this->mEnvironments[$del_env][0]);
unset ($this->mEnvironments[$del_env]);
foreach ($del_envs as $del_env) {
unset ($this->mEnvironments[$del_env][1]);
$this->mEnvironments[$del_env][0]->removeDpObject();
unset ($this->mDpObjects[$del_obj]);
unset ($this->mDpObjectResets[$del_reset]);
foreach ($del_timeouts as $del_timeout) {
unset ($this->mTimeouts[$del_timeout][0]);
unset ($this->mTimeouts[$del_timeout]);
foreach ($del_events as $event => $i) {
unset ($this->mAlertEvents[$event][$i]);
* Finds an object in the universe with the given unique id
* @param string $unique_id the object's unique string id
* @return mixed Object reference if found, FALSE otherwise
foreach ($this->mDpObjects as $i => &$ob) {
if ($unique_id === $ob->getUniqueId()) {
* Finds or makes an object in the universe with the given pathname
* If an object with the given pathname exists, a reference to that object
* is returned. Otherwise a new instance of the class found at $pathname
* is created and returned.
* The object can have a unique location, given with $pathname, or just be
* based on $pathname, with a sublocation handling multiple objects. For
* example, the URL in uour browser contains the following bit for the
* DutchPIPE "about" page: location=/page/about.php
* It is a unique page with a unique location. The manual however is not
* based on many unique locations, but just one object which spawns pages
* based on the sublocation given in the URL:
* location=/page/manual.php&sublocation=index.html
* @param string $pathname a path within dpuniverse/
* @param string $sublocation optional sublocation
* @return object Reference to instance of $pathname
if (FALSE === $sublocation) {
foreach ($this->mDpObjects as $i => &$ob) {
if ($pathname === $ob->location) {
//echo dp_text("getDpObject(): returning existing object with location %s, no sublocation\n",
foreach ($this->mDpObjects as $i => &$ob) {
if ($pathname === $ob->location
&& $sublocation === $ob->sublocation) {
//echo dp_text("getDpObject(): returning existing object with location %s, sublocation %s\n",
// $pathname, $sublocation);
echo FALSE == $sublocation
? dp_text("getDpObject(): returning new object for location %s, no sublocation\n",
: dp_text("getDpObject(): returning new object for location %s, sublocation %s\n",
$pathname, $sublocation);
* Gets the object reference to the environment of this object
* Don't call this method here, call it in objects in the universe instead,
* for instance $user->getEnvironment().
* :WARNING: This method should normally only be called from DpObject.php.
* @param object &$ob object we want to know environment of
* @return mixed object reference or FALSE for no environment
function &getEnvironment(&$ob)
foreach ($this->mEnvironments as &$pair) {
* Gets an array with object references to all objects in our inventory
* If this object contains no other objects, an empty array is returned.
* :WARNING: This method should normally only be called from DpObject.php.
* @param object &$ob object we want to know inventory of
* @return array object references to objects in our inventory
function &getInventory(&$ob)
foreach ($this->mEnvironments as &$pair) {
* Checks if an object is present
* Search in the inventory of an object, as specified in $where, for another
* object, as specified in $what. If $what is a string the objects searched
* are checked for returning TRUE on the call isId($what).
* :WARNING: This method should normally only be called from DpObject.php.
* @param string|object $what object to search for
* @param object &$where object to search in
* @return object|boolean the found object or FALSE if not found
* @see DpObject::getEnvironment(), DpObject::getInventory()
function &isPresent($what, &$where)
$inv = $this->getInventory($where);
if ($ob->isId($what) && (0 === -- $nr)) {
* Gets the current user connected to the server
* If a user page or AJAX request caused the current chain of execution that
* caused this function to be called, that user object is returned.
* Otherwise FALSE is returned. For example, if the chain of execution if
* caused by a setTimeout, this will return FALSE.
* :WARNING: Use the function get_current_dpuser() instead of calling this
* @return object Reference to user object, FALSE for no current user
function &getCurrentDpUser()
$rval = !isset ($this->mrCurrentDpUserRequest) ? FALSE
: $this->mrCurrentDpUserRequest->getUser();
* Finds the user with the given user name or id.
* @param string $userName user name or id of player
* @return object|boolean the found player or FALSE if not found
foreach ($this->mDpUsers as &$u) {
* Gets an array with user object references of all users on this site
* @return array user object references of all users
* :TODO: Since this will be used a lot, keep a seperate copy instead of
* constructing this array each time
foreach ($this->mDpUsers as &$u) {
* Gets the number of users on this site
* @return int number of users on this site
return sizeof($this->mDpUsers);
* Calls a given method in an object after the given number of seconds
* Use this to perform delayed method calls in the given object. Note that
* functions such as get_current_dpuser can be totally different when the
* method is called. Also note that the actual delay is not exact science.
* :WARNING: This method should normally only be called from DpObject.php.
* @param object &$ob reference to object to call method in
* @param string $method name of method to call
* @param mixed $arg1, $arg2, ... optional arguments for method
* @param int d $secs delay in seconds
function setTimeout(&$ob, $method, $secs)
$timeout_info = array(&$ob, $method, time() + $secs);
$this->mTimeouts[] = & $timeout_info;
* Gets an array with information about the universe
* The following array is returned:
* 'memory_usage' : <int Universe memory usage in bytes>
* 'nr_of_objects' : <int Number of objects in the universe>
* 'nr_of_users' : <int Number of users in the universe>
* 'nr_of_environments' : <int Number of environments in the universe>
* 'nr_of_timeouts' : <int Number of "timeouts" in the universe>
* @return array universe information
$serialized_universe = serialize($this);
$fp = fopen('/tmp/serialized_universe', 'w');
fwrite($fp, $serialized_universe);
'nr_of_objects' => sizeof($this->mDpObjects),
'nr_of_users' => sizeof($this->mDpUsers),
'nr_of_environments' => sizeof($this->mEnvironments),
'nr_of_timeouts' => sizeof($this->mTimeouts));
* Attempts to login the given user object of a guest as a registered user
* @param object &$user user to validate
* @return boolean TRUE for succesful login, FALSE otherwise
$this->mLastNewUserErrors = array();
if (!isset ($user->_GET['username'])
|| 0 === dp_strlen($user->_GET['username'])) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('No username was given') . '</li>';
if (0 === sizeof($this->mLastNewUserErrors)
&& $user->_GET['username'] === $user->getTitle()) {
$this->mLastNewUserErrors[] = '<li>'
$user->getTitle()) . '</li>';
} elseif (!isset ($user->_GET['password'])
|| 0 === dp_strlen($user->_GET['password'])) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('No password was given') . '</li>';
if (0 === sizeof($this->mLastNewUserErrors)) {
$result = dp_db_query('SELECT userUsername, userPassword, '
. 'userCookieId, userCookiePassword FROM Users WHERE '
. 'userUsernameLower=' . dp_db_quote($username, 'text'));
$this->mLastNewUserErrors[] =
'<li>' . dp_text('That username doesn\'t exist') . '</li>';
if ($row[1] !== $user->_GET['password']) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('Invalid password') . '</li>';
if (sizeof($this->mLastNewUserErrors)) {
$user->tell('<window styleclass="dpwindow_error"><h1>'
. dp_text('Invalid login') . '</h1><br /><ul>'
. implode('', $this->mLastNewUserErrors) . '</ul></window>');
$user->tell('<cookie>removeguest</cookie>');
$user->_GET['username'] = $username;
$user->addId($user->_GET['username']);
$user->setTitle(ucfirst($user->_GET['username']));
$user->isRegistered = TRUE;
$user->titleImgWidth = NULL;
$user->titleImgHeight = NULL;
$row = $this->mrCurrentDpUserRequest->findAndInitRegisteredDpUser(
$cookie_id, $cookie_pass);
$this->mrCurrentDpUserRequest->initDpUser();
/* :TODO: Move admin flag to user table in db */
foreach ($this->mDpUsers as $user_nr => &$u) {
$this->mDpUsers[$user_nr][2] = $user->_GET['username'];
$this->mDpUsers[$user_nr][3] = $cookie_id;
$this->mDpUsers[$user_nr][4] = $cookie_pass;
$this->mDpUsers[$user_nr][5] = 1;
$user->tell('<changeDpElement id="username">'
. $user->_GET['username'] . '</changeDpElement>');
$user->tell(array('abstract' => '<changeDpElement id="'
. $user->getUniqueId() . '"><b>'
. $user->getAppearance(1, FALSE) . '</b></changeDpElement>',
'graphical' => '<changeDpElement id="'
. $user->getUniqueId() . '"><b>'
. $user->getAppearance(1, FALSE, $user, 'graphical')
. '</b></changeDpElement>'));
$user->tell('<changeDpElement id="loginlink"><a href="'
. 'login.php&act=logout" style="padding-left: 4px">'
. dp_text('Logout') . '</a></changeDpElement>');
$user->getEnvironment()->tell(array(
'abstract' => '<changeDpElement id="' . $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE) . '</changeDpElement>',
'graphical' => '<changeDpElement id="'
. $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE, $user, 'graphical')
. '</changeDpElement>'), $user);
$user->tell('<window><h1>' . dp_text('Welcome back') . '</h1><br />'
$user->getTitle()) . '</window>');
* Logs out a given registered user and turns the user into a guest
* @param object &$user user to logout
$oldtitle = $user->getTitle();
$user->tell('<cookie>removeregistered</cookie>');
$this->tellCurrentDpUserRequest("Set-Login: "
$user->_GET['username'] = $username;
$user->removeId($oldtitle);
$user->setTitle(ucfirst($username));
if ($user->avatarCustom) {
$user->avatarCustom = NULL;
$user->titleImgWidth = NULL;
$user->titleImgHeight = NULL;
. $user->avatarNr . '.gif';
. 'user' . $user->avatarNr . '_body.gif" border="0" '
. 'alt="" align="left" style="margin-right: 15px" />'
$user->isRegistered = FALSE;
$this->mrCurrentDpUserRequest->setUsername($username);
foreach ($this->mDpUsers as $user_nr => &$u) {
$this->mDpUsers[$user_nr][2] = $username;
$this->mDpUsers[$user_nr][3] = $cookie_id;
$this->mDpUsers[$user_nr][4] = $cookie_pass;
$this->mDpUsers[$user_nr][5] = 0;
$this->mrCurrentDpUserRequest->setHasMoved();
$this->mNoDirectTell = TRUE;
$oldtitle) . '</h1><br />' . dp_text('See you later!') . '<br />'
. dp_text('You are now: <b>%s</b>', $user->getTitle())
$this->mNoDirectTell = FALSE;
. 'login.php</location>');
$user->getEnvironment()->tell(array(
'abstract' => '<changeDpElement id="' . $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE) . '</changeDpElement>',
'graphical' => '<changeDpElement id="'
. $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE, $user, 'graphical')
. '</changeDpElement>'), $user);
* Attempts the first step in registering a new user, throws CAPTCHA
* @param object &$user user to register
* @return boolean TRUE to continue to CAPTCHA, FALSE for errors
if (FALSE === $this->validLoginInfo($user->_GET['username'],
$user->_GET['password'], $user->_GET['password2'])) {
$user->tell('<window styleclass="dpwindow_error"><h1>'
. dp_text('Invalid registration') . '</h1><br />'
. dp_text('Please correct the following errors:') . '<ul>'
. implode('', $this->mLastNewUserErrors) . '</ul></window>');
if (!isset ($user->_GET['givencode'])) {
$user->tell('<window><form method="post" '
. 'onsubmit="send_captcha(' . $captcha_id
. '); return false"><div align="center">'
. 'dpcaptcha.php?captcha_id='
. $captcha_id . '" border="0" alt="" /></div>'
. dp_text('To complete registration, please enter the code you see above:')
. '<br /><br /><div align="center" '
. 'style="margin-bottom: 5px"><input id="givencode" '
. 'type="text" size="6" maxlength="6" value="" /> '
. '<input type="submit" value="'
. dp_text('OK') . '" /></div><br />'
. dp_text('This system is used to filter software robots
from registrations submitted by individuals. If you are unable to validate the
above code, please <a href="mailto:registration@dutchpipe.org">mail us</a>
to complete registration.') . '</form></window>');
* Attempts the second step in registering a new user, validates CAPTCHA
* @param object &$user user to register
if (!isset ($user->_GET['captcha_id'])
|| !isset ($user->_GET['givencode']) || FALSE ===
$user->_GET['givencode'])) {
if (NULL === ($captcha_attempts = $user->captchaAttempts)
|| $captcha_attempts < 2) {
NULL === $captcha_attempts ? 1 : $captcha_attempts + 1);
unset ($user->captchaAttempts);
$user->tell('<window styleclass="dpwindow_error"><h1>'
. dp_text('Failure validating code') . '</h1><br />'
. dp_text('Please try again.') . '</window>');
$username = $user->_GET['username'];
$keys[] = 'userUsername';
$keys[] = 'userUsernameLower';
$keys[] = 'userPassword';
$keys[] = 'userCookieId';
$keys[] = 'userCookiePassword';
dp_db_exec("INSERT INTO Users ($keys) VALUES ($vals)")) {
$user->tell('<cookie>removeguest</cookie>');
"$cookie_id;$cookie_pass";
$user->setTitle(ucfirst($username));
$user->isRegistered = TRUE;
$this->mrCurrentDpUserRequest->findAndInitRegisteredDpUser(
$cookie_id, $cookie_pass);
foreach ($this->mDpUsers as $user_nr => &$u) {
$this->mDpUsers[$user_nr][2] = $username;
$this->mDpUsers[$user_nr][3] = $cookie_id;
$this->mDpUsers[$user_nr][4] = $cookie_pass;
$this->mDpUsers[$user_nr][5] = 1;
$user->tell('<changeDpElement id="username">' . $username
$user->tell(array('abstract' => '<changeDpElement id="'
. $user->getUniqueId() . '"><b>'
. $user->getAppearance(1, FALSE) . '</b></changeDpElement>',
'graphical' => '<changeDpElement id="'
. $user->getUniqueId() . '"><b>'
. $user->getAppearance(1, FALSE, $user, 'graphical')
. '</b></changeDpElement>'));
$user->tell('<changeDpElement id="loginlink"><a href="'
. 'login.php&act=logout" style="padding-left: 4px">'
. dp_text('Logout') . '</a></changeDpElement>');
if ($env = $user->getEnvironment()) {
$env->tell(array('abstract' => '<changeDpElement id="'
. $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE) . '</changeDpElement>',
'graphical' => '<changeDpElement id="'
. $user->getUniqueId() . '">'
. $user->getAppearance(1, FALSE, $user, 'graphical')
. '</changeDpElement>'), $user);
$user->tell('<window><h1>'
. dp_text('Thank you for registering and welcome to DutchPIPE!')
dp_text('You are now logged in as: <b>%s</b>'), $username)
dp_text('There was a problem registering your new user. Registration failed.')
* Validates a given CAPTCHA code against the code in the database
* @param string $captchaId Captcha id given by the validation form
* @param string $captchaGivenCode User input
* @return boolean TRUE if the given code was valid, FALSE otherwise
$captchaGivenCode = addslashes($captchaGivenCode);
$result = dp_db_query('SELECT captchaId FROM Captcha WHERE '
. ' AND captchaFile=' . dp_db_quote($captchaGivenCode . '.gif'));
* Is the given login info valid for a new user?
* In case of errors, fills array $this->mLastNewUserErrors with one or more
* @param string $userName given user name to check
* @param string $password given password
* @param string $password2 given repeated pssword
* @return boolean TRUE for valid login info, FALSE otherwise
private function validLoginInfo($userName, $password, $password2)
$this->mLastNewUserErrors = array();
$this->mLastNewUserErrors[] = '<li>'
. dp_text('No username was given') . '</li>';
$this->mLastNewUserErrors[] = '<li>' . sprintf(
dp_text('The username must be at least %d characters long'),
$this->mLastNewUserErrors[] = '<li>' . sprintf(
dp_text('The username must be at most %d characters long'),
/*if (FALSE !== ($words = file(DUTCHPIPE_FORBIDDEN_USERNAMES_FILE))
foreach ($words as $word) {
if (FALSE !== dp_strpos($userName, $word)) {
$this->lastUsernameError[] = '<li>' .
dp_text('This username is not allowed, please try again.')
if ($lower_user_name{0} < 'a' || $lower_user_name{0} > 'z') {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('Illegal character in username at position 1 (usernames must start with a letter, digits or other characters are not allowed)')
for ($i = 1; $i < $len; $i++ ) {
if (($lower_user_name{$i} < 'a' || $lower_user_name{$i} > 'z')
&& ($lower_user_name{$i} < '0'
|| $lower_user_name{$i} > '9')) {
$this->mLastNewUserErrors[] = '<li>' . sprintf(
dp_text('Illegal character in username at position %d (you can only use a-z and 0-9)'),
$result = dp_db_query('SELECT userId FROM Users WHERE '
$this->mLastNewUserErrors[] = '<li>'
. dp_text('That username is already in use') . '</li>';
if (!isset ($password) || !dp_strlen($password)) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('No password was given') . '</li>';
if (0 === sizeof($this->mLastNewUserErrors)) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('Your password must be at least 6 characters long')
$this->mLastNewUserErrors[] = '<li>'
. dp_text('Your password must be at most 32 characters long')
$this->mLastNewUserErrors[] = '<li>'
. dp_text('You didn\'t repeat your password') . '</li>';
} elseif ($password !== $password2) {
$this->mLastNewUserErrors[] = '<li>'
. dp_text('The repeated password was different') . '</li>';
return 0 === sizeof($this->mLastNewUserErrors);
* Gets a random avatar image number in order to give guests an avatar
* Checks the avatar image directory for images with the format:
* public/avatar/user<number>.gif
* @return int random avatar image number
return (mt_rand(51, 50 + $entries) - 50);
* Gets the available number of avatars images
* Searches the DPUNIVERSE_AVATAR_STD_PATH directory for files ending with
* @return int the number of available avatar images
while (false !== ($entry = $d->read())) {
if ($entry !== '.' && $entry !== '..' && dp_strlen($entry) > 13
* Sets the user been told anything this request
function setToldSomething()
$this->mrCurrentDpUserRequest->setToldSomething();;
* Has the user been told anything this request?
* @return boolean TRUE if user was told something, FALSE otherwise
function isToldSomething()
return $this->mrCurrentDpUserRequest->isToldSomething();
* Do not tell anything to the current http request?
* @return boolean TRUE for no telling, FALSE otherwise
function isNoDirectTell()
return $this->mNoDirectTell;
* Adds a user to the listener list of the given event
* @param string $event Name of the event
* @param string &$who User listening to event
if (!isset ($this->mAlertEvents[$event])) {
$this->mAlertEvents[$event] = array();
if (FALSE === array_search($who, $this->mAlertEvents[$event], TRUE)) {
$this->mAlertEvents[$event][] = & $who;
* Removes a user from the listener list of the given event
* @param string $event Name of the event
* @param string &$who User listening to event
if (isset ($this->mAlertEvents[$event]) && FALSE !==
($key = array_search($who, $this->mAlertEvents[$event], TRUE))) {
unset ($this->mAlertEvents[$event][$key]);
if (0 === count($this->mAlertEvents[$event])) {
unset ($this->mAlertEvents[$event]);
* Gets list of listening users to the given event
* @param string $event Name of the event
* @return array Users listening to event
if (!isset ($this->mAlertEvents[$event])) {
/* Clean up event listeners list */
foreach ($this->mAlertEvents[$event] as $key => &$who) {
if (!isset ($who) || is_null($who) || $who->isRemoved) {
unset ($this->mAlertEvents[$event][$key]);
$rval = $this->mAlertEvents[$event];
* Shows a line with memory and universe info
printf("Memory: %dKB #Objects: %d #Users: %d #Environments: %d "
round($info['memory_usage'] / 1024),
$info['nr_of_environments'],
$info['nr_of_timeouts']);
* Gets a list of all objects in this DutchPIPE universe
* @return array A list of all objects in this DutchPIPE universe
* @see DpUser::actionOblist()
$rval = '<table border="0" cellspacing="2" cellpadding="2" '
. 'class="dpoblist sortable"><thead><tr>'
. '<th nowrap="nowrap" style="white-space: nowrap">'
. '<div style="float: left">' . dp_text('Unique Id')
. '<th>' . dp_text('Title') . '</th>'
. '<th>' . dp_text('Location') . '</th>'
. '<th>' . dp_text('Environment') . '</th>'
. '<th class="align_c">' . dp_text('User') . '</th>'
. '<th class="align_c">' . dp_text('Adm') . '</th>'
. '<th class="align_c">' . dp_text('NPC') . '</th>'
. '<th class="align_c">' . dp_text('Page') . '</th>'
. '<th>' . dp_text('Created') . '</th>'
. '<th>' . dp_text('Reset') . '</th>'
. '</tr></thead><tbody>';
. '" width="7" height="7" border="0" alt="*" title="*" />';
$ob_sz = count($this->mDpObjects);
foreach ($this->mDpObjects as &$ob) {
. '<td sorttable_customkey="' . $ob_nr . '">'
. '<td><div>' . ucfirst($ob->title) . '</div></td>'
. '<td><div>' . $ob->location . '</div></td>'
. (!($env = $ob->getEnvironment()) ? ' '
. '?location=' : '') . $env->location . '">'
. '<td class="align_c"' . (!$ob->isUser
? ' sorttable_customkey="' . $ob_sz . '"> '
. '<td class="align_c"' . (!$ob->isAdmin
? ' sorttable_customkey="' . $ob_sz . '"> '
. '<td class="align_c"' . (!$ob->isNpc
? ' sorttable_customkey="' . $ob_sz . '"> '
. '<td class="align_c"' . (!$ob->isPage
? ' sorttable_customkey="' . $ob_sz . '"> '
. strftime('%d %b %H:%M', $ob->creationTime)
return $rval . '</tbody></table>';
|