OSDN Git Service

Merge branch 'skinnable-master'
[nucleus-jp/nucleus-next.git] / nucleus / libs / MEMBER.php
index 8172d43..1047d78 100644 (file)
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
 <?php\r
 \r
 /* \r
@@ -1318,3 +1319,1356 @@ class Member
                return $language;\r
        }\r
 }\r
+=======
+<?php
+
+/* 
+ * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
+ * Copyright (C) 2002-2009 The Nucleus Group
+ * 
+ * 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.
+ * (see nucleus/documentation/index.html#license for more info)
+ */
+/**
+ * A class representing site members
+ * 
+ * @license http://nucleuscms.org/license.txt GNU General Public License
+ * @copyright Copyright (C) 2002-2009 The Nucleus Group
+ * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
+ */
+class Member
+{
+       // 1 when authenticated, 0 when not
+       public $loggedin = 0;
+       public $password;               // not the actual password, but rather a MD5 hash
+       private $algorism = 'md5';
+       
+       public $cookiekey;              // value that should also be in the client cookie to allow authentication
+       private $cookie_salt = FALSE;
+       
+       // member info
+       public $id = -1;
+       public $realname;
+       public $displayname;
+       public $email;
+       public $url;
+       public $admin = 0;                      // (either 0 or 1)
+       public $canlogin = 0;           // (either 0 or 1)
+       public $notes;
+       public $autosave = 1;           // if the member use the autosave draft function
+       public $adminskin = 0;          // if the member use the autosave draft function
+       public $bookmarklet = 0;                // if the member use the autosave draft function
+       private $locale = '';
+       
+       /**
+        * Member::__construct()
+        * Constructor for a member object
+        * 
+        * @param       Void
+        * @return      Void
+        * 
+        */
+       public function __construct()
+       {
+               return;
+       }
+       
+       /**
+        * Member::createFromName()
+        * Create a member object for a given displayname
+        * 
+        * @static
+        * @param       String  $displayname    login name
+        * @return      Object  member object
+        * 
+        */
+       public static function &createFromName($displayname)
+       {
+               $mem = new Member();
+               $mem->readFromName($displayname);
+               return $mem;
+       }
+       
+       /**
+        * Member::createFromID()
+        * Create a member object for a given ID
+        * 
+        * @static
+        * @param       Integer $id     id for member
+        */
+       public static function &createFromID($id)
+       {
+               $mem = new Member();
+               $mem->readFromID($id);
+               return $mem;
+       }
+       
+       /**
+        * Member::readFromName()
+        * Read member table in database
+        * 
+        * @param       String  $displayname    login name
+        * @return      Object  SQL resource
+        * 
+        */
+       public function readFromName($displayname)
+       {
+               return $this->read('mname='.DB::quoteValue($displayname));
+       }
+       
+       /**
+        * Member::readFromID()
+        * Read member table in database
+        * 
+        * @param       Integer $id     id for member
+        * @return      Object  SQL resource
+        * 
+        */
+       public function readFromID($id)
+       {
+               return $this->read("mnumber=" . intval($id));
+       }
+       
+       /**
+        * Member::hash()
+        * hash the target string
+        * 
+        * @param       String  $string target string
+        * @return      Void    hashed string
+        */
+       public function hash($string)
+       {
+               switch ( $this->algorism )
+               {
+                       case 'md5':
+                       default:
+                               $string = md5($string);
+               }
+               return $string;
+       }
+       
+       /**
+        * Member::set_cookie_salt()
+        * 
+        * @param       integer $key    secureCookieKey value
+        * @return      void
+        * 
+        */
+       private function set_cookie_salt($key = 0)
+       {
+               if ( !$key )
+               {
+                       $key = 24;
+               }
+               
+               switch( $key )
+               {
+                       case 8:
+                               $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
+                               break;
+                       case 16:
+                               $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
+                               break;
+                       case 24:
+                               $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
+                               break;
+                       case 32:
+                               $this->cookie_salt = serverVar('REMOTE_ADDR');
+                               break;
+                       default:
+                               $this->cookie_salt = 'none';
+               }
+               return;
+       }
+       
+       /**
+        * Member::login()
+        * Tries to login as a given user.
+        * Returns true when succeeded, returns false when failed
+        * 3.40 adds CustomLogin event
+        * 
+        * @param       String  $login  login name for member
+        * @param       String  $password       password for member
+        * @param       Integer $shared whether the user agent is shared or not
+        * 
+        */
+       public function login($login, $password, $shared=1)
+       {
+               global $CONF, $errormessage, $manager;
+               
+               /* TODO: validation for $login, $password, $shared */
+               if ( $login == '' || $password == '' )
+               {
+                       return 0;
+               }
+               /* limiting the length of password to avoid hash collision */
+               $password=i18n::substr($password, 0, 40);
+               
+               /* 
+                * generate cookie salt from secure cookie key settings
+               * (either 'none', 0, 8, 16, 24, or 32)
+               */
+               if ( !$this->cookie_salt )
+               {
+                       $salt = 0;
+                       if ( array_key_exists('secureCookieKey', $CONF) )
+                       {
+                               $salt = $CONF['secureCookieKey'];
+                       }
+                       $this->set_cookie_salt($salt);
+               }
+               
+               $success = 0;
+               $allowlocal = 1;
+               $data = array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal);
+               $manager->notify('CustomLogin', $data);
+               
+               $this->loggedin = 0;
+               if ( $success )
+               {
+                       $this->loggedin = ( $this->readFromName($login) );
+               }
+               elseif ( $allowlocal )
+               {
+                       $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
+               }
+               
+               /* login failed */
+               if ( !$this->loggedin )
+               {
+                       $trimlogin = trim($login);
+                       if ( empty($trimlogin) )
+                       {
+                               $errormessage = "Please enter a username.";
+                       }
+                       else
+                       {
+                               $errormessage = 'Login failed for ' . $login;
+                       }
+                       $data = array('username' => $login);
+                       $manager->notify('LoginFailed', $data);
+                       ActionLog::add(INFO, $errormessage);
+               }
+               /* login success */
+               else
+               {
+                       /* For lower compatibility */
+                       if ( strlen($this->password) === 32 )
+                       {
+                               $this->password = $this->hash($password);
+                       }
+                       
+                       $this->newCookieKey();
+                       $this->setCookies($shared);
+                       
+                       if ( $this->cookie_salt !== 'none' )
+                       {
+                               /* secure cookie key */
+                               $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));
+                               $this->write();
+                       }
+                       
+                       $errormessage = '';
+                       $data = array('member' => &$this, 'username' => $login);
+                       $manager->notify('LoginSuccess', $data);
+                       ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");
+               }
+               
+               return $this->loggedin;
+       }
+       
+       /**
+        * Member::cookielogin()
+        * Login using cookie key
+        * 
+        * @param       String  $login  not used
+        * @param       String  $cookiekey      not used
+        * @return      Boolean login or not
+        */
+       public function cookielogin($login='', $cookiekey='')
+       {
+               global $CONF, $manager;
+               
+               if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
+               {
+                       /* Cookie Authentication */
+                       $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");
+                               
+                       /* TODO: validation for each cookie values */
+                               
+                       /* limiting the length of password to avoid hash collision */
+                       $ck = i18n::substr($ck,0,32);
+                               
+                       /* 
+                        * generate cookie salt from secure cookie key settings
+                       * (either 'none', 0, 8, 16, 24, or 32)
+                       */
+                       if ( !$this->cookie_salt )
+                       {
+                               $salt = 0;
+                               if ( array_key_exists('secureCookieKey', $CONF) )
+                               {
+                                       $salt = $CONF['secureCookieKey'];
+                               }
+                               $this->set_cookie_salt($salt);
+                       }
+                       
+                       if ( $this->cookie_salt !== 'none' )
+                       {
+                               $ck = $this->hash($ck . $this->cookie_salt);
+                       }
+                       $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );
+                       unset($ck);
+                               
+                       /* renew cookies when not on a shared computer */
+                       if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )
+                       {
+                               $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));
+                               $this->setCookies();
+                       }
+               }
+               return $this->loggedin;
+       }
+       
+       /**
+        * Member::logout()
+        * logout and expire cookie
+        * 
+        * @param       Void
+        * @return      Void
+        */
+       public function logout()
+       {
+               global $CONF, $manager;
+               
+               if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
+               {
+                       /* remove cookies on logout */
+                       setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
+                       setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
+                       $data = array('username' => cookieVar("{$CONF['CookiePrefix']}user"));
+                       $manager->notify('Logout', $data);
+               }
+               
+               $this->loggedin = 0;
+               return;
+       }
+       
+       /**
+        * Member::isLoggedIn()
+        * return member is loggedin or not
+        * 
+        * @param       Void
+        * @return      Void
+        */
+       public function isLoggedIn()
+       {
+               return $this->loggedin;
+       }
+       
+       /**
+        * MEMBER:read()
+        * Read member information from the database
+        * 
+        * @param       String  $where  where statement
+        * @return      Resource        SQL resource
+        * 
+        */
+       public function read($where)
+       {
+               // read info
+               $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
+               
+               $row = DB::getRow($query);
+               
+               $this->setRealName($row['mrealname']);
+               $this->setEmail($row['memail']);
+               $this->password = $row['mpassword'];
+               $this->setCookieKey($row['mcookiekey']);
+               $this->setURL($row['murl']);
+               $this->setDisplayName($row['mname']);
+               $this->setAdmin($row['madmin']);
+               $this->id = $row['mnumber'];
+               $this->setCanLogin($row['mcanlogin']);
+               $this->setNotes($row['mnotes']);
+               $this->setLocale($row['mlocale']);
+               $this->setAutosave($row['mautosave']);
+               $this->setAdminSkin($row['madminskin']);
+               $this->setBookmarklet($row['mbkmklt']);
+               
+               return $row ? TRUE : FALSE;
+       }
+       
+       /**
+        * Member::isBlogAdmin()
+        * Returns true if member is an admin for the given blog
+        * (returns false if not a team member)
+        * 
+        * @param       Integer $blogid weblog id
+        * @return      Integer weblog admin or not
+        * 
+        */
+       public function isBlogAdmin($blogid)
+       {
+               $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
+               . ' tblog=' . intval($blogid)
+               . ' and tmember='. $this->getID();
+               $res = DB::getValue($query);
+               if ( $res )
+                       return ($res == 1);
+               else
+                       return 0;
+       }
+       
+       /**
+        * Member::blogAdminRights()
+        * 
+        * @param       integer $blogid ID of target weblog
+        * @return      boolean whether to have admin rights to the weblog or not
+        * 
+        */
+       public function blogAdminRights($blogid)
+       {
+               return ($this->isAdmin() || $this->isBlogAdmin($blogid));
+       }
+       
+       /**
+        * Member::teamRights()
+        * 
+        * @param       integer $blogid ID of target weblog
+        * @return      boolean whether to have admin right to the weblog or not
+        * 
+        */
+       public function teamRights($blogid)
+       {
+               return ($this->isAdmin() || $this->isTeamMember($blogid));
+       }
+       
+       /**
+        * Member::isTeamMember()
+        * Returns true if this member is a team member of the given blog
+        * 
+        * @param       integer $blogid ID of target weblog
+        * @return      boolean whether to join the weblog or not
+        * 
+        */
+       public function isTeamMember($blogid)
+       {
+               $query = 'SELECT * FROM '.sql_table('team').' WHERE'
+                          . ' tblog=' . intval($blogid)
+                          . ' and tmember='. $this->getID();
+               $res = DB::getResult($query);
+               return ($res->rowCount() != 0);
+       }
+       
+       /**
+        * Member::canAddItem()
+        * 
+        * @param       integer $catid  ID of target category
+        * @return      boolean whether to be able to add items to the category or not
+        * 
+        */
+       public function canAddItem($catid)
+       {
+               global $manager;
+               
+               // if this is a 'newcat' style newcat
+               // no blog admin of destination blog -> NOK
+               // blog admin of destination blog -> OK
+               if ( i18n::strpos($catid,'newcat') === 0 )
+               {
+                       // get blogid
+                       list($blogid) = sscanf($catid,"newcat-%d");
+                       return $this->blogAdminRights($blogid);
+               }
+               
+               // category does not exist -> NOK
+               if ( !$manager->existsCategory($catid) )
+               {
+                       return 0;
+               }
+               
+               $blogid = getBlogIDFromCatID($catid);
+               
+               // no team rights for blog -> NOK
+               if (!$this->teamRights($blogid))
+               {
+                       return 0;
+               }
+               
+               // all other cases: OK
+               return 1;
+       }
+       
+       /**
+        * Member::canAlterComment()
+        * Returns true if this member can edit/delete a commentitem. This can be in the
+        * following cases:
+        *        - member is a super-admin
+        *   - member is the author of the comment
+        *   - member is admin of the blog associated with the comment
+        *   - member is author of the item associated with the comment
+        * 
+        * @param       integer $commentid      ID of target comment
+        * @return      boolean delete/edit the comment or not
+        * 
+        */
+       public function canAlterComment($commentid)
+       {
+               if ( $this->isAdmin() )
+               {
+                       return 1;
+               }
+               
+               $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
+                          . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
+                          . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
+               $res = DB::getRow($query);
+               
+               return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());
+       }
+       
+       /**
+        * Member::canAlterItem()
+        * Returns true if this member can edit/delete an item. This is true in the following
+        * cases: - member is a super-admin
+        *             - member is the author of the item
+        *        - member is admin of the the associated blog
+        * 
+        * @param       integer $itemid ID of target item
+        * @return      boolean delete/edit the item or not
+        * 
+        */
+       public function canAlterItem($itemid)
+       {
+               if ($this->isAdmin()) return 1;
+               
+               $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
+               $res = DB::getRow($query);
+               return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);
+       }
+       
+       /**
+        * Member::canBeDeleted()
+        * Return true if member can be deleted. This means that there are no items posted by the member left
+        * 
+        * @param       void
+        * @return      boolean whether there is no items or exists
+        * 
+        */
+       public function canBeDeleted()
+       {
+               $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
+               return ( $res->rowCount() == 0 );
+       }
+       
+       /**
+        * Member::canUpdateItem()
+        * returns true if this member can move/update an item to a given category,
+        * false if not (see comments fot the tests that are executed)
+        * 
+        * @param       integer $itemid
+        * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)
+        * @return      boolean whether being able to update the item or not
+        * 
+        */
+       public function canUpdateItem($itemid, $newcat)
+       {
+               global $manager;
+               
+               // item does not exists -> NOK
+               if ( !$manager->existsItem($itemid,1,1) )
+               {
+                       return 0;
+               }
+               
+               // cannot alter item -> NOK
+               if (!$this->canAlterItem($itemid))
+               {
+                       return 0;
+               }
+               
+               // if this is a 'newcat' style newcat
+               // no blog admin of destination blog -> NOK
+               // blog admin of destination blog -> OK
+               if ( i18n::strpos($newcat, 'newcat') === 0 )
+               {
+                       // get blogid
+                       list($blogid) = sscanf($newcat, 'newcat-%d');
+                       return $this->blogAdminRights($blogid);
+               }
+               
+               // category does not exist -> NOK
+               if (!$manager->existsCategory($newcat))
+               {
+                       return 0;
+               }
+               
+               // get item
+               $item =& $manager->getItem($itemid,1,1);
+               
+               // old catid = new catid -> OK
+               if ($item['catid'] == $newcat)
+               {
+                       return 1;
+               }
+               
+               // not a valid category -> NOK
+               $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
+               if ( !$validCat )
+               {
+                       return 0;
+               }
+               
+               // get destination blog
+               $item =& $manager->getItem($itemid, 1, 1);
+               $source_blogid = $item['blogid'];
+               $dest_blogid = getBlogIDFromCatID($newcat);
+               
+               // not a team member of destination blog -> NOK
+               if ( !$this->teamRights($dest_blogid) )
+               {
+                       return 0;
+               }
+               
+               // if member is author of item -> OK
+               if ( $item['authorid'] == $this->getID() )
+               {
+                       return 1;
+               }
+               
+               // if member has admin rights on both blogs: OK
+               if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
+               {
+                       return 1;
+               }
+               
+               // all other cases: NOK
+               return 0;
+       }
+       
+       /**
+        * Member::setCookies()
+        * Sets the cookies for the member
+        * 
+        * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire
+        *                              at the end of the session in this case.
+        * @return      void
+        * 
+        */
+       public function setCookies($shared = 0)
+       {
+               global $CONF;
+               
+               if ( $CONF['SessionCookie'] || $shared )
+               {
+                       $lifetime = 0;
+               }
+               else
+               {
+                       $lifetime = time()+2592000;
+               }
+               
+               setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
+               setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
+               
+               // make sure cookies on shared pcs don't get renewed
+               if ( $shared )
+               {
+                       setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
+               }
+               return;
+       }
+       
+       /**
+        * Member::sendActivationLink()
+        * Send activation mail
+        * 
+        * @param       string  $type   activation type
+        * @param       string  $extra  extra info
+        * @return      void
+        */
+       public function sendActivationLink($type, $extra='')
+       {
+               global $CONF;
+               
+               if ( !isset($CONF['ActivationDays']) )
+               {
+                       $CONF['ActivationDays'] = 2;
+               }
+               
+               // generate key and URL
+               $key = $this->generateActivationEntry($type, $extra);
+               $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
+               
+               // choose text to use in mail
+               switch ( $type )
+               {
+                       case 'register':
+                               $message = _ACTIVATE_REGISTER_MAIL;
+                               $subject = _ACTIVATE_REGISTER_MAILTITLE;
+                               break;
+                       case 'forgot':
+                               $message = _ACTIVATE_FORGOT_MAIL;
+                               $subject = _ACTIVATE_FORGOT_MAILTITLE;
+                               break;
+                       case 'addresschange':
+                               $message = _ACTIVATE_CHANGE_MAIL;
+                               $subject = _ACTIVATE_CHANGE_MAILTITLE;
+                               break;
+                       default;
+               }
+               
+               // fill out variables in text
+               $aVars = array(
+                       'siteName' => $CONF['SiteName'],
+                       'siteUrl' => $CONF['IndexURL'],
+                       'memberName' => $this->getDisplayName(),
+                       'activationUrl' => $url,
+                       'activationDays' => $CONF['ActivationDays']
+               );
+               
+               $message = Template::fill($message, $aVars);
+               $subject = Template::fill($subject, $aVars);
+               
+               // send mail
+               NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
+               
+               ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
+               return;
+       }
+       
+       /**
+        * Member::getAdminBlogs()
+        * Returns an array of all blogids for which member has admin rights
+        * 
+        * @param       void
+        * @return      array   weblog IDs in which this member has admin rights
+        * 
+        */
+       public function getAdminBlogs()
+       {
+               $blogs = array();
+               
+               if ($this->isAdmin())
+               {
+                       $query = 'SELECT bnumber as blogid from '.sql_table('blog');
+               }
+               else
+               {
+                       $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
+               }
+               
+               $res = DB::getResult($query);
+               if ( $res->rowCount() > 0 )
+               {
+                       foreach ( $res as $row )
+                       {
+                               array_push($blogs, $row['blogid']);
+                       }
+               }
+               return $blogs;
+       }
+       
+       /**
+        * Member::getTeamBlogs()
+        * Returns an array of all blogids for which member has team rights
+        * 
+        * @param       boolean $incAdmin       whether checking weblog admin rights or not
+        * @return      array   weblog IDs in which this member join
+        * 
+        */
+       public function getTeamBlogs($incAdmin = 1)
+       {
+               $incAdmin = intval($incAdmin);
+               $blogs = array();
+               
+               if ( $this->isAdmin() && $incAdmin )
+               {
+                       $query = 'SELECT bnumber as blogid from '.sql_table('blog');
+               }
+               else
+               {
+                       $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
+               }
+               
+               $res = DB::getResult($query);
+               if ( $res->rowCount() > 0 )
+               {
+                       foreach ( $res as $row )
+                       {
+                               array_push($blogs, $row['blogid']);
+                       }
+               }
+               return $blogs;
+       }
+       
+       /**
+        * Member::getNotifyFromMailAddress()
+        * 
+        * Returns an email address from which notification of commenting/karma voting can
+        * be sent. A suggestion can be given for when the member is not logged in
+        * 
+        * @param       String  $suggest
+        * @return      String  mail address or suggestion
+        */
+       public function getNotifyFromMailAddress($suggest = "")
+       {
+               global $CONF;
+               if ( $this->isLoggedIn() )
+               {
+                       return $this->getDisplayName() . " <" . $this->getEmail() . ">";
+               }
+               else if ( NOTIFICATION::address_validation($suggest) )
+               {
+                       return $suggest;
+               }
+               return $CONF['AdminEmail'];
+       }
+       
+       /**
+        * Member::write()
+        * Write data to database
+        * 
+        * @param       void
+        * @return      void
+        * 
+        */
+       public function write()
+       {
+               $query =  'UPDATE '.sql_table('member')
+                       . ' SET mname=' . DB::quoteValue($this->displayname) . ', '
+                          . 'mrealname='. DB::quoteValue($this->realname) . ', '
+                          . 'mpassword='. DB::quoteValue($this->password) . ', '
+                          . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '
+                          . 'murl=' . DB::quoteValue($this->url) . ', '
+                          . 'memail=' . DB::quoteValue($this->email) . ', '
+                          . 'madmin=' . intval($this->admin) . ', '
+                          . 'mnotes=' . DB::quoteValue($this->notes) . ', '
+                          . 'mcanlogin=' . intval($this->canlogin) . ', '
+                          . 'mlocale=' . DB::quoteValue($this->locale) . ', '
+                          . 'madminskin=' . DB::quoteValue($this->adminskin) . ', '
+                          . 'mbkmklt=' . DB::quoteValue($this->bookmarklet) . ', '
+                          . 'mautosave=' . intval($this->autosave) . ' '
+                       . 'WHERE mnumber=' . intval($this->id);
+               DB::execute($query);
+               return;
+       }
+       
+       public function checkCookieKey($key)
+       {
+               return ( ($key != '') && ( $key == $this->getCookieKey() ) );
+       }
+       
+       public function checkPassword($pw)
+       {
+               /* for lower compatibility (md5) */
+               if ( strlen($this->password) === 32 )
+               {
+                       return (md5($pw) == $this->password);
+               }
+               return ($this->hash($pw) == $this->password);
+       }
+       
+       public function getRealName()
+       {
+               return $this->realname;
+       }
+       
+       public function setRealName($name)
+       {
+               $this->realname = $name;
+       }
+       
+       public function getEmail()
+       {
+               return $this->email;
+       }
+       
+       public function setEmail($email)
+       {
+               $this->email = $email;
+       }
+       
+       public function getPassword()
+       {
+               return $this->password;
+       }
+       
+       public function setPassword($pwd)
+       {
+               $this->password = $this->hash($pwd);
+       }
+       
+       public function getCookieKey()
+       {
+               return $this->cookiekey;
+       }
+       
+       /**
+        * Member::newCookieKey()
+        * Generate new cookiekey, save it, and return it
+        * 
+        * @param       void
+        * @return      void
+        * 
+        */
+       public function newCookieKey()
+       {
+               mt_srand( (double) microtime() * 1000000);
+               $this->cookiekey = $this->hash(uniqid(mt_rand()));
+               $this->write();
+               return $this->cookiekey;
+       }
+       
+       public function setCookieKey($val)
+       {
+               $this->cookiekey = $val;
+       }
+       
+       public function getURL()
+       {
+               return $this->url;
+       }
+       
+       public function setURL($site)
+       {
+               $this->url = $site;
+       }
+       
+       public function setAdminSkin($skin)
+       {
+               $this->adminskin = $skin;
+       }
+
+       public function setBookmarklet($skin)
+       {
+               $this->bookmarklet = $skin;
+       }
+       
+       public function getAdminSkin()
+       {
+               return $this->adminskin;
+       }
+
+       public function getBookmarklet()
+       {
+               return $this->bookmarklet;
+       }
+
+       public function getLocale()
+       {
+               return $this->locale;
+       }
+       
+       public function setLocale($locale)
+       {
+               if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)
+                && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
+               {
+                       $locale = '';
+               }
+               $this->locale = $locale;
+               return;
+       }
+       
+       public function setDisplayName($nick)
+       {
+               $this->displayname = $nick;
+       }
+       
+       public function getDisplayName()
+       {
+               return $this->displayname;
+       }
+       
+       public function isAdmin()
+       {
+               return $this->admin;
+       }
+       
+       public function setAdmin($val)
+       {
+               $this->admin = $val;
+       }
+       
+       public function canLogin()
+       {
+               return $this->canlogin;
+       }
+       
+       public function setCanLogin($val)
+       {
+               $this->canlogin = $val;
+       }
+       
+       public function getNotes()
+       {
+               return $this->notes;
+       }
+       
+       public function setNotes($val)
+       {
+               $this->notes = $val;
+       }
+       
+       public function getAutosave()
+       {
+               return $this->autosave;
+       }
+       
+       public function setAutosave($val)
+       {
+               $this->autosave = $val;
+               return;
+       }
+       
+       /**
+        * Member::getID()
+        * 
+        * @param       void
+        * @return      integer id of this member object
+        * 
+        */
+       public function getID()
+       {
+               return $this->id;
+       }
+       
+       /**
+        * Member::exists()
+        * Returns true if there is a member with the given login name
+        * 
+        * @static
+        * @param       string  $name   target name
+        * @return      boolean whether target name exists or not
+        */
+       public static function exists($name)
+       {
+               $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));
+               return ( $r->rowCount() != 0 );
+       }
+       
+       /**
+        * Member::existsID()
+        * Returns true if there is a member with the given ID
+        * 
+        * @static
+        * @param       integer $id     target id
+        * @return      boolean whether target id exists or not
+        * 
+        */
+       public static function existsID($id)
+       {
+               $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));
+               return ( $r->rowCount() != 0 );
+       }
+       
+       /**
+        * Member::isNameProtected()
+        *  Checks if a username is protected.
+        *  If so, it can not be used on anonymous comments
+        * 
+        * @param       string  $name   target name
+        * @return      boolean whether the name exists or not
+        * 
+        */
+       public function isNameProtected($name)
+       {
+               // extract name
+               $name = strip_tags($name);
+               $name = trim($name);
+               return self::exists($name);
+       }
+       
+       /**
+        * Member::create()
+        * Adds a new member
+        * 
+        * @static
+        * @param       String  $name
+        * @param       String  $realname
+        * @param       String  $password
+        * @param       String  $email
+        * @param       String  $url
+        * @param       String  $admin
+        * @param       String  $canlogin
+        * @param       String  $notes
+        * @return      String  1 if success, others if fail
+        */
+       static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
+       {
+               if ( !NOTIFICATION::address_validation($email) )
+               {
+                       return _ERROR_BADMAILADDRESS;
+               }
+               
+               /* TODO: this method should be in MEMBER class, not globalfunctions */
+               if ( !isValidDisplayName($name) )
+               {
+                       return _ERROR_BADNAME;
+               }
+               
+               if ( self::exists($name) )
+               {
+                       return _ERROR_NICKNAMEINUSE;
+               }
+               
+               if ( !$realname )
+               {
+                       return _ERROR_REALNAMEMISSING;
+               }
+               
+               /* TODO: check the number of characters */
+               if ( !$password )
+               {
+                       return _ERROR_PASSWORDMISSING;
+               }
+               
+               /* 
+                *  begin if: sometimes user didn't prefix the URL with http:// or https://,
+                *  this cause a malformed URL. Let's fix it.
+                */
+               
+               if ( !preg_match('#^https?://#', $url) )
+               {
+                       $url = 'http://' . $url;
+               }
+               
+               $name           = DB::quoteValue($name);
+               $realname       = DB::quoteValue($realname);
+               /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */
+               $password       = DB::quoteValue(md5($password));
+               $email          = DB::quoteValue($email);
+               $url            = DB::quoteValue($url);
+               $admin          = (integer) $admin;
+               $canlogin       = (integer) $canlogin;
+               $notes          = DB::quoteValue($notes);
+               
+               $query = "INSERT INTO %s"
+                      . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"
+                      . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";
+               $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);
+               DB::execute($query);
+               
+               ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
+               
+               return 1;
+       }
+       
+       /**
+        * Member::getActivationInfo()
+        * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
+        * 
+        * @static
+        * @param       string  $key    activation key
+        * @return      mixed   return 0 if failed, else return activation table object
+        * 
+        */
+       public static function getActivationInfo($key)
+       {
+               $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);
+               $res = DB::getResult($query);
+               
+               if ( !$res || ($res->rowCount() == 0) )
+               {
+                       return 0;
+               }
+               return $res->fetch();
+       }
+       
+       /**
+        * Member::generateActivationEntry()
+        * Creates an account activation key
+        * addresschange -> old email address
+        * 
+        * @param       string  $type   one of the following values (determines what to do when activation expires)
+        *                              'register'      (new member registration)
+        *                              'forgot'        (forgotton password)
+        *                              'addresschange' (member address has changed)
+        * @param       string  $extra  extra info (needed when validation link expires)
+        * @return      string  activation key
+        */
+       public function generateActivationEntry($type, $extra = '')
+       {
+               // clean up old entries
+               $this->cleanupActivationTable();
+               
+               // kill any existing entries for the current member (delete is ok)
+               // (only one outstanding activation key can be present for a member)
+               DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
+               
+               // indicates if the member can log in while the link is active
+               $canLoginWhileActive = false;
+               switch ( $type )
+               {
+                       case 'forgot':
+                               $canLoginWhileActive = true;
+                               break;
+                       case 'register':
+                               break;
+                       case 'addresschange':
+                               $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
+                               break;
+               }
+               
+               $ok = false;
+               while ( !$ok )
+               {
+                       // generate a random key
+                       srand((double)microtime()*1000000);
+                       $key = $this->hash(uniqid(rand(), true));
+                       
+                       // attempt to add entry in database
+                       // add in database as non-active
+                       $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';
+                       $query = sprintf($query
+                               , sql_table('activation')
+                               , DB::quoteValue($key)
+                               , DB::formatDateTime()
+                               , intval($this->getID())
+                               , DB::quoteValue($type)
+                               , DB::quoteValue($extra)
+                       );
+                       if ( DB::execute($query) !== FALSE )
+                               $ok = true;
+               }
+               
+               // mark member as not allowed to log in
+               if ( !$canLoginWhileActive )
+               {
+                       $this->setCanLogin(0);
+                       $this->write();
+               }
+               
+               // return the key
+               return $key;
+       }
+       
+       /**
+        * Member::activate()
+        * Inidicates that an activation link has been clicked and any forms displayed
+        * there have been successfully filled out.
+        * 
+        * @param       string  $key    activation key
+        * @return      boolean
+        * 
+        */
+       public function activate($key)
+       {
+               // get activate info
+               $info = self::getActivationInfo($key);
+               
+               // no active key
+               if ( !$info )
+               {
+                       return false;
+               }
+               
+               switch ( $info['vtype'] )
+               {
+                       case 'forgot':
+                               // nothing to do
+                               break;
+                       case 'register':
+                               // set canlogin value
+                               global $CONF;
+                               DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));
+                               break;
+                       case 'addresschange':
+                               // reset old 'canlogin' value
+                               list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);
+                               DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));
+                               break;
+               }
+               
+               // delete from activation table
+               DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));
+               
+               // success!
+               return true;
+       }
+       
+       /**
+        * Member::cleanupActivationTable()
+        * Cleans up entries in the activation table. All entries older than 2 days are removed.
+        * (static)
+        * 
+        * @param       void
+        * @return      void
+        */
+       public function cleanupActivationTable()
+       {
+               $actdays = 2;
+               if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
+               {
+                       $actdays = intval($CONF['ActivationDays']);
+               }
+               else
+               {
+                       $CONF['ActivationDays'] = 2;
+               }
+               $boundary = time() - (60 * 60 * 24 * $actdays);
+               
+               // 1. walk over all entries, and see if special actions need to be performed
+               $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
+               $res = DB::getResult($query);
+               
+               foreach ( $res as $row )
+               {
+                       switch ( $row['vtype'] )
+                       {
+                               case 'register':
+                                       // delete all information about this site member. registration is undone because there was
+                                       // no timely activation
+                                       include_once($DIR_LIBS . 'ADMIN.php');
+                                       Admin::deleteOneMember(intval($row['vmember']));
+                                       break;
+                               case 'addresschange':
+                                       // revert the e-mail address of the member back to old address
+                                       list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);
+                                       DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));
+                                       break;
+                               case 'forgot':
+                                       // delete the activation link and ignore. member can request a new password using the
+                                       // forgot password link
+                                       break;
+                       }
+               }
+               
+               // 2. delete activation entries for real
+               $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
+               DB::execute($query);
+               return;
+       }
+       
+       /**
+        * Member::$language
+        * 
+        * @obsolete
+        * @param       void
+        * @return      void
+        * 
+        */
+       public $language = '';
+       /**
+        * Member::getLanguage()
+        * 
+        * @obsolete
+        * @param       void
+        * @return      void
+        * 
+        */
+       public function getLanguage()
+       {
+               if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )
+               {
+                       $language = '';
+               }
+               return $language;
+       }
+}
+>>>>>>> skinnable-master