OSDN Git Service

MERGE: リビジョン1672から1680にかけて行われた修正のうち、反映されてないものを追加。
[nucleus-jp/nucleus-next.git] / nucleus / libs / MEMBER.php
1 <?php
2
3 /*
4  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
5  * Copyright (C) 2002-2009 The Nucleus Group
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * (see nucleus/documentation/index.html#license for more info)
12  */
13 /**
14  * A class representing site members
15  *
16  * @license http://nucleuscms.org/license.txt GNU General Public License
17  * @copyright Copyright (C) 2002-2009 The Nucleus Group
18  * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
19  */
20 class MEMBER {
21         
22         // 1 when authenticated, 0 when not
23         public $loggedin = 0;
24         public $password;               // not the actual password, but rather a MD5 hash
25         
26         public $cookiekey;              // value that should also be in the client cookie to allow authentication
27         
28         // member info
29         public $id = -1;
30         public $realname;
31         public $displayname;
32         public $email;
33         public $url;
34         public $admin = 0;                      // (either 0 or 1)
35         public $canlogin = 0;           // (either 0 or 1)
36         public $notes;
37         public $autosave = 1;           // if the member use the autosave draft function
38         
39         /*
40          * NOTE: $locale value obsoleted $language value since version 4.0
41          */
42         public $language = '';
43         public $locale = '';
44         
45         /**
46          * Constructor for a member object
47          */             
48         public function MEMBER()
49         {
50                 // do nothing
51                 return;
52         }
53
54         /**
55          * Create a member object for a given displayname
56          *
57          * @static               
58          */             
59         public static function &createFromName($displayname)
60         {
61                 $mem = new MEMBER();
62                 $mem->readFromName($displayname);
63                 return $mem;
64         }
65
66         /**
67          * Create a member object for a given ID
68          *
69          * @static      
70          */
71         public static function &createFromID($id)
72         {
73                 $mem = new MEMBER();
74                 $mem->readFromID($id);
75                 return $mem;
76         }
77         
78         public function readFromName($displayname)
79         {
80                 return $this->read("mname='".sql_real_escape_string($displayname)."'");
81         }
82         
83         public function readFromID($id)
84         {
85                 return $this->read("mnumber=" . intval($id));
86         }
87         
88         /*
89          * Tries to login as a given user.
90          * Returns true when succeeded, returns false when failed
91          * 3.40 adds CustomLogin event
92          */
93         public function login($login, $password)
94         {
95                 global $manager;
96                 
97                 if ( $login == '' || $password == '' )
98                 {
99                         return 0;
100                 }
101                 
102                 $success = 0;
103                 $allowlocal = 1;
104                 $manager->notify('CustomLogin', array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal));
105                 
106                 $this->loggedin = 0;
107                 if ( $success )
108                 {
109                         $this->loggedin = ( $this->readFromName($login) );
110                 }
111                 elseif ( $allowlocal )
112                 {
113                         $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
114                 }
115                 return $this->loggedin;
116         }
117         
118         /*
119          * Login using cookie key
120          */
121         public function cookielogin($login, $cookiekey)
122         {
123                 $this->loggedin = ( $this->readFromName($login) && $this->checkCookieKey($cookiekey) );
124                 return $this->loggedin;
125         }
126         
127         public function logout()
128         {
129                 $this->loggedin = 0;
130                 return;
131         }
132         
133         public function isLoggedIn()
134         {
135                 return $this->loggedin;
136         }
137         
138         /*
139          * Read member information from the database 
140          */
141         public function read($where) {
142                 // read info
143                 $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
144                 
145                 $res = sql_query($query);
146                 $obj = sql_fetch_object($res);
147                 
148                 $this->setRealName($obj->mrealname);
149                 $this->setEmail($obj->memail);
150                 $this->password = $obj->mpassword;
151                 $this->setCookieKey($obj->mcookiekey);
152                 $this->setURL($obj->murl);
153                 $this->setDisplayName($obj->mname);
154                 $this->setAdmin($obj->madmin);
155                 $this->id = $obj->mnumber;
156                 $this->setCanLogin($obj->mcanlogin);
157                 $this->setNotes($obj->mnotes);
158                 $this->setLocale($obj->mlocale);
159                 $this->setAutosave($obj->mautosave);
160                 
161                 return sql_num_rows($res);
162         }
163         
164         /*
165          * Returns true if member is an admin for the given blog
166          * (returns false if not a team member)
167          */
168         public function isBlogAdmin($blogid)
169         {
170                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
171                            . ' tblog=' . intval($blogid)
172                            . ' and tmember='. $this->getID();
173                 $res = sql_query($query);
174                 if ( sql_num_rows($res) == 0 )
175                         return 0;
176                 else
177                         return ( sql_result($res,0,0) == 1 );
178         }
179         
180         public function blogAdminRights($blogid)
181         {
182                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));
183         }
184         
185         public function teamRights($blogid)
186         {
187                 return ($this->isAdmin() || $this->isTeamMember($blogid));
188         }
189
190         /*
191          * Returns true if this member is a team member of the given blog
192          */
193         function isTeamMember($blogid)
194         {
195                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'
196                            . ' tblog=' . intval($blogid)
197                            . ' and tmember='. $this->getID();
198                 $res = sql_query($query);
199                 return (sql_num_rows($res) != 0);
200         }
201
202         function canAddItem($catid)
203         {
204                 global $manager;
205                 
206                 // if this is a 'newcat' style newcat
207                 // no blog admin of destination blog -> NOK
208                 // blog admin of destination blog -> OK
209                 if ( strstr($catid,'newcat') )
210                 {
211                         // get blogid
212                         list($blogid) = sscanf($catid,"newcat-%d");
213                         return $this->blogAdminRights($blogid);
214                 }
215                 
216                 // category does not exist -> NOK
217                 if ( !$manager->existsCategory($catid) )
218                 {
219                         return 0;
220                 }
221                 
222                 $blogid = getBlogIDFromCatID($catid);
223                 
224                 // no team rights for blog -> NOK
225                 if (!$this->teamRights($blogid))
226                 {
227                         return 0;
228                 }
229                 
230                 // all other cases: OK
231                 return 1;
232         }
233         
234         /*
235          * Returns true if this member can edit/delete a commentitem. This can be in the
236          * following cases:
237          *        - member is a super-admin
238          *   - member is the author of the comment
239          *   - member is admin of the blog associated with the comment
240          *   - member is author of the item associated with the comment
241          */
242         public function canAlterComment($commentid)
243         {
244                 if ( $this->isAdmin() )
245                 {
246                         return 1;
247                 }
248         
249                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
250                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
251                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
252                 $res = sql_query($query);
253                 $obj = sql_fetch_object($res);
254         
255                 return ($obj->cauthor == $this->getID()) or $this->isBlogAdmin($obj->blogid) or ($obj->iauthor == $this->getID());
256         }
257         
258         /*
259          * Returns true if this member can edit/delete an item. This is true in the following
260          * cases: - member is a super-admin
261          *             - member is the author of the item
262          *        - member is admin of the the associated blog
263          */
264         public function canAlterItem($itemid)
265         {
266                 if ($this->isAdmin()) return 1;
267
268                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
269                 $res = sql_query($query);
270                 $obj = sql_fetch_object($res);
271                 return ($obj->iauthor == $this->getID()) or $this->isBlogAdmin($obj->iblog);
272         }
273
274         /*
275          * Return true if member can be deleted. This means that there are no items
276          * posted by the member left
277          */
278         public function canBeDeleted()
279         {
280                 $res = sql_query('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
281                 return (sql_num_rows($res) == 0);
282         }
283
284         /*
285          * returns true if this member can move/update an item to a given category,
286          * false if not (see comments fot the tests that are executed)
287          *
288          * @param itemid
289          * @param newcat (can also be of form 'newcat-x' with x=blogid)
290          */
291         public function canUpdateItem($itemid, $newcat)
292         {
293                 global $manager;
294
295                 // item does not exists -> NOK
296                 if ( !$manager->existsItem($itemid,1,1) )
297                 {
298                         return 0;
299                 }
300                 
301                 // cannot alter item -> NOK
302                 if (!$this->canAlterItem($itemid))
303                 {
304                         return 0;
305                 }
306                 
307                 // if this is a 'newcat' style newcat
308                 // no blog admin of destination blog -> NOK
309                 // blog admin of destination blog -> OK
310                 if (strstr($newcat,'newcat'))
311                 {
312                         // get blogid
313                         list($blogid) = sscanf($newcat,'newcat-%d');
314                         return $this->blogAdminRights($blogid);
315                 }
316                 
317                 // category does not exist -> NOK
318                 if (!$manager->existsCategory($newcat))
319                 {
320                         return 0;
321                 }
322                 
323                 // get item
324                 $item =& $manager->getItem($itemid,1,1);
325                 
326                 // old catid = new catid -> OK
327                 if ($item['catid'] == $newcat)
328                 {
329                         return 1;
330                 }
331                 
332                 // not a valid category -> NOK
333                 $validCat = quickQuery('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
334                 if ( !$validCat )
335                 {
336                         return 0;
337                 }
338                 
339                 // get destination blog
340                 $source_blogid = getBlogIDFromItemID($itemid);
341                 $dest_blogid = getBlogIDFromCatID($newcat);
342                 
343                 // not a team member of destination blog -> NOK
344                 if ( !$this->teamRights($dest_blogid) )
345                 {
346                         return 0;
347                 }
348                 
349                 // if member is author of item -> OK
350                 if ( $item['authorid'] == $this->getID() )
351                 {
352                         return 1;
353                 }
354                 
355                 // if member has admin rights on both blogs: OK
356                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
357                 {
358                         return 1;
359                 }
360                 
361                 // all other cases: NOK
362                 return 0;
363         }
364
365         /**
366           * Sets the cookies for the member
367           *
368           * @param shared
369           *             set this to 1 when using a shared computer. Cookies will expire
370           *             at the end of the session in this case.
371           */
372         public function setCookies($shared = 0)
373         {
374                 global $CONF;
375                 
376                 if ( $CONF['SessionCookie'] || $shared )
377                 {
378                         $lifetime = 0;
379                 }
380                 else
381                 {
382                         $lifetime = time()+2592000;
383                 }
384                 
385                 setcookie($CONF['CookiePrefix'] .'user',$this->getDisplayName(),$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
386                 setcookie($CONF['CookiePrefix'] .'loginkey', $this->getCookieKey(),$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
387                 
388                 // make sure cookies on shared pcs don't get renewed
389                 if ( $shared )
390                 {
391                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
392                 }
393                 return;
394         }
395         
396         /**
397          * MEMBER::sendActivationLink()
398          * Send activation mail
399          * 
400          * @param       String  $type   activation type
401          * @param       String  $extra  extra info
402          * @return      Void
403          */
404         public function sendActivationLink($type, $extra='')
405         {
406                 global $CONF;
407                 
408                 if ( !isset($CONF['ActivationDays']) )
409                 {
410                         $CONF['ActivationDays'] = 2;
411                 }
412                 
413                 // generate key and URL
414                 $key = $this->generateActivationEntry($type, $extra);
415                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
416                 
417                 // choose text to use in mail
418                 switch ( $type )
419                 {
420                         case 'register':
421                                 $message = _ACTIVATE_REGISTER_MAIL;
422                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;
423                                 break;
424                         case 'forgot':
425                                 $message = _ACTIVATE_FORGOT_MAIL;
426                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;
427                                 break;
428                         case 'addresschange':
429                                 $message = _ACTIVATE_CHANGE_MAIL;
430                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;
431                                 break;
432                         default;
433                 }
434                 
435                 // fill out variables in text
436                 $aVars = array(
437                         'siteName' => $CONF['SiteName'],
438                         'siteUrl' => $CONF['IndexURL'],
439                         'memberName' => $this->getDisplayName(),
440                         'activationUrl' => $url,
441                         'activationDays' => $CONF['ActivationDays']
442                 );
443                 
444                 $message = TEMPLATE::fill($message, $aVars);
445                 $subject = TEMPLATE::fill($subject, $aVars);
446                 
447                 // send mail
448                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
449                 
450                 ACTIONLOG::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
451                 return;
452         }
453         
454         /*
455          * Returns an array of all blogids for which member has admin rights
456          */
457         public function getAdminBlogs()
458         {
459                 $blogs = array();
460                 
461                 if ($this->isAdmin())
462                 {
463                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');
464                 }
465                 else
466                 {
467                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
468                 }
469                 
470                 $res = sql_query($query);
471                 if ( sql_num_rows($res) > 0 )
472                 {
473                         while ( $obj = sql_fetch_object($res) )
474                         {
475                                 array_push($blogs, $obj->blogid);
476                         }
477                 }
478                 return $blogs;
479         }
480         
481         /*
482          * Returns an array of all blogids for which member has team rights
483          */
484         public function getTeamBlogs($incAdmin = 1)
485         {
486                 $incAdmin = intval($incAdmin);
487                 $blogs = array();
488                 
489                 if ( $this->isAdmin() && $incAdmin )
490                 {
491                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');
492                 }
493                 else
494                 {
495                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
496                 }
497                 
498                 $res = sql_query($query);
499                 if ( sql_num_rows($res) > 0 )
500                 {
501                         while ( $obj = sql_fetch_object($res) )
502                         {
503                                 array_push($blogs, $obj->blogid);
504                         }
505                 }
506                 return $blogs;
507         }
508         
509         /**
510          * MEMBER::getNotifyFromMailAddress()
511          * 
512          * Returns an email address from which notification of commenting/karma voting can
513          * be sent. A suggestion can be given for when the member is not logged in
514          * 
515          * @param       String  $suggest        
516          * @return      String  mail address or suggestion
517          */
518         public function getNotifyFromMailAddress($suggest = "")
519         {
520                 global $CONF;
521                 if ( $this->isLoggedIn() )
522                 {
523                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";
524                 }
525                 else if ( NOTIFICATION::address_validation($suggest) )
526                 {
527                         return $suggest;
528                 }
529                 else
530                 {
531                         return $CONF['AdminEmail'];
532                 }
533         }
534         
535         /*
536          * Write data to database
537          */
538         function write()
539         {
540                 $query =  'UPDATE '.sql_table('member')
541                            . " SET mname='" . sql_real_escape_string($this->getDisplayName()) . "',"
542                            . "     mrealname='". sql_real_escape_string($this->getRealName()) . "',"
543                            . "     mpassword='". sql_real_escape_string($this->getPassword()) . "',"
544                            . "     mcookiekey='". sql_real_escape_string($this->getCookieKey()) . "',"
545                            . "     murl='" . sql_real_escape_string($this->getURL()) . "',"
546                            . "     memail='" . sql_real_escape_string($this->getEmail()) . "',"
547                            . "     madmin=" . $this->isAdmin() . ","
548                            . "     mnotes='" . sql_real_escape_string($this->getNotes()) . "',"
549                            . "     mcanlogin=" . $this->canLogin() . ","
550                            . "     mlocale='" . sql_real_escape_string($this->getLocale()) . "',"
551                            . "     mautosave=" . intval($this->getAutosave()) . ""                         
552                            . " WHERE mnumber=" . $this->getID();
553                 sql_query($query);
554                 return;
555         }
556         
557         public function checkCookieKey($key)
558         {
559                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );
560         }
561         
562         public function checkPassword($pw)
563         {
564                 return (md5($pw) == $this->getPassword());
565         }
566         
567         public function getRealName()
568         {
569                 return $this->realname;
570         }
571
572         public function setRealName($name)
573         {
574                 $this->realname = $name;
575         }
576
577         public function getEmail()
578         {
579                 return $this->email;
580         }
581
582         public function setEmail($email)
583         {
584                 $this->email = $email;
585         }
586
587         public function getPassword()
588         {
589                 return $this->password;
590         }
591
592         public function setPassword($pwd)
593         {
594                 $this->password = md5($pwd);
595         }
596
597         public function getCookieKey()
598         {
599                 return $this->cookiekey;
600         }
601         
602         /*
603          * Generate new cookiekey, save it, and return it
604          */
605         public function newCookieKey()
606         {
607                 mt_srand( (double) microtime() * 1000000);
608                 $this->cookiekey = md5(uniqid(mt_rand()));
609                 $this->write();
610                 return $this->cookiekey;
611         }
612         
613         public function setCookieKey($val)
614         {
615                 $this->cookiekey = $val;
616         }
617         
618         public function getURL()
619         {
620                 return $this->url;
621         }
622         
623         public function setURL($site)
624         {
625                 $this->url = $site;
626         }
627         
628         /*
629          * FIXME: $this->locale is always correct or not?
630          * NOTE: Deprecated, this will be obsoleted soon.
631          */
632         public function getLanguage()
633         {
634                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )
635                 {
636                         $language = '';
637                 }
638                 return $language;
639         }
640         
641         public function getLocale()
642         {
643                 return $this->locale;
644         }
645         
646         /*
647          * FIXME: $locale value should obsolete $language value near future
648          */
649         public function setLocale($locale)
650         {
651                 if ( !!preg_match('#^(.+)_(.+)_(.+)$#', $locale)
652                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
653                 {
654                         $locale = '';
655                 }
656                 $this->locale = $locale;
657                 return;
658         }
659         
660         public function setDisplayName($nick)
661         {
662                 $this->displayname = $nick;
663         }
664         
665         public function getDisplayName()
666         {
667                 return $this->displayname;
668         }
669         
670         public function isAdmin()
671         {
672                 return $this->admin;
673         }
674         
675         public function setAdmin($val)
676         {
677                 $this->admin = $val;
678         }
679         
680         public function canLogin()
681         {
682                 return $this->canlogin;
683         }
684         
685         public function setCanLogin($val)
686         {
687                 $this->canlogin = $val;
688         }
689         
690         public function getNotes()
691         {
692                 return $this->notes;
693         }
694         
695         public function setNotes($val)
696         {
697                 $this->notes = $val;
698         }
699         
700         public function getAutosave()
701         {
702                 return $this->autosave;
703         }
704         
705         public function setAutosave($val)
706         {
707                 $this->autosave = $val;
708         }
709         
710         public function getID()
711         {
712                 return $this->id;
713         }
714         
715         /*
716          * Returns true if there is a member with the given login name
717          * 
718          * @static
719          */
720         public static function exists($name)
721         {
722                 $r = sql_query('select * FROM '.sql_table('member')." WHERE mname='".sql_real_escape_string($name)."'");
723                 return ( sql_num_rows($r) != 0 );
724         }
725         
726         /*
727          * Returns true if there is a member with the given ID
728          *
729          * @static
730          */
731         public static function existsID($id)
732         {
733                 $r = sql_query('select * FROM '.sql_table('member')." WHERE mnumber='".intval($id)."'");
734                 return (sql_num_rows($r) != 0);
735         }
736         
737         /*
738          *  Checks if a username is protected. 
739          *  If so, it can not be used on anonymous comments
740          */
741         function isNameProtected($name)
742         {
743                 // extract name
744                 $name = strip_tags($name);
745                 $name = trim($name);
746                 return MEMBER::exists($name);
747         }
748         
749         /**
750          * MEMBER::create()
751          * 
752          * Adds a new member
753          * 
754          * @static
755          * @param       String  $name
756          * @param       String  $realname
757          * @param       String  $password
758          * @param       String  $email
759          * @param       String  $url
760          * @param       String  $admin
761          * @param       String  $canlogin
762          * @param       String  $notes
763          * @return      String  1 if success, others if fail
764          */
765         public static function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
766         {
767                 if ( !NOTIFICATION::address_validation($email) )
768                 {
769                         return _ERROR_BADMAILADDRESS;
770                 }
771                 
772                 if ( !isValidDisplayName($name) )
773                 {
774                         return _ERROR_BADNAME;
775                 }
776                 
777                 if ( MEMBER::exists($name) )
778                 {
779                         return _ERROR_NICKNAMEINUSE;
780                 }
781                 
782                 if ( !$realname )
783                 {
784                         return _ERROR_REALNAMEMISSING;
785                 }
786                 
787                 if ( !$password )
788                 {
789                         return _ERROR_PASSWORDMISSING;
790                 }
791                 
792                 /*
793                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,
794                  *  this cause a malformed URL. Let's fix it.
795                  */
796                 
797                 if ( !preg_match('#^https?://#', $url) )
798                 {
799                         $url = 'http://' . $url;
800                 }
801                 
802                 $name = sql_real_escape_string($name);
803                 $realname = sql_real_escape_string($realname);
804                 $password = sql_real_escape_string(md5($password));
805                 $email = sql_real_escape_string($email);
806                 $url = sql_real_escape_string($url);
807                 $admin = intval($admin);
808                 $canlogin = intval($canlogin);
809                 $notes = sql_real_escape_string($notes);
810                 
811                 $query = 'INSERT INTO '.sql_table('member')." (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES) "
812                            . "VALUES ('$name','$realname','$password','$email','$url',$admin, $canlogin, '$notes')";
813                 sql_query($query);
814                 
815                 ACTIONLOG::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
816                 
817                 return 1;
818         }
819         
820         /*
821          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
822          * (static)
823          *
824          * @author karma
825          */
826         public static function getActivationInfo($key)
827         {
828                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=\'' . sql_real_escape_string($key). '\'';
829                 $res = sql_query($query);
830                 
831                 if ( !$res || (sql_num_rows($res) == 0) )
832                 {
833                         return 0;
834                 }
835                 else
836                 {
837                         return sql_fetch_object($res);
838                 }
839         }
840         
841         /*
842          * Creates an account activation key
843          *
844          * @param $type one of the following values (determines what to do when activation expires)
845          *                'register' (new member registration)
846          *                'forgot' (forgotton password)
847          *                'addresschange' (member address has changed)
848          * @param $extra extra info (needed when validation link expires)
849          *                                addresschange -> old email address
850          * @author dekarma
851          */
852         public function generateActivationEntry($type, $extra = '')
853         {
854                 // clean up old entries
855                 $this->cleanupActivationTable();
856                 
857                 // kill any existing entries for the current member (delete is ok)
858                 // (only one outstanding activation key can be present for a member)
859                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
860                 
861                 // indicates if the member can log in while the link is active
862                 $canLoginWhileActive = false;
863                 switch ( $type )
864                 {
865                         case 'forgot':
866                                 $canLoginWhileActive = true;
867                                 break;
868                         case 'register':
869                                 break;
870                         case 'addresschange':
871                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
872                                 break;
873                 }
874                 
875                 $ok = false;
876                 while ( !$ok )
877                 {
878                         // generate a random key
879                         srand((double)microtime()*1000000);
880                         $key = md5(uniqid(rand(), true));
881                         
882                         // attempt to add entry in database
883                         // add in database as non-active
884                         $query = 'INSERT INTO ' . sql_table('activation'). ' (vkey, vtime, vmember, vtype, vextra) ';
885                         $query .= 'VALUES (\'' . sql_real_escape_string($key). '\', \'' . date('Y-m-d H:i:s',time()) . '\', \'' . intval($this->getID()). '\', \'' . sql_real_escape_string($type). '\', \'' . sql_real_escape_string($extra). '\')';
886                         if ( sql_query($query) )
887                                 $ok = true;
888                 }
889                 
890                 // mark member as not allowed to log in
891                 if ( !$canLoginWhileActive )
892                 {
893                         $this->setCanLogin(0);
894                         $this->write();
895                 }
896                 
897                 // return the key
898                 return $key;
899         }
900         
901         /*
902          * Inidicates that an activation link has been clicked and any forms displayed
903          * there have been successfully filled out.
904          * @author dekarma
905          */
906         function activate($key)
907         {
908                 // get activate info
909                 $info = MEMBER::getActivationInfo($key);
910                 
911                 // no active key
912                 if ( !$info )
913                 {
914                         return false;
915                 }
916                 
917                 switch ( $info->vtype )
918                 {
919                         case 'forgot':
920                                 // nothing to do
921                                 break;
922                         case 'register':
923                                 // set canlogin value
924                                 global $CONF;
925                                 sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info->vmember));
926                                 break;
927                         case 'addresschange':
928                                 // reset old 'canlogin' value
929                                 list($oldEmail, $oldCanLogin) = i18n::explode('/', $info->vextra);
930                                 sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info->vmember));
931                                 break;
932                 }
933                 
934                 // delete from activation table
935                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=\'' . sql_real_escape_string($key) . '\'');
936                 
937                 // success!
938                 return true;
939         }
940         
941         /**
942          * Cleans up entries in the activation table. All entries older than 2 days are removed.
943          * (static)
944          *
945          * @author dekarma
946          */
947         public function cleanupActivationTable()
948         {
949                 $actdays = 2;
950                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
951                 {
952                         $actdays = intval($CONF['ActivationDays']);
953                 }
954                 else
955                 {
956                         $CONF['ActivationDays'] = 2;
957                 }
958                 $boundary = time() - (60 * 60 * 24 * $actdays);
959                 
960                 // 1. walk over all entries, and see if special actions need to be performed
961                 $res = sql_query('SELECT * FROM ' . sql_table('activation') . ' WHERE vtime < \'' . date('Y-m-d H:i:s',$boundary) . '\'');
962                 
963                 while ( $o = sql_fetch_object($res) )
964                 {
965                         switch ( $o->vtype )
966                         {
967                                 case 'register':
968                                         // delete all information about this site member. registration is undone because there was
969                                         // no timely activation
970                                         include_once($DIR_LIBS . 'ADMIN.php');
971                                         ADMIN::deleteOneMember(intval($o->vmember));
972                                         break;
973                                 case 'addresschange':
974                                         // revert the e-mail address of the member back to old address
975                                         list($oldEmail, $oldCanLogin) = i18n::explode('/', $o->vextra);
976                                         sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=\'' . sql_real_escape_string($oldEmail). '\' WHERE mnumber=' . intval($o->vmember));
977                                         break;
978                                 case 'forgot':
979                                         // delete the activation link and ignore. member can request a new password using the
980                                         // forgot password link
981                                         break;
982                         }
983                 }
984                 
985                 // 2. delete activation entries for real
986                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vtime < \'' . date('Y-m-d H:i:s',$boundary) . '\'');
987         }
988 }