<?php
/******************************************************************************
* SiteBar 3 - The Bookmark Server for Personal and Team Use. *
* Copyright (C) 2003-2006 Ondrej Brablc <http://brablc.com/mailto?o> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
******************************************************************************/
require_once('./inc/database.inc.php');
require_once('./inc/errorhandler.inc.php');
require_once('./inc/page.inc.php');
define ('SB_ADMIN', 1);
define ('SB_ANONYM', 2);
class SB_UserManager extends SB_ErrorHandler
{
var $user = null;
var $config;
var $useCookies = true;
var $setupDone;
var $showEmail = false;
var $db;
var $pmode; // personal mode
var $plugins = array(); // plugin cache
var $pluginPaths = array();
var $uid;
var $username;
var $name;
var $email;
var $comment;
var $verified;
var $approved;
var $demo;
var $params = array('config'=>array(),'user'=>array());
var $hiddenFolders = array();
function SB_UserManager()
{
$this->db =& SB_Database::staticInstance();
/* Read SiteBar configuration - must be the first step ! */
if ($this->db->hasTable('sitebar_config'))
{
$rset = $this->db->select(null, 'sitebar_config');
$this->config = $this->db->fetchRecord($rset);
}
else
{
$this->config['release'] = '';
}
if ($this->db->currentRelease() != $this->config['release'])
{
header('Location: config.php');
exit;
}
$this->explodeParams($this->config['params'],'config');
if (strlen($this->getParamB64('config','baseurl')))
{
SB_Page::absBaseUrl($this->getParamB64('config','baseurl'));
}
$this->pmode = $this->getParam('config','personal_mode');
$this->loadPlugins();
/* Check whether Admin has password if not we will run setup */
$rset = $this->db->select(null, 'sitebar_user',
array( 'uid'=> SB_ADMIN, '^1'=> 'AND', 'pass'=>null));
$this->setupDone = ($this->db->fetchRecord($rset)===false);
$anonym = $this->getUser(SB_ANONYM);
$this->explodeParams($anonym['params'],'default');
if (!$this->isLogged())
{
if (!$anonym)
{
$this->error('Database corrupted - missing anonymous account!');
}
else
{
$this->initUser($anonym);
unset($this->user['pass']); // Security
}
}
$lang = $this->getParam('user','lang');
if (!$lang)
{
$l =& SB_Localizer::staticInstance();
$browserLang = $l->getBrowserLang();
if (!$this->getParam('config','lang'))
{
$this->setParam('config','lang',$l->langDefault);
}
$lang = $browserLang?$browserLang:$this->getParam('config','lang');
$this->setParam('user','lang',$lang);
}
// Set our language
SB_SetLanguage($lang);
// Check if we have plugin that changes rights
foreach ($this->plugins as $plugin)
{
$execute = $plugin['prefix'].'Init';
if (function_exists($execute))
{
$execute($this);
}
}
}
function statistics(&$data)
{
$rset = $this->db->select('count(*) count', 'sitebar_user');
$rec = $this->db->fetchRecord($rset);
$data['users'] = $rec['count']-1;
$rset = $this->db->select('count(*) count', 'sitebar_group');
$rec = $this->db->fetchRecord($rset);
$data['groups'] = $rec['count'];
}
function & staticInstance()
{
static $um;
if (!$um)
{
// Here we give chance to the plugins to change any aspect of this
// class, be creating an ascendant class.
$count = 0;
$dirName = "./plugins";
$classes = '';
if (is_dir($dirName) && ($dir = opendir($dirName)))
{
while (($plugin = readdir($dir)) !== false)
{
$plugdir = $dirName.'/'.$plugin;
$umclass = $plugdir.'/usermanager.inc.php';
if (!is_dir($plugdir) || !is_file($umclass))
{
continue;
}
$fp = fopen($umclass, "r");
if (!$fp)
{
die("Cannot open existing file $umclass!");
}
$count++;
$skip = 1;
while ( !feof($fp) )
{
$buffer = fgets($fp, 4096);
if (strpos($buffer, 'class')===0)
{
# Eat {
fgets($fp, 4096);
$skip = 0;
$sub = "SB_UserManager$count";
$sup = ($count>1?'SB_UserManager'.($count-1):'SB_UserManager');
$classes .= "class $sub extends $sup\n{\n";
$classes .= " function $sub() { \$this->$sup(); }\n";
continue;
}
if ($skip)
{
continue;
}
$classes .= $buffer;
}
fclose($fp);
}
closedir($dir);
}
if ($count)
{
// echo("<pre>$classes</pre>");
eval($classes);
eval("\$um = new SB_UserManager$count();");
}
else
{
$um = new SB_UserManager();
}
}
return $um;
}
function initUser(&$rec)
{
$this->user = $rec;
$this->uid = $rec['uid'];
$this->username = $rec['username'];
$this->email = $rec['email'];
$this->name = SB_safeVal($rec,'name');
$this->comment = SB_safeVal($rec,'comment');
$this->verified = $rec['verified'];
$this->approved = $rec['approved'];
$this->demo = $rec['demo'];
$this->explodeParams($rec['params'],'user');
if ($this->getParam('user','use_hiding') && $this->getParam('user','hidden_folders'))
{
$ids = explode(':',$this->getParam('user','hidden_folders'));
$this->hiddenFolders = array();
foreach ($ids as $id)
{
$this->hiddenFolders[$id] = 1;
}
}
}
function setCookie($name, $value='', $expires=null)
{
if (!$this->useCookies)
{
return;
}
if ($expires===null)
{
// Default expiration 7 days
$expires = time()+60*60*24*7;
}
if (!$value)
{
// Remove now
$expires = time()-60*60;
}
setcookie($name, $value, $expires);
$_COOKIE[$name] = $value;
}
function explodeParams(&$params, $prefix)
{
$default = array();
switch ($prefix)
{
case 'config':
$default['auth']='';
$default['allow_contact']=1;
$default['allow_sign_up']=1;
$default['allow_user_groups']=0;
$default['allow_user_trees']=1;
$default['allow_user_tree_deletion']=1;
$default['allow_anonymous_export']=1;
$default['max_session_time']=60*60*24*365; // 1 year
$default['comment_impex']=0;
$default['comment_limit']=1024;
$default['integrator_url']=base64_encode('http://my.sitebar.org/integrator.php');
$default['max_icon_age']=30;
$default['max_icon_cache']=1000;
$default['max_icon_size']=20000;
$default['max_icon_user']=100;
$default['filter_users_limit']=30;
$default['filter_groups_limit']=10;
$default['personal_mode']=0;
$default['feed_reader_url']= base64_encode('http://www.google.com/reader/preview/*/feed/%s');
$default['search_engine_url']= base64_encode('http://www.google.com/search'.
'?q=%SEARCH%'.
'&sourceid=sitebar-search'.
'&start=0'.
'&ie=utf-8'.
'&oe=utf-8');
$default['search_engine_ico']= base64_encode('http://www.google.com/favicon.ico');
$default['allow_custom_search_engine']=1;
$default['sender_email']='';
$default['show_logo']=1;
$default['show_statistics']=1;
$default['skin']='Modern';
$default['use_compression']=1;
$default['use_conv_engine']=1;
$default['use_favicon_cache']=1;
$default['use_hit_counter']=1;
$default['use_mail_features']=1;
$default['use_outbound_connection']=1;
$default['users_must_verify_email']=0;
$default['users_must_be_approved']=0;
$default['version_check_interval']=60*60*24*30;
$default['web_search_user_agents']=base64_encode('Googlebot|Slurp|Scooter|MSNBOT');
break;
case 'user':
$default = $this->params['default'];
break;
case 'default':
case 'tmp':
default:
$default['allow_given_membership']=1;
$default['allow_info_mails']=1;
$default['auto_close']=1;
$default['auto_retrieve_favicon']=1;
$default['default_folder']='';
$default['default_search']='all';
$default['default_search_tool']='backend';
$default['default_url']='http://';
$default['extern_commander']=0;
$default['expert_mode']=0;
$default['feed_reader_url']= '';
$default['hidden_folders']='';
$default['hide_xslt']=0;
$default['private_over_ssl_only']=0;
$default['link_sort_mode']='abc';
$default['menu_icon']=0;
$default['mix_mode']='nodes';
$default['paste_mode']='ask';
$default['show_acl']=1;
$default['use_search_engine']=1;
$default['use_search_engine_iframe']=1;
$default['use_favicons']=1;
$default['use_hiding']=1;
$default['use_tooltips']=1;
$default['use_trash']=1;
break;
}
// Clear old values
$this->params[$prefix] = $default;
// If we have some params then explode them
if ($params)
{
foreach (explode(';',$params) as $param)
{
$pair = explode('=',$param);
$key = array_shift($pair);
$value = array_shift($pair);
$this->setParam($prefix,$key,$value);
}
}
switch ($prefix)
{
case 'config':
if (!strlen($this->getParam('config','sender_email')))
{
$admin = $this->getUser(SB_ADMIN);
if ($admin['email']!='')
{
$this->setParam('config', 'sender_email', $admin['email']);
}
}
if (!$this->getParam('config', 'use_outbound_connection'))
{
$this->setParam('config', 'use_favicon_cache', 0);
$this->setParam('config', 'version_check_interval', 0);
}
if ($this->getParam('config', 'auth'))
{
$this->setParam('config', 'allow_sign_up', 0);
}
if (!$this->getParam('config', 'use_mail_features'))
{
$this->setParam('config', 'users_must_verify_email', 0);
}
break;
case 'user':
if (!$this->getParam('config','use_hit_counter'))
{
if (!in_array($this->getParam('user','link_sort_mode'),array('abc','added')))
{
$this->setParam('user', 'link_sort_mode', 'abc');
}
}
if ($this->getParam('user', 'link_sort_mode')=='visit')
{
$this->setParam('user', 'link_sort_mode', 'waiting');
}
if (!$this->getParam('config', 'use_outbound_connection') ||
!$this->getParam('user', 'use_favicons'))
{
$this->setParam('user', 'auto_retrieve_favicon', 0);
}
if (!$this->getParam('config', 'allow_custom_search_engine'))
{
$this->setParam('user', 'search_engine_url', $this->getParam('config','search_engine_url'));
$this->setParam('user', 'search_engine_ico', $this->getParam('config','search_engine_ico'));
}
if ($this->getParam('user', 'search_engine_url')=='')
{
$this->setParam('user', 'search_engine_url', $this->getParam('config','search_engine_url'));
$this->setParam('user', 'search_engine_ico', $this->getParam('config','search_engine_ico'));
}
if ($this->getParam('user', 'feed_reader_url')=='')
{
$this->setParam('user', 'feed_reader_url', $this->getParam('config','feed_reader_url'));
}
if ($this->isAnonymous() && isset($_COOKIE['SB3SETTINGS']))
{
foreach (explode(';', $_COOKIE['SB3SETTINGS']) as $param)
{
list($key,$value) = explode('=',$param);
$this->setParam('user', $key, $value);
}
}
break;
}
}
function implodeParams($prefix)
{
$params = array();
foreach ($this->params[$prefix] as $name => $value)
{
$params[] = $name.'='.$value;
}
return implode(';',$params);
}
function getParam($prefix, $name, $default=null)
{
return isset($this->params[$prefix][$name])
?$this->params[$prefix][$name]:$default;
}
function getParamB64($prefix, $name, $default=null)
{
return isset($this->params[$prefix][$name])
?base64_decode($this->params[$prefix][$name]):$default;
}
function getParamCheck($prefix, $name)
{
return $this->getParam($prefix,$name)?null:'';
}
function setParam($prefix, $name, $value)
{
$this->params[$prefix][$name] = $value;
}
function setParamB64($prefix, $name, $value)
{
$this->params[$prefix][$name] = base64_encode($value);
}
function isAnonymous()
{
return $this->uid == SB_ANONYM;
}
function isAdmin()
{
if (!$this->user) return false;
if ($this->uid == SB_ADMIN) return true;
static $isAdmin = null;
if ($isAdmin === null)
{
$rset = $this->db->select('g.gid',
'sitebar_group g natural join sitebar_member m',
array('uid'=>$this->uid, '^1'=> 'AND',
'g.gid'=> $this->config['gid_admins']));
$rec = $this->db->fetchRecord($rset);
$isAdmin = is_array($rec);
}
return $isAdmin;
}
function isModerator($gid = null)
{
$groups = $this->getModeratedGroups($this->uid);
if (!count($groups))
{
return false;
}
return $gid?in_array($gid, array_keys($groups)):true;
}
function canUseMail()
{
return $this->verified && $this->getParam('config','use_mail_features');
}
function accessDenied()
{
$this->error('Access denied!');
if (!$this->verified && $this->getParam('config','users_must_verify_email'))
{
$this->warn('This SiteBar server requires your email address to be verified in order to use some functions.');
$this->warn('Please click on the "%s" command and finish the verification procedure.', 'Verify Email');
}
}
function isAuthorized($command, $ignoreAnonymous=false, $gid=null, $nid=null, $lid=null)
{
$acl = null;
$node = null;
$link = null;
$readOnly = false;
if ($lid)
{
$tree =& SB_Tree::staticInstance();
$link = $tree->getLink($lid);
$nid = $link->id_parent;
if ($link->private && !$tree->inMyTree($nid))
{
return false;
}
}
if ($nid)
{
$tree =& SB_Tree::staticInstance();
$node = $tree->getNode($nid);
$readOnly = $node->id_parent && !$node->parentHasRight('update');
if (!$node)
{
return false;
}
$acl =& $node->getACL();
if (!$acl)
{
return false;
}
if ($node && $node->deleted_by != null)
{
if ($command != 'Purge Folder' && $command != 'Undelete')
{
return false;
}
}
}
if (!$this->isAnonymous())
{
$mustApprove = $this->getParam('config','users_must_be_approved');
$mustVerify = $this->getParam('config','users_must_verify_email');
// Hide commands if we are not setup completely
if ($mustVerify && !$this->verified || $mustApprove && !$this->approved)
{
//
switch ($command)
{
case 'Browse Folder':
case 'Show Link News':
case 'Show All Links':
case 'Open Integrator':
case 'Add Page Bookmarklet':
case 'Contact Admin':
case 'Display Topic':
case 'Download Bookmarks':
case 'Email Link':
case 'Help':
case 'Log Out':
case 'Verify Email':
case 'Email Verified':
case 'Invalid Token':
break;
default:
$command = 'N/A';
}
}
}
// Check if we have plugin that changes rights
foreach ($this->plugins as $plugin)
{
if (in_array($command, $plugin['authorization']))
{
$execute = $plugin['prefix'] . 'IsAuthorized';
$result = $execute($this, $command, $ignoreAnonymous, $gid, $node, $acl, $link);
if ($result !== null)
{
return $result;
}
}
}
// If !$acl then we just ask for command list.
switch ($command)
{
case 'Add Page Bookmarklet':
case 'Display Topic':
case 'Email Link':
case 'Email Verified':
case 'Help':
case 'Invalid Token':
case 'New Password':
case 'Open Integrator':
return true;
case 'Set Up':
return !$this->setupDone;
case 'Sign Up':
return ($this->isAnonymous() || $ignoreAnonymous)
&& $this->getParam('config','allow_sign_up');
case 'Log In':
return $this->isAnonymous() || $ignoreAnonymous;
case 'Log Out':
return !$this->isAnonymous() || $ignoreAnonymous;
case 'Reset Password':
return $this->isAnonymous() &&
$this->getParam('config','use_mail_features');
case 'Verify Email':
return !$this->pmode &&
!$this->isAnonymous() &&
!$this->verified &&
!$this->demo &&
$this->email!='' &&
$this->getParam('config','use_mail_features');
case 'Browse Folder':
case 'Show Link News':
case 'Show All Links':
return !$this->getParam('user','hide_xslt') && (!$acl || $acl['allow_select']);
case 'Contact Admin':
return $this->getParam('config','use_mail_features') &&
($this->getParam('config','allow_contact') || !$this->isAnonymous());
case 'Contact Moderator':
return !$this->pmode && !$this->isAnonymous();
case 'Add Link':
case 'Retrieve Link Information':
case 'Add Folder':
case 'Mark as Default':
return !$acl || $acl['allow_insert'];
case 'Paste': // Paste does its own checking later
case 'Import Bookmarks':
return !$acl || $acl['allow_insert'];
case 'Hide Folder':
case 'Unhide Subfolders':
case 'Unhide Trees':
return !$this->isAnonymous() &&
$this->getParam('user','use_hiding');
case 'Copy':
case 'Copy Link':
return !$acl || $acl['allow_select'];
case 'Export Bookmarks':
case 'Download Bookmarks':
case 'Show Feed URL':
return (!$this->isAnonymous() || $this->getParam('config','allow_anonymous_export'))
&& (!$acl || $acl['allow_select']);
case 'Custom Order':
return !$acl || ($acl['allow_update'] && !$readOnly);
case 'Folder Properties':
case 'Properties':
return !$acl || ($acl['allow_update']);
case 'Validate Links':
case 'Validation':
return !$acl || ($acl['allow_update'] && $this->getParam('config','use_outbound_connection'));
case 'Export Description':
// Select is enough but, currently update is necessary
return !$acl || ($acl['allow_select']);
case 'Import Description':
return !$acl || ($acl['allow_update'] && $this->getParam('config','comment_impex'));
case 'Delete Link':
return !$acl || ($acl['allow_delete']);
case 'Delete Folder':
return !$acl || ($acl['allow_delete']);
case 'Delete Tree':
return !$acl || $this->isAdmin() ||
( $acl['allow_delete'] && $acl['allow_purge'] && $this->getParam('config','allow_user_tree_deletion'));
case 'Purge Folder':
return $this->getParam('user','use_trash') && (!$acl || $acl['allow_purge']);
case 'Undelete':
return $this->getParam('user','use_trash') && (!$acl || ($acl['allow_delete'] && $acl['allow_insert']));
case 'Maintain Trees':
case 'Order of Trees':
case 'User Settings':
return !$this->isAnonymous();
case 'Session Settings':
return $this->isAnonymous();
case 'Personal Data':
// Either we are number 1 user, or we have SiteBar authorization
return !$this->isAnonymous() && ($this->uid==SB_ADMIN || !strlen($this->getParam('config', 'auth')));
case 'Unhide Folders':
return !$this->pmode && !$this->isAnonymous();
case 'Delete Account':
return !$this->isAnonymous() && !$this->demo && $this->uid != SB_ADMIN;
case 'Membership':
case 'Security':
return !$this->isAnonymous() && !$this->pmode;
case 'Create Tree':
return !$this->isAnonymous() &&
$this->getParam('config','allow_user_trees');
case 'Active Users':
case 'Approve All Users':
case 'Approve User':
case 'Approve Users':
case 'Create User':
case 'Default User Settings':
case 'Delete All Inactive Users':
case 'Delete Inactive Users':
case 'Delete User':
case 'Export Bookmarks Settings':
case 'Favicon Management':
case 'Filter Users':
case 'Inactive Users':
case 'Maintain Users':
case 'Modify User':
case 'Most Active Users':
case 'Pending Unverified Users':
case 'Pending User':
case 'Pending Users':
case 'Pending Users':
case 'Pending Verified Users':
case 'Purge Cache':
case 'Reject All Users':
case 'Reject Users':
case 'Show All Icons':
case 'SiteBar Settings':
return $this->isAdmin();
case 'Send Email to User':
case 'Send Email to All':
return $this->isAdmin() && $this->verified
&& $this->getParam('config','use_mail_features');
case 'Create Group':
case 'Delete Group':
case 'Filter Groups':
return !$this->isAnonymous() && !$this->pmode && ($this->isAdmin() || $this->getParam('config','allow_user_groups'));
case 'Maintain Groups':
return !$this->isAnonymous() && !$this->pmode && ($this->isAdmin() || $this->isModerator() || $this->getParam('config','allow_user_groups'));
case 'Group Properties':
case 'Group Members':
case 'Group Moderators':
return !$this->isAnonymous() && !$this->pmode && $this->isModerator($gid);
case 'Send Email to Members':
case 'Send Email to Moderators':
return !$this->isAnonymous() && !$this->pmode && ($this->isAdmin() || $this->isModerator($gid))
&& $this->verified
&& $this->getParam('config','use_mail_features');
}
return false;
}
function canMove($sid,$tid,$isnode=true)
{
if ($this->isAuthorized(($isnode?'Delete Folder':'Delete Link'), false, null, $sid))
{
$tree = SB_Tree::staticInstance();
$sroot = $tree->getRootNode($sid);
$troot = $tree->getRootNode($tid);
if ($sroot->id == $troot->id)
{
return true;
}
else // Another tree and the source tree does not have purge right
{
return $this->isAuthorized('Purge Folder', false, null, $sid);
}
}
return false;
}
function & inPlaceCommands()
{
static $commands = array
(
'Log In',
'Log Out',
'Sign Up',
'Set Up',
'Email Verified',
'Invalid Token',
);
return $commands;
}
// expires as delta time in seconds
function login($username, $pass, $expires=0)
{
$auth = $this->getParam('config', 'auth');
$added = array();
$rec = null;
// We have another setting and do not know the user or we know him and he is not admin
$useAltAuth = strlen($auth) && (!is_array($rec));
// Plugin based authorization
if ($useAltAuth)
{
$rec = $this->getUserByUsername($username);
if ($rec['uid'] == SB_ADMIN)
{
$useAltAuth = false;
}
}
// Plugin based authorization
if ($useAltAuth)
{
$file = './plugins/' . $auth . '/auth.inc.php';
if (!is_file($file))
{
$this->error('Authentication plugin %s missing!', $auth);
return false;
}
include_once($file);
if (!authenticate($this, $username, $pass, $added))
{
$this->error('Unknown username or wrong password!');
return false;
}
}
else
{
$rec = $this->getUserByUsername($username, $pass);
}
// We have either wrong password or we do not exists
if (!is_array($rec))
{
// Still ok, create new user.
if ($useAltAuth)
{
if (!strlen($added['name']))
{
$added['name'] = $username;
}
// Auto add user to database
$uid = $this->signUp(
$username,
'NOPASSWORD',
$added['email'],
$added['name'],
$added['comment'],
$createdByAdmin=true,
$verified=true,
$demoAccount=false,
$lang=null);
if (!$uid)
{
return false;
}
$tree =& SB_Tree::staticInstance();
$tree->addRoot($uid, $username);
return $this->login($username, $pass, $expires);
}
else
{
$this->error('Unknown username or wrong password!');
}
return false;
}
$this->initUser($rec);
unset($this->user['pass']); // Security
// Noone from outside can reconstruct the password, because
// only half of its md5 is used to generate another md5 and
// hence we use password noone from outside can guess the code.
// Is it obscure, unsercure or slow? Please tell me.
$code = md5(substr(md5($pass),6,18).date('r').$username);
$expires = ($expires?$expires+time():0);
$this->db->insert('sitebar_session', array(
'uid' => $this->uid,
'code' => $code,
'created' => array('now' => null),
'expires' => $expires,
'ip' => $_SERVER['REMOTE_ADDR']
));
$this->setCookie('SB3AUTH', $code, $expires);
$this->remote = false;
return true;
}
function logout()
{
$this->user = null;
$this->setCookie('SB3AUTH');
}
function isLogged()
{
if (!isset($_COOKIE['SB3AUTH']))
{
return false;
}
// Check if we have valid session
$rset = $this->db->select('uid', 'sitebar_session',
array('code' => $_COOKIE['SB3AUTH'],
'^1' => 'AND (expires <= 0 OR expires>=unix_timestamp())'));
$rec = $this->db->fetchRecord($rset);
// Delete invalid cookie
if (!is_array($rec))
{
$this->setCookie('SB3AUTH');
return false;
}
// If yes then let us go in.
// - first update some statistics
$this->db->update('sitebar_user', array
(
'visits' => array('visits+'=>'1'),
'visited'=>array('now'=>null)
)
,array
(
'uid' => $rec['uid']
));
$rset = $this->db->select(null, 'sitebar_user', array('uid' => $rec['uid']));
$rec = $this->db->fetchRecord($rset);
// User deleted?
if (!is_array($rec))
{
$this->setCookie('SB3AUTH');
return false;
}
$this->initUser($rec);
unset($this->user['pass']); // Security
return true;
}
function setUp($username,$pass,$email,$name)
{
$rset = $this->db->update('sitebar_user',
array(
'username' => $username,
'email' => $email,
'pass' => array('md5' => $pass),
'name' => $name,
'verified' => 1,
),
array('uid'=>SB_ADMIN));
return $this->login($username, $pass);
}
function saveConfiguration()
{
$rset = $this->db->update('sitebar_config',
array('params' => $this->implodeParams('config')));
return true;
}
/*** User functions ***/
function signUp($username, $pass, $email, $name, $comment,
$createdByAdmin=false, $verified=false, $demoAccount=false, $lang=null)
{
$rset = $this->db->select(null, 'sitebar_user', array(
'ucase(username)' => array('ucase' => $username)));
$user = $this->db->fetchRecord($rset);
if (is_array($user))
{
$this->error('This username is already used. Try to login using your email address!');
return false;
}
$params = '';
if ($lang)
{
$params = 'lang='.$lang;
}
$this->db->insert('sitebar_user', array(
'username' => $username,
'pass' => array('md5' => $pass),
'email' => $email,
'name' => $name,
'comment' => $comment,
'verified' => ($verified?1:0),
'approved' => ($this->getParam('config', 'users_must_be_approved')?0:1),
'demo' => ($demoAccount?1:0),
'params' => $params
));
$newUID = $this->db->getLastId();
$closedMatchingGroups = array();
if (strlen($email))
{
// Join groups where verification is not neccessary and
// return list of groups when it is.
$closedMatchingGroups = $this->autoJoinGroups($newUID, $email, $createdByAdmin);
}
if ($this->getParam('config','use_mail_features') && !$createdByAdmin)
{
// If verification is not required, we must check whether the user should
// verify because of pending membership. However not when he must verify anyway.
if (!$this->getParam('config','users_must_verify_email'))
{
if (count($closedMatchingGroups))
{
$groups = implode(', ', $closedMatchingGroups);
$url = $this->getVerificationUrl($newUID);
$subject = SB_T('SiteBar: Email Verification');
$msg = SB_P('usermanager::auto_verify_email', array($groups,$url));
// Verify email
$this->sendMail(array('name'=>$name, 'email'=>$email), $subject, $msg);
}
$this->showEmail = true;
$user = $this->getUser($newUID);
$this->showEmail = false;
$paraName = 'usermanager::signup_info';
$paraAtt = array($user['completename'],SB_Page::absBaseUrl());
if ($this->getParam('config', 'users_must_be_approved'))
{
$paraName = 'usermanager::signup_approval';
$paraAtt[] = $this->getApproveUserUrl($username);
$paraAtt[] = $this->getRejectUserUrl($username);
$paraAtt[] = $this->getPendingUsersUrl();
}
$this->mailToAdmins('SiteBar: New SiteBar User', $paraName, $paraAtt, '', '', $lang);
}
}
// Always greater then zero
return $newUID;
}
function mailToAdmins($subject, $bodyid, $bodyparams, $from_name='', $from_email='', $lang=null)
{
$case = '';
if ($subject == 'SiteBar: Contact Admin')
{
$caseNum = intval($this->db->getData('admin','case'))+1;
$this->db->setData('admin','case',$caseNum);
$case = sprintf(' [#%05d]', $caseNum);
}
$admins = $this->getMembers($this->config['gid_admins']);
foreach ($admins as $uid => $user)
{
$this->explodeParams($user['params'], 'tmp');
SB_SetLanguage($this->getParam('tmp', 'lang'));
$this->sendMail($user, SB_T($subject).$case, SB_P($bodyid, $bodyparams), $from_name, $from_email);
}
SB_SetLanguage($lang?$lang:$this->getParam('user','lang'));
}
function getVerificationUrl($uid='')
{
if (!$uid)
{
$uid = $this->uid;
}
require_once('./inc/token.inc.php');
$token = SB_Token::staticInstance();
return $token->createVerifyToken($uid);
}
function getPendingUsersUrl()
{
return SB_Page::absBaseUrl().
'command.php'.
'?command=Pending%20Users';
}
function getApproveUserUrl($username)
{
return SB_Page::absBaseUrl().
'command.php'.
'?command=Approve%20User'.
'&username='.$username;
}
function getRejectUserUrl($username)
{
return SB_Page::absBaseUrl().
'command.php'.
'?command=Reject%20User'.
'&username='.$username;
}
function decodeValue($value, $header=false)
{
if ($header)
{
return '=?UTF-8?B?'.base64_encode($value).'?=';
}
else
{
$tmp = str_replace("\n", "\x0D\x0A", $value);
return stripslashes($tmp);
}
}
function sendMail($user, $subject, $msg, $from_name='', $from_email='', $cc=false)
{
$headers = "Content-Type: text/plain; ".CHARSET."\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$sender = $this->getParam('config','sender_email');
$email = $user['email'];
if (!$email)
{
return false;
}
if (!$from_email)
{
$from_name = SB_T('SiteBar Server');
$from_email = $sender;
}
if ($from_name)
{
$headers .= sprintf("From: \"%s\" <%s>\n",
$this->decodeValue($from_name, true), $from_email);
}
else
{
$headers .= sprintf("From: %s\n", $from_email);
}
if ($cc)
{
$headers .= sprintf("bcc: %s\n", $from_email);
}
$headers .= sprintf("Reply-to: \"%s\" <%s>\n",
$this->decodeValue($from_name, true), $from_email);
$headers .= sprintf("Sender: \"%s\" <%s>\n",
$this->decodeValue(SB_T('SiteBar Server'), true), $sender);
$headers .= sprintf("Return-path: <%s>\n", $sender);
// Do not set "To:" - it would duplicate mails.
if (!mail($email,
$this->decodeValue($subject, true),
$this->decodeValue($msg), $headers))
{
return false;
}
return true;
}
function getLastModeratorGroups($uid)
{
$groups = $this->getModeratedGroups($uid);
$names = array();
foreach ($groups as $gid => $rec)
{
$members = $this->getMembers($gid);
$moderators = $this->getMembers($gid, true);
if (count($moderators)==1 && count($members)>1)
{
$names[] = $rec['name'];
}
}
return $names;
}
function removeUser($uid)
{
$names = $this->getLastModeratorGroups($uid);
if (count($names))
{
$this->error('Cannot delete user because he is the only moderator of the following group(s): %s!',
array(implode(', ',$names)));
return false;
}
$rset = $this->db->delete('sitebar_member', array('uid' => $uid));
$rset = $this->db->delete('sitebar_user', array('uid' => $uid));
return true;
}
function deleteAccount()
{
$names = $this->getLastModeratorGroups($this->uid);
if (count($names))
{
$this->error('You are the last moderator of used group(s): %s. Account cannot be deleted!',
array(implode(', ',$names)));
return false;
}
$tree =& SB_Tree::staticInstance();
$this->db->clearUserData($this->uid);
if (!$this->removeUser($this->uid))
{
return false;
}
if (!$tree->changeOwner($this->uid, SB_ADMIN, $this->username))
{
return false;
}
return true;
}
function personalData($username, $pass, $email, $name, $comment)
{
if ($email != $this->email && $this->verified)
{
$this->verified = 0;
}
if ($username != $this->username)
{
$rset = $this->db->select(null, 'sitebar_user', array(
'ucase(username)' => array('ucase' => $username)));
$user = $this->db->fetchRecord($rset);
if (is_array($user))
{
$this->error('This username is already used. Did you forget your password?');
return false;
}
}
$this->db->update('sitebar_user',
array
(
'username' => $username,
'name' => $name,
'email' => $email,
'comment' => $comment,
'verified' => $this->verified,
),
array('uid'=>$this->uid));
$this->username = $username;
$this->email = $email;
if ($pass)
{
$this->changePassword($this->uid, $pass);
$this->login($username, $pass);
}
return true;
}
function saveUserParams($uid=null, $prefix='user')
{
if ($uid===null)
{
$uid = $this->uid;
}
$this->db->update('sitebar_user',
array('params' => $this->implodeParams($prefix)),
array('uid'=>$uid));
}
function modifyUser($uid, $pass, $columns)
{
if ($pass)
{
$this->changePassword($uid, $pass);
}
$this->db->update('sitebar_user', $columns, array('uid'=>$uid));
}
function checkPassword($uid, $pass)
{
$auth = $this->getParam('config', 'auth');
$useAltAuth = strlen($auth) && ($uid != SB_ADMIN);
// Plugin based authorization
if ($useAltAuth)
{
$addedRealName = '';
$addedComment = '';
$user = $this->getUser($uid);
include_once('./plugins/' . $auth . '/auth.inc.php');
return authenticate($this, $user['username'], $pass, $addedRealName, $addedComment);
}
else
{
$rset = $this->db->select(null,'sitebar_user', array(
'pass' => array('md5' => $pass),
'^1' => 'AND',
'uid'=>$uid));
return is_array($this->db->fetchRecord($rset));
}
}
function changePassword($uid, $pass)
{
$this->db->update('sitebar_user',
array('pass' => array('md5' => $pass)),
array('uid'=>$uid));
}
/*** Group functions ***/
function addGroup($name, $comment, $uid, $allow_addself, $allow_contact, $auto_join)
{
$rset = $this->db->select(null, 'sitebar_group', array(
'name' => $name));
$group = $this->db->fetchRecord($rset);
if (is_array($group))
{
$this->error('Group name "%s" is already used!', array($group['name']));
return false;
}
$this->db->insert('sitebar_group', array(
'name' => $name,
'comment' => $comment,
'allow_addself' => $allow_addself,
'allow_contact' => $allow_contact,
'is_usergroup' => $this->isAdmin()?0:1,
'auto_join' => $auto_join,
));
$gid = $this->db->getLastId();
$this->addMember($gid, $uid, true);
// Always greater then zero
return $gid;
}
function removeGroup($gid)
{
$this->db->delete('sitebar_acl', array('gid'=>$gid));
$this->db->delete('sitebar_member', array('gid'=>$gid));
$this->db->delete('sitebar_group', array('gid'=>$gid));
}
function updateGroup($gid, $name, $comment, $allow_addself, $allow_contact, $auto_join)
{
$this->db->update('sitebar_group',
array('name' => $name,
'comment' => $comment,
'allow_addself' => $allow_addself,
'allow_contact' => $allow_contact,
'auto_join' => $auto_join),
array('gid'=>$gid));
}
function addMember($gid, $uid, $moderator=false)
{
$this->db->purgeCache('acl_nodes', $uid);
$this->db->purgeCache('vis_nodes', $uid);
$this->db->insert('sitebar_member', array(
'gid' => $gid,
'uid' => $uid,
'moderator' => $moderator?1:0),
array(1062)); // Ignore duplicate membership
}
function removeMember($gid, $uid)
{
$this->db->purgeCache('acl_nodes', $uid);
$this->db->purgeCache('vis_nodes', $uid);
$this->db->delete('sitebar_member',
array('gid'=>$gid, '^1'=>'AND', 'uid'=>$uid));
}
function updateMember($gid, $uid, $moderator)
{
$this->db->update('sitebar_member',
array('moderator' => ($moderator?1:0)),
array('gid'=>$gid, '^1'=>'AND', 'uid'=>$uid));
}
function showPublic()
{
return in_array($this->config['gid_everyone'],
array_keys($this->getUserGroups()));
}
function useUserFilter()
{
$res = $this->db->select('count(*) count', 'sitebar_user');
$rec = $this->db->fetchRecord($res);
return $rec['count']>=$this->getParam('config','filter_users_limit');
}
function useGroupFilter()
{
$res = $this->db->select('count(*) count', 'sitebar_group');
$rec = $this->db->fetchRecord($res);
return $rec['count']>=$this->getParam('config','filter_groups_limit');
}
/*** Search functions ***/
function getUser($uid)
{
$rset = $this->db->select(null, 'sitebar_user',
array('uid' => $uid));
$users = array( $this->db->fetchRecord($rset));
$this->enrichUser($users);
return $users[0];
}
function firstSession($uid)
{
$rset = $this->db->select('min(created) signup', 'sitebar_session', array('uid' => $uid));
$rec = $this->db->fetchRecord($rset);
return $rec['signup'];
}
function getUserByUsername($username, $pass='')
{
$where = array('ucase(username)'=>array('ucase'=>$username));
if (strlen($pass))
{
$where['^1'] = 'AND';
$where['pass'] = array('md5' => $pass);
}
$rset = $this->db->select(null, 'sitebar_user', $where);
return $this->db->fetchRecord($rset);
}
function getUserByEmail($email)
{
$rset = $this->db->select(null, 'sitebar_user', array('ucase(email)'=>array('ucase'=>$email)));
return $this->db->fetchRecord($rset);
}
function enrichUser(&$users)
{
foreach ($users as $uid => $rec)
{
$fullname = $rec['username'];
if ($rec['name'])
{
$fullname .= ' - '. $rec['name'];
}
$users[$uid]['fullname'] = $fullname;
if ($rec['email'] && ($this->isAdmin() || $this->showEmail) )
{
$fullname .= ' <'. $rec['email'] . '>';
}
$users[$uid]['completename'] = $fullname;
}
}
function getUsers()
{
$rset = $this->db->select('uid, username, email, verified, approved, name, params',
'sitebar_user', null, 'ucase(username)');
$users = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
if ($rec['uid'] == SB_ANONYM) continue;
$users[$rec['uid']] = $rec;
}
$this->enrichUser($users);
return $users;
}
function getPending($verified=-1)
{
$where = array('approved'=>0);
if ($verified!=-1)
{
$where['^1'] = 'AND';
$where['verified'] = $verified;
}
$rset = $this->db->select('uid, username, email, verified, name, params', 'sitebar_user', $where, 'ucase(username)');
$users = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
if ($rec['uid'] == SB_ANONYM) continue;
$users[$rec['uid']] = $rec;
}
$this->enrichUser($users);
return $users;
}
function getUsersUsingVisited($days, $cmp, $order)
{
$rset = $this->db->select
(
'uid, username, email, name, visited, visits',
'sitebar_user',
'visited '.$cmp.' DATE_ADD( now() , INTERVAL -'.intval($days).' DAY)',
$order
);
$users = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
if ($rec['uid'] == SB_ANONYM) continue;
$users[$rec['uid']] = $rec;
}
$this->enrichUser($users);
return $users;
}
function getMembers($gid, $moderators=false)
{
$where = array('gid'=>$gid);
if ($moderators)
{
$where['^1'] = 'AND';
$where['moderator'] = 1;
}
$rset = $this->db->select('u.uid, username, email, verified, approved, name, params, moderator',
'sitebar_user u natural join sitebar_member m', $where, 'ucase(username)');
$members = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
$members[$rec['uid']] = $rec;
}
$this->enrichUser($members);
return $members;
}
function getModeratedGroups($uid=null)
{
if (!$uid)
{
$uid = $this->uid;
}
$rset = $this->db->select('g.gid, g.name',
'sitebar_group g natural join sitebar_member m',
array('uid'=>$uid, '^1'=> 'AND', 'moderator'=>1 ), 'name');
$groups = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
$groups[$rec['gid']] = $rec;
}
return $groups;
}
function getUserGroups($uid=null)
{
if (!$uid)
{
$uid = $this->uid;
}
$rset = $this->db->select('g.gid, g.name, moderator',
'sitebar_group g natural join sitebar_member m',
array('uid'=>$uid), 'name');
$groups = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
$groups[$rec['gid']] = $rec;
}
return $groups;
}
function getUserGroupsForSecurity($uid=null)
{
return $this->getUserGroups();
}
function getGroup($gid)
{
$rset = $this->db->select(null, 'sitebar_group',
array( 'gid'=> $gid), 'is_usergroup, name');
$group = $this->db->fetchRecord($rset);
if (!$group)
{
$this->error('Group has already been deleted.');
return null;
}
return $group;
}
function getGroupByName($name)
{
$rset = $this->db->select(null, 'sitebar_group',
array( 'name'=> $name), 'is_usergroup, name');
$group = $this->db->fetchRecord($rset);
if (!$group)
{
$this->error('Group has already been deleted.');
return null;
}
return $group;
}
function getGroups()
{
$rset = $this->db->select(null, 'sitebar_group', null, 'name');
$groups = array();
foreach ($this->db->fetchRecords($rset) as $rec)
{
$this->renameGroup($rec);
$groups[$rec['gid']] = $rec;
}
return $groups;
}
function isHiddenGroup(&$rec, $command)
{
return false;
}
function renameGroup(&$rec)
{
if ($rec['gid']<=2)
{
$rec['name'] = SB_T($rec['name']);
}
if ($rec['is_usergroup'])
{
$rec['long_name'] = SB_T('UsrGrp: %s', $rec['name']);
}
else
{
$rec['long_name'] = $rec['name'];
}
}
function autoJoinGroups($uid, $email, $verified=false)
{
$groups = array();
if ($email == '')
{
return $groups;
}
// Add member to all groups that have auto_join filled and matching
foreach ($this->getGroups() as $gid => $rec)
{
$res = $this->canJoinGroup($rec, $uid, $email, $verified);
if ($res === true)
{
// Only add based on auto join - do not add when
// he just can join the group
if (strlen($rec['auto_join']))
{
$this->addMember($gid, $uid);
}
}
elseif ($res === null) // Verification missing, note group name
{
// Otherwise return
$groups[] = $rec['name'];
}
}
return $groups;
}
function canJoinGroup(&$groupRec, $uid=null, $email=null, $verified=null)
{
if ($uid===null) $uid = $this->uid;
if ($email===null) $email = $this->email;
if ($verified===null) $verified= $this->verified;
// If he can add himself then just let himself
if ($groupRec['allow_addself'])
{
return true;
}
// If he cannot add himself directly then try auto join pattern
if (strlen($groupRec['auto_join']))
{
if ($groupRec['auto_join']{0} != '/')
{
$groupRec['auto_join'] = '/'.$groupRec['auto_join'].'/i';
}
if (preg_match($groupRec['auto_join'], $email))
{
// If open group or mail already verified or demo account
// then allow direct add self
if ($verified || $this->demo)
{
return true;
}
else
{
// Otherwise signal that verification required
return null;
}
}
}
return false;
}
function loadPlugins()
{
$this->plugins = array();
$this->pluginPaths = array();
$dirName = "./plugins";
if (is_dir($dirName) && ($dir = opendir($dirName)))
{
while (($plugin = readdir($dir)) !== false)
{
$plugdir = $dirName.'/'.$plugin;
if (!is_dir($plugdir))
{
continue;
}
$authfile = $plugdir.'/auth.inc.php';
if (is_file($authfile) && $this->getParam('config', 'auth') != $plugin)
{
continue;
}
$plugfile = $plugdir.'/command.inc.php';
if (is_file($plugfile))
{
include($plugfile);
$this->pluginPaths[] = $plugdir;
$plugin['dir'] = $plugdir;
// $plugin gets injected
$this->plugins[] = $plugin;
}
}
closedir($dir);
}
if (count($this->plugins))
{
$l =& SB_Localizer::staticInstance();
$l->setPlugins($this->pluginPaths);
}
}
}
?>