OSDN Git Service

Merge branch 'skinnable-master'
[nucleus-jp/nucleus-next.git] / nucleus / libs / MEMBER.php
1 <<<<<<< HEAD
2 <?php\r
3 \r
4 /* \r
5  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
6  * Copyright (C) 2002-2009 The Nucleus Group\r
7  * \r
8  * This program is free software; you can redistribute it and/or\r
9  * modify it under the terms of the GNU General Public License\r
10  * as published by the Free Software Foundation; either version 2\r
11  * of the License, or (at your option) any later version.\r
12  * (see nucleus/documentation/index.html#license for more info)\r
13  */\r
14 /**\r
15  * A class representing site members\r
16  * \r
17  * @license http://nucleuscms.org/license.txt GNU General Public License\r
18  * @copyright Copyright (C) 2002-2009 The Nucleus Group\r
19  * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $\r
20  */\r
21 class Member\r
22 {\r
23         // 1 when authenticated, 0 when not\r
24         public $loggedin = 0;\r
25         public $password;               // not the actual password, but rather a MD5 hash\r
26         private $algorism = 'md5';\r
27         \r
28         public $cookiekey;              // value that should also be in the client cookie to allow authentication\r
29         private $cookie_salt = FALSE;\r
30         \r
31         // member info\r
32         public $id = -1;\r
33         public $realname;\r
34         public $displayname;\r
35         public $email;\r
36         public $url;\r
37         public $admin = 0;                      // (either 0 or 1)\r
38         public $canlogin = 0;           // (either 0 or 1)\r
39         public $notes;\r
40         public $autosave = 1;           // if the member use the autosave draft function\r
41         private $locale = '';\r
42         \r
43         /**\r
44          * Member::__construct()\r
45          * Constructor for a member object\r
46          * \r
47          * @param       Void\r
48          * @return      Void\r
49          * \r
50          */\r
51         public function __construct()\r
52         {\r
53                 return;\r
54         }\r
55         \r
56         /**\r
57          * Member::createFromName()\r
58          * Create a member object for a given displayname\r
59          * \r
60          * @static\r
61          * @param       String  $displayname    login name\r
62          * @return      Object  member object\r
63          * \r
64          */\r
65         public static function &createFromName($displayname)\r
66         {\r
67                 $mem = new Member();\r
68                 $mem->readFromName($displayname);\r
69                 return $mem;\r
70         }\r
71         \r
72         /**\r
73          * Member::createFromID()\r
74          * Create a member object for a given ID\r
75          * \r
76          * @static\r
77          * @param       Integer $id     id for member\r
78          */\r
79         public static function &createFromID($id)\r
80         {\r
81                 $mem = new Member();\r
82                 $mem->readFromID($id);\r
83                 return $mem;\r
84         }\r
85         \r
86         /**\r
87          * Member::readFromName()\r
88          * Read member table in database\r
89          * \r
90          * @param       String  $displayname    login name\r
91          * @return      Object  SQL resource\r
92          * \r
93          */\r
94         public function readFromName($displayname)\r
95         {\r
96                 return $this->read('mname='.DB::quoteValue($displayname));\r
97         }\r
98         \r
99         /**\r
100          * Member::readFromID()\r
101          * Read member table in database\r
102          * \r
103          * @param       Integer $id     id for member\r
104          * @return      Object  SQL resource\r
105          * \r
106          */\r
107         public function readFromID($id)\r
108         {\r
109                 return $this->read("mnumber=" . intval($id));\r
110         }\r
111         \r
112         /**\r
113          * Member::hash()\r
114          * hash the target string\r
115          * \r
116          * @param       String  $string target string\r
117          * @return      Void    hashed string\r
118          */\r
119         public function hash($string)\r
120         {\r
121                 switch ( $this->algorism )\r
122                 {\r
123                         case 'md5':\r
124                         default:\r
125                                 $string = md5($string);\r
126                 }\r
127                 return $string;\r
128         }\r
129         \r
130         /**\r
131          * Member::set_cookie_salt()\r
132          * \r
133          * @param       integer $key    secureCookieKey value\r
134          * @return      void\r
135          * \r
136          */\r
137         private function set_cookie_salt($key = 0)\r
138         {\r
139                 if ( !$key )\r
140                 {\r
141                         $key = 24;\r
142                 }\r
143                 \r
144                 switch( $key )\r
145                 {\r
146                         case 8:\r
147                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
148                                 break;\r
149                         case 16:\r
150                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
151                                 break;\r
152                         case 24:\r
153                                 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
154                                 break;\r
155                         case 32:\r
156                                 $this->cookie_salt = serverVar('REMOTE_ADDR');\r
157                                 break;\r
158                         default:\r
159                                 $this->cookie_salt = 'none';\r
160                 }\r
161                 return;\r
162         }\r
163         \r
164         /**\r
165          * Member::login()\r
166          * Tries to login as a given user.\r
167          * Returns true when succeeded, returns false when failed\r
168          * 3.40 adds CustomLogin event\r
169          * \r
170          * @param       String  $login  login name for member\r
171          * @param       String  $password       password for member\r
172          * @param       Integer $shared whether the user agent is shared or not\r
173          * \r
174          */\r
175         public function login($login, $password, $shared=1)\r
176         {\r
177                 global $CONF, $errormessage, $manager;\r
178                 \r
179                 /* TODO: validation for $login, $password, $shared */\r
180                 if ( $login == '' || $password == '' )\r
181                 {\r
182                         return 0;\r
183                 }\r
184                 /* limiting the length of password to avoid hash collision */\r
185                 $password=i18n::substr($password, 0, 40);\r
186                 \r
187                 /* \r
188                  * generate cookie salt from secure cookie key settings\r
189                 * (either 'none', 0, 8, 16, 24, or 32)\r
190                 */\r
191                 if ( !$this->cookie_salt )\r
192                 {\r
193                         $salt = 0;\r
194                         if ( array_key_exists('secureCookieKey', $CONF) )\r
195                         {\r
196                                 $salt = $CONF['secureCookieKey'];\r
197                         }\r
198                         $this->set_cookie_salt($salt);\r
199                 }\r
200                 \r
201                 $success = 0;\r
202                 $allowlocal = 1;\r
203                 $manager->notify('CustomLogin', array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal));\r
204                 \r
205                 $this->loggedin = 0;\r
206                 if ( $success )\r
207                 {\r
208                         $this->loggedin = ( $this->readFromName($login) );\r
209                 }\r
210                 elseif ( $allowlocal )\r
211                 {\r
212                         $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );\r
213                 }\r
214                 \r
215                 /* login failed */\r
216                 if ( !$this->loggedin )\r
217                 {\r
218                         $trimlogin = trim($login);\r
219                         if ( empty($trimlogin) )\r
220                         {\r
221                                 $errormessage = "Please enter a username.";\r
222                         }\r
223                         else\r
224                         {\r
225                                 $errormessage = 'Login failed for ' . $login;\r
226                         }\r
227                         $manager->notify('LoginFailed', array('username' => $login) );\r
228                         ActionLog::add(INFO, $errormessage);\r
229                 }\r
230                 /* login success */\r
231                 else\r
232                 {\r
233                         /* For lower compatibility */\r
234                         if ( strlen($this->password) === 32 )\r
235                         {\r
236                                 $this->password = $this->hash($password);\r
237                         }\r
238                         \r
239                         $this->newCookieKey();\r
240                         $this->setCookies($shared);\r
241                         \r
242                         if ( $this->cookie_salt !== 'none' )\r
243                         {\r
244                                 /* secure cookie key */\r
245                                 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));\r
246                                 $this->write();\r
247                         }\r
248                         \r
249                         $errormessage = '';\r
250                         $manager->notify('LoginSuccess', array('member' => &$member, 'username' => $login) );\r
251                         ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");\r
252                 }\r
253                 \r
254                 return $this->loggedin;\r
255         }\r
256         \r
257         /**\r
258          * Member::cookielogin()\r
259          * Login using cookie key\r
260          * \r
261          * @param       String  $login  not used\r
262          * @param       String  $cookiekey      not used\r
263          * @return      Boolean login or not\r
264          */\r
265         public function cookielogin($login='', $cookiekey='')\r
266         {\r
267                 global $CONF, $manager;\r
268                 \r
269                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
270                 {\r
271                         /* Cookie Authentication */\r
272                         $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");\r
273                                 \r
274                         /* TODO: validation for each cookie values */\r
275                                 \r
276                         /* limiting the length of password to avoid hash collision */\r
277                         $ck = i18n::substr($ck,0,32);\r
278                                 \r
279                         /* \r
280                          * generate cookie salt from secure cookie key settings\r
281                         * (either 'none', 0, 8, 16, 24, or 32)\r
282                         */\r
283                         if ( !$this->cookie_salt )\r
284                         {\r
285                                 $salt = 0;\r
286                                 if ( array_key_exists('secureCookieKey', $CONF) )\r
287                                 {\r
288                                         $salt = $CONF['secureCookieKey'];\r
289                                 }\r
290                                 $this->set_cookie_salt($salt);\r
291                         }\r
292                         \r
293                         if ( $this->cookie_salt !== 'none' )\r
294                         {\r
295                                 $ck = $this->hash($ck . $this->cookie_salt);\r
296                         }\r
297                         $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );\r
298                         unset($ck);\r
299                                 \r
300                         /* renew cookies when not on a shared computer */\r
301                         if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )\r
302                         {\r
303                                 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));\r
304                                 $this->setCookies();\r
305                         }\r
306                 }\r
307                 return $this->loggedin;\r
308         }\r
309         \r
310         /**\r
311          * Member::logout()\r
312          * logout and expire cookie\r
313          * \r
314          * @param       Void\r
315          * @return      Void\r
316          */\r
317         public function logout()\r
318         {\r
319                 global $CONF, $manager;\r
320                 \r
321                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
322                 {\r
323                         /* remove cookies on logout */\r
324                         setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
325                         setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
326                         $manager->notify('Logout', array('username' => cookieVar("{$CONF['CookiePrefix']}user") ) );\r
327                 }\r
328                 \r
329                 $this->loggedin = 0;\r
330                 return;\r
331         }\r
332         \r
333         /**\r
334          * Member::isLoggedIn()\r
335          * return member is loggedin or not\r
336          * \r
337          * @param       Void\r
338          * @return      Void\r
339          */\r
340         public function isLoggedIn()\r
341         {\r
342                 return $this->loggedin;\r
343         }\r
344         \r
345         /**\r
346          * MEMBER:read()\r
347          * Read member information from the database\r
348          * \r
349          * @param       String  $where  where statement\r
350          * @return      Resource        SQL resource\r
351          * \r
352          */\r
353         public function read($where)\r
354         {\r
355                 // read info\r
356                 $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;\r
357                 \r
358                 $row = DB::getRow($query);\r
359                 \r
360                 $this->setRealName($row['mrealname']);\r
361                 $this->setEmail($row['memail']);\r
362                 $this->password = $row['mpassword'];\r
363                 $this->setCookieKey($row['mcookiekey']);\r
364                 $this->setURL($row['murl']);\r
365                 $this->setDisplayName($row['mname']);\r
366                 $this->setAdmin($row['madmin']);\r
367                 $this->id = $row['mnumber'];\r
368                 $this->setCanLogin($row['mcanlogin']);\r
369                 $this->setNotes($row['mnotes']);\r
370                 $this->setLocale($row['mlocale']);\r
371                 $this->setAutosave($row['mautosave']);\r
372                 \r
373                 return $row ? TRUE : FALSE;\r
374         }\r
375         \r
376         /**\r
377          * Member::isBlogAdmin()\r
378          * Returns true if member is an admin for the given blog\r
379          * (returns false if not a team member)\r
380          * \r
381          * @param       Integer $blogid weblog id\r
382          * @return      Integer weblog admin or not\r
383          * \r
384          */\r
385         public function isBlogAdmin($blogid)\r
386         {\r
387                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'\r
388                 . ' tblog=' . intval($blogid)\r
389                 . ' and tmember='. $this->getID();\r
390                 $res = DB::getValue($query);\r
391                 if ( $res )\r
392                         return ($res == 1);\r
393                 else\r
394                         return 0;\r
395         }\r
396         \r
397         /**\r
398          * Member::blogAdminRights()\r
399          * \r
400          * @param       integer $blogid ID of target weblog\r
401          * @return      boolean whether to have admin rights to the weblog or not\r
402          * \r
403          */\r
404         public function blogAdminRights($blogid)\r
405         {\r
406                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));\r
407         }\r
408         \r
409         /**\r
410          * Member::teamRights()\r
411          * \r
412          * @param       integer $blogid ID of target weblog\r
413          * @return      boolean whether to have admin right to the weblog or not\r
414          * \r
415          */\r
416         public function teamRights($blogid)\r
417         {\r
418                 return ($this->isAdmin() || $this->isTeamMember($blogid));\r
419         }\r
420         \r
421         /**\r
422          * Member::isTeamMember()\r
423          * Returns true if this member is a team member of the given blog\r
424          * \r
425          * @param       integer $blogid ID of target weblog\r
426          * @return      boolean whether to join the weblog or not\r
427          * \r
428          */\r
429         public function isTeamMember($blogid)\r
430         {\r
431                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'\r
432                            . ' tblog=' . intval($blogid)\r
433                            . ' and tmember='. $this->getID();\r
434                 $res = DB::getResult($query);\r
435                 return ($res->rowCount() != 0);\r
436         }\r
437         \r
438         /**\r
439          * Member::canAddItem()\r
440          * \r
441          * @param       integer $catid  ID of target category\r
442          * @return      boolean whether to be able to add items to the category or not\r
443          * \r
444          */\r
445         public function canAddItem($catid)\r
446         {\r
447                 global $manager;\r
448                 \r
449                 // if this is a 'newcat' style newcat\r
450                 // no blog admin of destination blog -> NOK\r
451                 // blog admin of destination blog -> OK\r
452                 if ( i18n::strpos($catid,'newcat') === 0 )\r
453                 {\r
454                         // get blogid\r
455                         list($blogid) = sscanf($catid,"newcat-%d");\r
456                         return $this->blogAdminRights($blogid);\r
457                 }\r
458                 \r
459                 // category does not exist -> NOK\r
460                 if ( !$manager->existsCategory($catid) )\r
461                 {\r
462                         return 0;\r
463                 }\r
464                 \r
465                 $blogid = getBlogIDFromCatID($catid);\r
466                 \r
467                 // no team rights for blog -> NOK\r
468                 if (!$this->teamRights($blogid))\r
469                 {\r
470                         return 0;\r
471                 }\r
472                 \r
473                 // all other cases: OK\r
474                 return 1;\r
475         }\r
476         \r
477         /**\r
478          * Member::canAlterComment()\r
479          * Returns true if this member can edit/delete a commentitem. This can be in the\r
480          * following cases:\r
481          *        - member is a super-admin\r
482          *   - member is the author of the comment\r
483          *   - member is admin of the blog associated with the comment\r
484          *   - member is author of the item associated with the comment\r
485          * \r
486          * @param       integer $commentid      ID of target comment\r
487          * @return      boolean delete/edit the comment or not\r
488          * \r
489          */\r
490         public function canAlterComment($commentid)\r
491         {\r
492                 if ( $this->isAdmin() )\r
493                 {\r
494                         return 1;\r
495                 }\r
496                 \r
497                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'\r
498                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')\r
499                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);\r
500                 $res = DB::getRow($query);\r
501                 \r
502                 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());\r
503         }\r
504         \r
505         /**\r
506          * Member::canAlterItem()\r
507          * Returns true if this member can edit/delete an item. This is true in the following\r
508          * cases: - member is a super-admin\r
509          *             - member is the author of the item\r
510          *        - member is admin of the the associated blog\r
511          * \r
512          * @param       integer $itemid ID of target item\r
513          * @return      boolean delete/edit the item or not\r
514          * \r
515          */\r
516         public function canAlterItem($itemid)\r
517         {\r
518                 if ($this->isAdmin()) return 1;\r
519                 \r
520                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);\r
521                 $res = DB::getRow($query);\r
522                 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);\r
523         }\r
524         \r
525         /**\r
526          * Member::canBeDeleted()\r
527          * Return true if member can be deleted. This means that there are no items posted by the member left\r
528          * \r
529          * @param       void\r
530          * @return      boolean whether there is no items or exists\r
531          * \r
532          */\r
533         public function canBeDeleted()\r
534         {\r
535                 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());\r
536                 return ( $res->rowCount() == 0 );\r
537         }\r
538         \r
539         /**\r
540          * Member::canUpdateItem()\r
541          * returns true if this member can move/update an item to a given category,\r
542          * false if not (see comments fot the tests that are executed)\r
543          * \r
544          * @param       integer $itemid\r
545          * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)\r
546          * @return      boolean whether being able to update the item or not\r
547          * \r
548          */\r
549         public function canUpdateItem($itemid, $newcat)\r
550         {\r
551                 global $manager;\r
552                 \r
553                 // item does not exists -> NOK\r
554                 if ( !$manager->existsItem($itemid,1,1) )\r
555                 {\r
556                         return 0;\r
557                 }\r
558                 \r
559                 // cannot alter item -> NOK\r
560                 if (!$this->canAlterItem($itemid))\r
561                 {\r
562                         return 0;\r
563                 }\r
564                 \r
565                 // if this is a 'newcat' style newcat\r
566                 // no blog admin of destination blog -> NOK\r
567                 // blog admin of destination blog -> OK\r
568                 if ( i18n::strpos($newcat, 'newcat') === 0 )\r
569                 {\r
570                         // get blogid\r
571                         list($blogid) = sscanf($newcat, 'newcat-%d');\r
572                         return $this->blogAdminRights($blogid);\r
573                 }\r
574                 \r
575                 // category does not exist -> NOK\r
576                 if (!$manager->existsCategory($newcat))\r
577                 {\r
578                         return 0;\r
579                 }\r
580                 \r
581                 // get item\r
582                 $item =& $manager->getItem($itemid,1,1);\r
583                 \r
584                 // old catid = new catid -> OK\r
585                 if ($item['catid'] == $newcat)\r
586                 {\r
587                         return 1;\r
588                 }\r
589                 \r
590                 // not a valid category -> NOK\r
591                 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));\r
592                 if ( !$validCat )\r
593                 {\r
594                         return 0;\r
595                 }\r
596                 \r
597                 // get destination blog\r
598                 $source_blogid = getBlogIDFromItemID($itemid);\r
599                 $dest_blogid = getBlogIDFromCatID($newcat);\r
600                 \r
601                 // not a team member of destination blog -> NOK\r
602                 if ( !$this->teamRights($dest_blogid) )\r
603                 {\r
604                         return 0;\r
605                 }\r
606                 \r
607                 // if member is author of item -> OK\r
608                 if ( $item['authorid'] == $this->getID() )\r
609                 {\r
610                         return 1;\r
611                 }\r
612                 \r
613                 // if member has admin rights on both blogs: OK\r
614                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )\r
615                 {\r
616                         return 1;\r
617                 }\r
618                 \r
619                 // all other cases: NOK\r
620                 return 0;\r
621         }\r
622         \r
623         /**\r
624          * Member::setCookies()\r
625          * Sets the cookies for the member\r
626          * \r
627          * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire\r
628          *                              at the end of the session in this case.\r
629          * @return      void\r
630          * \r
631          */\r
632         public function setCookies($shared = 0)\r
633         {\r
634                 global $CONF;\r
635                 \r
636                 if ( $CONF['SessionCookie'] || $shared )\r
637                 {\r
638                         $lifetime = 0;\r
639                 }\r
640                 else\r
641                 {\r
642                         $lifetime = time()+2592000;\r
643                 }\r
644                 \r
645                 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
646                 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
647                 \r
648                 // make sure cookies on shared pcs don't get renewed\r
649                 if ( $shared )\r
650                 {\r
651                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);\r
652                 }\r
653                 return;\r
654         }\r
655         \r
656         /**\r
657          * Member::sendActivationLink()\r
658          * Send activation mail\r
659          * \r
660          * @param       string  $type   activation type\r
661          * @param       string  $extra  extra info\r
662          * @return      void\r
663          */\r
664         public function sendActivationLink($type, $extra='')\r
665         {\r
666                 global $CONF;\r
667                 \r
668                 if ( !isset($CONF['ActivationDays']) )\r
669                 {\r
670                         $CONF['ActivationDays'] = 2;\r
671                 }\r
672                 \r
673                 // generate key and URL\r
674                 $key = $this->generateActivationEntry($type, $extra);\r
675                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;\r
676                 \r
677                 // choose text to use in mail\r
678                 switch ( $type )\r
679                 {\r
680                         case 'register':\r
681                                 $message = _ACTIVATE_REGISTER_MAIL;\r
682                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;\r
683                                 break;\r
684                         case 'forgot':\r
685                                 $message = _ACTIVATE_FORGOT_MAIL;\r
686                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;\r
687                                 break;\r
688                         case 'addresschange':\r
689                                 $message = _ACTIVATE_CHANGE_MAIL;\r
690                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;\r
691                                 break;\r
692                         default;\r
693                 }\r
694                 \r
695                 // fill out variables in text\r
696                 $aVars = array(\r
697                         'siteName' => $CONF['SiteName'],\r
698                         'siteUrl' => $CONF['IndexURL'],\r
699                         'memberName' => $this->getDisplayName(),\r
700                         'activationUrl' => $url,\r
701                         'activationDays' => $CONF['ActivationDays']\r
702                 );\r
703                 \r
704                 $message = Template::fill($message, $aVars);\r
705                 $subject = Template::fill($subject, $aVars);\r
706                 \r
707                 // send mail\r
708                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());\r
709                 \r
710                 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');\r
711                 return;\r
712         }\r
713         \r
714         /**\r
715          * Member::getAdminBlogs()\r
716          * Returns an array of all blogids for which member has admin rights\r
717          * \r
718          * @param       void\r
719          * @return      array   weblog IDs in which this member has admin rights\r
720          * \r
721          */\r
722         public function getAdminBlogs()\r
723         {\r
724                 $blogs = array();\r
725                 \r
726                 if ($this->isAdmin())\r
727                 {\r
728                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
729                 }\r
730                 else\r
731                 {\r
732                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();\r
733                 }\r
734                 \r
735                 $res = DB::getResult($query);\r
736                 if ( $res->rowCount() > 0 )\r
737                 {\r
738                         foreach ( $res as $row )\r
739                         {\r
740                                 array_push($blogs, $row['blogid']);\r
741                         }\r
742                 }\r
743                 return $blogs;\r
744         }\r
745         \r
746         /**\r
747          * Member::getTeamBlogs()\r
748          * Returns an array of all blogids for which member has team rights\r
749          * \r
750          * @param       boolean $incAdmin       whether checking weblog admin rights or not\r
751          * @return      array   weblog IDs in which this member join\r
752          * \r
753          */\r
754         public function getTeamBlogs($incAdmin = 1)\r
755         {\r
756                 $incAdmin = intval($incAdmin);\r
757                 $blogs = array();\r
758                 \r
759                 if ( $this->isAdmin() && $incAdmin )\r
760                 {\r
761                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
762                 }\r
763                 else\r
764                 {\r
765                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();\r
766                 }\r
767                 \r
768                 $res = DB::getResult($query);\r
769                 if ( $res->rowCount() > 0 )\r
770                 {\r
771                         foreach ( $res as $row )\r
772                         {\r
773                                 array_push($blogs, $row['blogid']);\r
774                         }\r
775                 }\r
776                 return $blogs;\r
777         }\r
778         \r
779         /**\r
780          * Member::getNotifyFromMailAddress()\r
781          * \r
782          * Returns an email address from which notification of commenting/karma voting can\r
783          * be sent. A suggestion can be given for when the member is not logged in\r
784          * \r
785          * @param       String  $suggest\r
786          * @return      String  mail address or suggestion\r
787          */\r
788         public function getNotifyFromMailAddress($suggest = "")\r
789         {\r
790                 global $CONF;\r
791                 if ( $this->isLoggedIn() )\r
792                 {\r
793                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";\r
794                 }\r
795                 else if ( NOTIFICATION::address_validation($suggest) )\r
796                 {\r
797                         return $suggest;\r
798                 }\r
799                 return $CONF['AdminEmail'];\r
800         }\r
801         \r
802         /**\r
803          * Member::write()\r
804          * Write data to database\r
805          * \r
806          * @param       void\r
807          * @return      void\r
808          * \r
809          */\r
810         public function write()\r
811         {\r
812                 $query =  'UPDATE '.sql_table('member')\r
813                         . ' SET mname=' . DB::quoteValue($this->displayname) . ', '\r
814                            . 'mrealname='. DB::quoteValue($this->realname) . ', '\r
815                            . 'mpassword='. DB::quoteValue($this->password) . ', '\r
816                            . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '\r
817                            . 'murl=' . DB::quoteValue($this->url) . ', '\r
818                            . 'memail=' . DB::quoteValue($this->email) . ', '\r
819                            . 'madmin=' . intval($this->admin) . ', '\r
820                            . 'mnotes=' . DB::quoteValue($this->notes) . ', '\r
821                            . 'mcanlogin=' . intval($this->canlogin) . ', '\r
822                            . 'mlocale=' . DB::quoteValue($this->locale) . ', '\r
823                            . 'mautosave=' . intval($this->autosave) . ' '\r
824                         . 'WHERE mnumber=' . intval($this->id);\r
825                 DB::execute($query);\r
826                 return;\r
827         }\r
828         \r
829         public function checkCookieKey($key)\r
830         {\r
831                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );\r
832         }\r
833         \r
834         public function checkPassword($pw)\r
835         {\r
836                 /* for lower compatibility (md5) */\r
837                 if ( strlen($this->password) === 32 )\r
838                 {\r
839                         return (md5($pw) == $this->password);\r
840                 }\r
841                 return ($this->hash($pw) == $this->password);\r
842         }\r
843         \r
844         public function getRealName()\r
845         {\r
846                 return $this->realname;\r
847         }\r
848         \r
849         public function setRealName($name)\r
850         {\r
851                 $this->realname = $name;\r
852         }\r
853         \r
854         public function getEmail()\r
855         {\r
856                 return $this->email;\r
857         }\r
858         \r
859         public function setEmail($email)\r
860         {\r
861                 $this->email = $email;\r
862         }\r
863         \r
864         public function getPassword()\r
865         {\r
866                 return $this->password;\r
867         }\r
868         \r
869         public function setPassword($pwd)\r
870         {\r
871                 $this->password = $this->hash($pwd);\r
872         }\r
873         \r
874         public function getCookieKey()\r
875         {\r
876                 return $this->cookiekey;\r
877         }\r
878         \r
879         /**\r
880          * Member::newCookieKey()\r
881          * Generate new cookiekey, save it, and return it\r
882          * \r
883          * @param       void\r
884          * @return      void\r
885          * \r
886          */\r
887         public function newCookieKey()\r
888         {\r
889                 mt_srand( (double) microtime() * 1000000);\r
890                 $this->cookiekey = $this->hash(uniqid(mt_rand()));\r
891                 $this->write();\r
892                 return $this->cookiekey;\r
893         }\r
894         \r
895         public function setCookieKey($val)\r
896         {\r
897                 $this->cookiekey = $val;\r
898         }\r
899         \r
900         public function getURL()\r
901         {\r
902                 return $this->url;\r
903         }\r
904         \r
905         public function setURL($site)\r
906         {\r
907                 $this->url = $site;\r
908         }\r
909         \r
910         public function getLocale()\r
911         {\r
912                 return $this->locale;\r
913         }\r
914         \r
915         public function setLocale($locale)\r
916         {\r
917                 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)\r
918                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )\r
919                 {\r
920                         $locale = '';\r
921                 }\r
922                 $this->locale = $locale;\r
923                 return;\r
924         }\r
925         \r
926         public function setDisplayName($nick)\r
927         {\r
928                 $this->displayname = $nick;\r
929         }\r
930         \r
931         public function getDisplayName()\r
932         {\r
933                 return $this->displayname;\r
934         }\r
935         \r
936         public function isAdmin()\r
937         {\r
938                 return $this->admin;\r
939         }\r
940         \r
941         public function setAdmin($val)\r
942         {\r
943                 $this->admin = $val;\r
944         }\r
945         \r
946         public function canLogin()\r
947         {\r
948                 return $this->canlogin;\r
949         }\r
950         \r
951         public function setCanLogin($val)\r
952         {\r
953                 $this->canlogin = $val;\r
954         }\r
955         \r
956         public function getNotes()\r
957         {\r
958                 return $this->notes;\r
959         }\r
960         \r
961         public function setNotes($val)\r
962         {\r
963                 $this->notes = $val;\r
964         }\r
965         \r
966         public function getAutosave()\r
967         {\r
968                 return $this->autosave;\r
969         }\r
970         \r
971         public function setAutosave($val)\r
972         {\r
973                 $this->autosave = $val;\r
974                 return;\r
975         }\r
976         \r
977         /**\r
978          * Member::getID()\r
979          * \r
980          * @param       void\r
981          * @return      integer id of this member object\r
982          * \r
983          */\r
984         public function getID()\r
985         {\r
986                 return $this->id;\r
987         }\r
988         \r
989         /**\r
990          * Member::exists()\r
991          * Returns true if there is a member with the given login name\r
992          * \r
993          * @static\r
994          * @param       string  $name   target name\r
995          * @return      boolean whether target name exists or not\r
996          */\r
997         public static function exists($name)\r
998         {\r
999                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));\r
1000                 return ( $r->rowCount() != 0 );\r
1001         }\r
1002         \r
1003         /**\r
1004          * Member::existsID()\r
1005          * Returns true if there is a member with the given ID\r
1006          * \r
1007          * @static\r
1008          * @param       integer $id     target id\r
1009          * @return      boolean whether target id exists or not\r
1010          * \r
1011          */\r
1012         public static function existsID($id)\r
1013         {\r
1014                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));\r
1015                 return ( $r->rowCount() != 0 );\r
1016         }\r
1017         \r
1018         /**\r
1019          * Member::isNameProtected()\r
1020          *  Checks if a username is protected.\r
1021          *  If so, it can not be used on anonymous comments\r
1022          * \r
1023          * @param       string  $name   target name\r
1024          * @return      boolean whether the name exists or not\r
1025          * \r
1026          */\r
1027         public function isNameProtected($name)\r
1028         {\r
1029                 // extract name\r
1030                 $name = strip_tags($name);\r
1031                 $name = trim($name);\r
1032                 return self::exists($name);\r
1033         }\r
1034         \r
1035         /**\r
1036          * Member::create()\r
1037          * Adds a new member\r
1038          * \r
1039          * @static\r
1040          * @param       String  $name\r
1041          * @param       String  $realname\r
1042          * @param       String  $password\r
1043          * @param       String  $email\r
1044          * @param       String  $url\r
1045          * @param       String  $admin\r
1046          * @param       String  $canlogin\r
1047          * @param       String  $notes\r
1048          * @return      String  1 if success, others if fail\r
1049          */\r
1050         static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)\r
1051         {\r
1052                 if ( !NOTIFICATION::address_validation($email) )\r
1053                 {\r
1054                         return _ERROR_BADMAILADDRESS;\r
1055                 }\r
1056                 \r
1057                 /* TODO: this method should be in MEMBER class, not globalfunctions */\r
1058                 if ( !isValidDisplayName($name) )\r
1059                 {\r
1060                         return _ERROR_BADNAME;\r
1061                 }\r
1062                 \r
1063                 if ( self::exists($name) )\r
1064                 {\r
1065                         return _ERROR_NICKNAMEINUSE;\r
1066                 }\r
1067                 \r
1068                 if ( !$realname )\r
1069                 {\r
1070                         return _ERROR_REALNAMEMISSING;\r
1071                 }\r
1072                 \r
1073                 /* TODO: check the number of characters */\r
1074                 if ( !$password )\r
1075                 {\r
1076                         return _ERROR_PASSWORDMISSING;\r
1077                 }\r
1078                 \r
1079                 /* \r
1080                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,\r
1081                  *  this cause a malformed URL. Let's fix it.\r
1082                  */\r
1083                 \r
1084                 if ( !preg_match('#^https?://#', $url) )\r
1085                 {\r
1086                         $url = 'http://' . $url;\r
1087                 }\r
1088                 \r
1089                 $name           = DB::quoteValue($name);\r
1090                 $realname       = DB::quoteValue($realname);\r
1091                 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */\r
1092                 $password       = DB::quoteValue(md5($password));\r
1093                 $email          = DB::quoteValue($email);\r
1094                 $url            = DB::quoteValue($url);\r
1095                 $admin          = (integer) $admin;\r
1096                 $canlogin       = (integer) $canlogin;\r
1097                 $notes          = DB::quoteValue($notes);\r
1098                 \r
1099                 $query = "INSERT INTO %s"\r
1100                        . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"\r
1101                        . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";\r
1102                 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);\r
1103                 DB::execute($query);\r
1104                 \r
1105                 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);\r
1106                 \r
1107                 return 1;\r
1108         }\r
1109         \r
1110         /**\r
1111          * Member::getActivationInfo()\r
1112          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)\r
1113          * \r
1114          * @static\r
1115          * @param       string  $key    activation key\r
1116          * @return      mixed   return 0 if failed, else return activation table object\r
1117          * \r
1118          */\r
1119         public static function getActivationInfo($key)\r
1120         {\r
1121                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);\r
1122                 $res = DB::getResult($query);\r
1123                 \r
1124                 if ( !$res || ($res->rowCount() == 0) )\r
1125                 {\r
1126                         return 0;\r
1127                 }\r
1128                 return $res->fetch();\r
1129         }\r
1130         \r
1131         /**\r
1132          * Member::generateActivationEntry()\r
1133          * Creates an account activation key\r
1134          * addresschange -> old email address\r
1135          * \r
1136          * @param       string  $type   one of the following values (determines what to do when activation expires)\r
1137          *                              'register'      (new member registration)\r
1138          *                              'forgot'        (forgotton password)\r
1139          *                              'addresschange' (member address has changed)\r
1140          * @param       string  $extra  extra info (needed when validation link expires)\r
1141          * @return      string  activation key\r
1142          */\r
1143         public function generateActivationEntry($type, $extra = '')\r
1144         {\r
1145                 // clean up old entries\r
1146                 $this->cleanupActivationTable();\r
1147                 \r
1148                 // kill any existing entries for the current member (delete is ok)\r
1149                 // (only one outstanding activation key can be present for a member)\r
1150                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));\r
1151                 \r
1152                 // indicates if the member can log in while the link is active\r
1153                 $canLoginWhileActive = false;\r
1154                 switch ( $type )\r
1155                 {\r
1156                         case 'forgot':\r
1157                                 $canLoginWhileActive = true;\r
1158                                 break;\r
1159                         case 'register':\r
1160                                 break;\r
1161                         case 'addresschange':\r
1162                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );\r
1163                                 break;\r
1164                 }\r
1165                 \r
1166                 $ok = false;\r
1167                 while ( !$ok )\r
1168                 {\r
1169                         // generate a random key\r
1170                         srand((double)microtime()*1000000);\r
1171                         $key = $this->hash(uniqid(rand(), true));\r
1172                         \r
1173                         // attempt to add entry in database\r
1174                         // add in database as non-active\r
1175                         $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';\r
1176                         $query = sprintf($query\r
1177                                 , sql_table('activation')\r
1178                                 , DB::quoteValue($key)\r
1179                                 , DB::formatDateTime()\r
1180                                 , intval($this->getID())\r
1181                                 , DB::quoteValue($type)\r
1182                                 , DB::quoteValue($extra)\r
1183                         );\r
1184                         if ( DB::execute($query) !== FALSE )\r
1185                                 $ok = true;\r
1186                 }\r
1187                 \r
1188                 // mark member as not allowed to log in\r
1189                 if ( !$canLoginWhileActive )\r
1190                 {\r
1191                         $this->setCanLogin(0);\r
1192                         $this->write();\r
1193                 }\r
1194                 \r
1195                 // return the key\r
1196                 return $key;\r
1197         }\r
1198         \r
1199         /**\r
1200          * Member::activate()\r
1201          * Inidicates that an activation link has been clicked and any forms displayed\r
1202          * there have been successfully filled out.\r
1203          * \r
1204          * @param       string  $key    activation key\r
1205          * @return      boolean\r
1206          * \r
1207          */\r
1208         public function activate($key)\r
1209         {\r
1210                 // get activate info\r
1211                 $info = self::getActivationInfo($key);\r
1212                 \r
1213                 // no active key\r
1214                 if ( !$info )\r
1215                 {\r
1216                         return false;\r
1217                 }\r
1218                 \r
1219                 switch ( $info['vtype'] )\r
1220                 {\r
1221                         case 'forgot':\r
1222                                 // nothing to do\r
1223                                 break;\r
1224                         case 'register':\r
1225                                 // set canlogin value\r
1226                                 global $CONF;\r
1227                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));\r
1228                                 break;\r
1229                         case 'addresschange':\r
1230                                 // reset old 'canlogin' value\r
1231                                 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);\r
1232                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));\r
1233                                 break;\r
1234                 }\r
1235                 \r
1236                 // delete from activation table\r
1237                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));\r
1238                 \r
1239                 // success!\r
1240                 return true;\r
1241         }\r
1242         \r
1243         /**\r
1244          * Member::cleanupActivationTable()\r
1245          * Cleans up entries in the activation table. All entries older than 2 days are removed.\r
1246          * (static)\r
1247          * \r
1248          * @param       void\r
1249          * @return      void\r
1250          */\r
1251         public function cleanupActivationTable()\r
1252         {\r
1253                 $actdays = 2;\r
1254                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )\r
1255                 {\r
1256                         $actdays = intval($CONF['ActivationDays']);\r
1257                 }\r
1258                 else\r
1259                 {\r
1260                         $CONF['ActivationDays'] = 2;\r
1261                 }\r
1262                 $boundary = time() - (60 * 60 * 24 * $actdays);\r
1263                 \r
1264                 // 1. walk over all entries, and see if special actions need to be performed\r
1265                 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1266                 $res = DB::getResult($query);\r
1267                 \r
1268                 foreach ( $res as $row )\r
1269                 {\r
1270                         switch ( $row['vtype'] )\r
1271                         {\r
1272                                 case 'register':\r
1273                                         // delete all information about this site member. registration is undone because there was\r
1274                                         // no timely activation\r
1275                                         include_once($DIR_LIBS . 'ADMIN.php');\r
1276                                         Admin::deleteOneMember(intval($row['vmember']));\r
1277                                         break;\r
1278                                 case 'addresschange':\r
1279                                         // revert the e-mail address of the member back to old address\r
1280                                         list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);\r
1281                                         DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));\r
1282                                         break;\r
1283                                 case 'forgot':\r
1284                                         // delete the activation link and ignore. member can request a new password using the\r
1285                                         // forgot password link\r
1286                                         break;\r
1287                         }\r
1288                 }\r
1289                 \r
1290                 // 2. delete activation entries for real\r
1291                 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1292                 DB::execute($query);\r
1293                 return;\r
1294         }\r
1295         \r
1296         /**\r
1297          * Member::$language\r
1298          * \r
1299          * @obsolete\r
1300          * @param       void\r
1301          * @return      void\r
1302          * \r
1303          */\r
1304         public $language = '';\r
1305         /**\r
1306          * Member::getLanguage()\r
1307          * \r
1308          * @obsolete\r
1309          * @param       void\r
1310          * @return      void\r
1311          * \r
1312          */\r
1313         public function getLanguage()\r
1314         {\r
1315                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )\r
1316                 {\r
1317                         $language = '';\r
1318                 }\r
1319                 return $language;\r
1320         }\r
1321 }\r
1322 =======
1323 <?php
1324
1325 /* 
1326  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
1327  * Copyright (C) 2002-2009 The Nucleus Group
1328  * 
1329  * This program is free software; you can redistribute it and/or
1330  * modify it under the terms of the GNU General Public License
1331  * as published by the Free Software Foundation; either version 2
1332  * of the License, or (at your option) any later version.
1333  * (see nucleus/documentation/index.html#license for more info)
1334  */
1335 /**
1336  * A class representing site members
1337  * 
1338  * @license http://nucleuscms.org/license.txt GNU General Public License
1339  * @copyright Copyright (C) 2002-2009 The Nucleus Group
1340  * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $
1341  */
1342 class Member
1343 {
1344         // 1 when authenticated, 0 when not
1345         public $loggedin = 0;
1346         public $password;               // not the actual password, but rather a MD5 hash
1347         private $algorism = 'md5';
1348         
1349         public $cookiekey;              // value that should also be in the client cookie to allow authentication
1350         private $cookie_salt = FALSE;
1351         
1352         // member info
1353         public $id = -1;
1354         public $realname;
1355         public $displayname;
1356         public $email;
1357         public $url;
1358         public $admin = 0;                      // (either 0 or 1)
1359         public $canlogin = 0;           // (either 0 or 1)
1360         public $notes;
1361         public $autosave = 1;           // if the member use the autosave draft function
1362         public $adminskin = 0;          // if the member use the autosave draft function
1363         public $bookmarklet = 0;                // if the member use the autosave draft function
1364         private $locale = '';
1365         
1366         /**
1367          * Member::__construct()
1368          * Constructor for a member object
1369          * 
1370          * @param       Void
1371          * @return      Void
1372          * 
1373          */
1374         public function __construct()
1375         {
1376                 return;
1377         }
1378         
1379         /**
1380          * Member::createFromName()
1381          * Create a member object for a given displayname
1382          * 
1383          * @static
1384          * @param       String  $displayname    login name
1385          * @return      Object  member object
1386          * 
1387          */
1388         public static function &createFromName($displayname)
1389         {
1390                 $mem = new Member();
1391                 $mem->readFromName($displayname);
1392                 return $mem;
1393         }
1394         
1395         /**
1396          * Member::createFromID()
1397          * Create a member object for a given ID
1398          * 
1399          * @static
1400          * @param       Integer $id     id for member
1401          */
1402         public static function &createFromID($id)
1403         {
1404                 $mem = new Member();
1405                 $mem->readFromID($id);
1406                 return $mem;
1407         }
1408         
1409         /**
1410          * Member::readFromName()
1411          * Read member table in database
1412          * 
1413          * @param       String  $displayname    login name
1414          * @return      Object  SQL resource
1415          * 
1416          */
1417         public function readFromName($displayname)
1418         {
1419                 return $this->read('mname='.DB::quoteValue($displayname));
1420         }
1421         
1422         /**
1423          * Member::readFromID()
1424          * Read member table in database
1425          * 
1426          * @param       Integer $id     id for member
1427          * @return      Object  SQL resource
1428          * 
1429          */
1430         public function readFromID($id)
1431         {
1432                 return $this->read("mnumber=" . intval($id));
1433         }
1434         
1435         /**
1436          * Member::hash()
1437          * hash the target string
1438          * 
1439          * @param       String  $string target string
1440          * @return      Void    hashed string
1441          */
1442         public function hash($string)
1443         {
1444                 switch ( $this->algorism )
1445                 {
1446                         case 'md5':
1447                         default:
1448                                 $string = md5($string);
1449                 }
1450                 return $string;
1451         }
1452         
1453         /**
1454          * Member::set_cookie_salt()
1455          * 
1456          * @param       integer $key    secureCookieKey value
1457          * @return      void
1458          * 
1459          */
1460         private function set_cookie_salt($key = 0)
1461         {
1462                 if ( !$key )
1463                 {
1464                         $key = 24;
1465                 }
1466                 
1467                 switch( $key )
1468                 {
1469                         case 8:
1470                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1471                                 break;
1472                         case 16:
1473                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1474                                 break;
1475                         case 24:
1476                                 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));
1477                                 break;
1478                         case 32:
1479                                 $this->cookie_salt = serverVar('REMOTE_ADDR');
1480                                 break;
1481                         default:
1482                                 $this->cookie_salt = 'none';
1483                 }
1484                 return;
1485         }
1486         
1487         /**
1488          * Member::login()
1489          * Tries to login as a given user.
1490          * Returns true when succeeded, returns false when failed
1491          * 3.40 adds CustomLogin event
1492          * 
1493          * @param       String  $login  login name for member
1494          * @param       String  $password       password for member
1495          * @param       Integer $shared whether the user agent is shared or not
1496          * 
1497          */
1498         public function login($login, $password, $shared=1)
1499         {
1500                 global $CONF, $errormessage, $manager;
1501                 
1502                 /* TODO: validation for $login, $password, $shared */
1503                 if ( $login == '' || $password == '' )
1504                 {
1505                         return 0;
1506                 }
1507                 /* limiting the length of password to avoid hash collision */
1508                 $password=i18n::substr($password, 0, 40);
1509                 
1510                 /* 
1511                  * generate cookie salt from secure cookie key settings
1512                 * (either 'none', 0, 8, 16, 24, or 32)
1513                 */
1514                 if ( !$this->cookie_salt )
1515                 {
1516                         $salt = 0;
1517                         if ( array_key_exists('secureCookieKey', $CONF) )
1518                         {
1519                                 $salt = $CONF['secureCookieKey'];
1520                         }
1521                         $this->set_cookie_salt($salt);
1522                 }
1523                 
1524                 $success = 0;
1525                 $allowlocal = 1;
1526                 $data = array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal);
1527                 $manager->notify('CustomLogin', $data);
1528                 
1529                 $this->loggedin = 0;
1530                 if ( $success )
1531                 {
1532                         $this->loggedin = ( $this->readFromName($login) );
1533                 }
1534                 elseif ( $allowlocal )
1535                 {
1536                         $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );
1537                 }
1538                 
1539                 /* login failed */
1540                 if ( !$this->loggedin )
1541                 {
1542                         $trimlogin = trim($login);
1543                         if ( empty($trimlogin) )
1544                         {
1545                                 $errormessage = "Please enter a username.";
1546                         }
1547                         else
1548                         {
1549                                 $errormessage = 'Login failed for ' . $login;
1550                         }
1551                         $data = array('username' => $login);
1552                         $manager->notify('LoginFailed', $data);
1553                         ActionLog::add(INFO, $errormessage);
1554                 }
1555                 /* login success */
1556                 else
1557                 {
1558                         /* For lower compatibility */
1559                         if ( strlen($this->password) === 32 )
1560                         {
1561                                 $this->password = $this->hash($password);
1562                         }
1563                         
1564                         $this->newCookieKey();
1565                         $this->setCookies($shared);
1566                         
1567                         if ( $this->cookie_salt !== 'none' )
1568                         {
1569                                 /* secure cookie key */
1570                                 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));
1571                                 $this->write();
1572                         }
1573                         
1574                         $errormessage = '';
1575                         $data = array('member' => &$this, 'username' => $login);
1576                         $manager->notify('LoginSuccess', $data);
1577                         ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");
1578                 }
1579                 
1580                 return $this->loggedin;
1581         }
1582         
1583         /**
1584          * Member::cookielogin()
1585          * Login using cookie key
1586          * 
1587          * @param       String  $login  not used
1588          * @param       String  $cookiekey      not used
1589          * @return      Boolean login or not
1590          */
1591         public function cookielogin($login='', $cookiekey='')
1592         {
1593                 global $CONF, $manager;
1594                 
1595                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
1596                 {
1597                         /* Cookie Authentication */
1598                         $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");
1599                                 
1600                         /* TODO: validation for each cookie values */
1601                                 
1602                         /* limiting the length of password to avoid hash collision */
1603                         $ck = i18n::substr($ck,0,32);
1604                                 
1605                         /* 
1606                          * generate cookie salt from secure cookie key settings
1607                         * (either 'none', 0, 8, 16, 24, or 32)
1608                         */
1609                         if ( !$this->cookie_salt )
1610                         {
1611                                 $salt = 0;
1612                                 if ( array_key_exists('secureCookieKey', $CONF) )
1613                                 {
1614                                         $salt = $CONF['secureCookieKey'];
1615                                 }
1616                                 $this->set_cookie_salt($salt);
1617                         }
1618                         
1619                         if ( $this->cookie_salt !== 'none' )
1620                         {
1621                                 $ck = $this->hash($ck . $this->cookie_salt);
1622                         }
1623                         $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );
1624                         unset($ck);
1625                                 
1626                         /* renew cookies when not on a shared computer */
1627                         if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )
1628                         {
1629                                 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));
1630                                 $this->setCookies();
1631                         }
1632                 }
1633                 return $this->loggedin;
1634         }
1635         
1636         /**
1637          * Member::logout()
1638          * logout and expire cookie
1639          * 
1640          * @param       Void
1641          * @return      Void
1642          */
1643         public function logout()
1644         {
1645                 global $CONF, $manager;
1646                 
1647                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )
1648                 {
1649                         /* remove cookies on logout */
1650                         setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1651                         setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1652                         $data = array('username' => cookieVar("{$CONF['CookiePrefix']}user"));
1653                         $manager->notify('Logout', $data);
1654                 }
1655                 
1656                 $this->loggedin = 0;
1657                 return;
1658         }
1659         
1660         /**
1661          * Member::isLoggedIn()
1662          * return member is loggedin or not
1663          * 
1664          * @param       Void
1665          * @return      Void
1666          */
1667         public function isLoggedIn()
1668         {
1669                 return $this->loggedin;
1670         }
1671         
1672         /**
1673          * MEMBER:read()
1674          * Read member information from the database
1675          * 
1676          * @param       String  $where  where statement
1677          * @return      Resource        SQL resource
1678          * 
1679          */
1680         public function read($where)
1681         {
1682                 // read info
1683                 $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;
1684                 
1685                 $row = DB::getRow($query);
1686                 
1687                 $this->setRealName($row['mrealname']);
1688                 $this->setEmail($row['memail']);
1689                 $this->password = $row['mpassword'];
1690                 $this->setCookieKey($row['mcookiekey']);
1691                 $this->setURL($row['murl']);
1692                 $this->setDisplayName($row['mname']);
1693                 $this->setAdmin($row['madmin']);
1694                 $this->id = $row['mnumber'];
1695                 $this->setCanLogin($row['mcanlogin']);
1696                 $this->setNotes($row['mnotes']);
1697                 $this->setLocale($row['mlocale']);
1698                 $this->setAutosave($row['mautosave']);
1699                 $this->setAdminSkin($row['madminskin']);
1700                 $this->setBookmarklet($row['mbkmklt']);
1701                 
1702                 return $row ? TRUE : FALSE;
1703         }
1704         
1705         /**
1706          * Member::isBlogAdmin()
1707          * Returns true if member is an admin for the given blog
1708          * (returns false if not a team member)
1709          * 
1710          * @param       Integer $blogid weblog id
1711          * @return      Integer weblog admin or not
1712          * 
1713          */
1714         public function isBlogAdmin($blogid)
1715         {
1716                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'
1717                 . ' tblog=' . intval($blogid)
1718                 . ' and tmember='. $this->getID();
1719                 $res = DB::getValue($query);
1720                 if ( $res )
1721                         return ($res == 1);
1722                 else
1723                         return 0;
1724         }
1725         
1726         /**
1727          * Member::blogAdminRights()
1728          * 
1729          * @param       integer $blogid ID of target weblog
1730          * @return      boolean whether to have admin rights to the weblog or not
1731          * 
1732          */
1733         public function blogAdminRights($blogid)
1734         {
1735                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));
1736         }
1737         
1738         /**
1739          * Member::teamRights()
1740          * 
1741          * @param       integer $blogid ID of target weblog
1742          * @return      boolean whether to have admin right to the weblog or not
1743          * 
1744          */
1745         public function teamRights($blogid)
1746         {
1747                 return ($this->isAdmin() || $this->isTeamMember($blogid));
1748         }
1749         
1750         /**
1751          * Member::isTeamMember()
1752          * Returns true if this member is a team member of the given blog
1753          * 
1754          * @param       integer $blogid ID of target weblog
1755          * @return      boolean whether to join the weblog or not
1756          * 
1757          */
1758         public function isTeamMember($blogid)
1759         {
1760                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'
1761                            . ' tblog=' . intval($blogid)
1762                            . ' and tmember='. $this->getID();
1763                 $res = DB::getResult($query);
1764                 return ($res->rowCount() != 0);
1765         }
1766         
1767         /**
1768          * Member::canAddItem()
1769          * 
1770          * @param       integer $catid  ID of target category
1771          * @return      boolean whether to be able to add items to the category or not
1772          * 
1773          */
1774         public function canAddItem($catid)
1775         {
1776                 global $manager;
1777                 
1778                 // if this is a 'newcat' style newcat
1779                 // no blog admin of destination blog -> NOK
1780                 // blog admin of destination blog -> OK
1781                 if ( i18n::strpos($catid,'newcat') === 0 )
1782                 {
1783                         // get blogid
1784                         list($blogid) = sscanf($catid,"newcat-%d");
1785                         return $this->blogAdminRights($blogid);
1786                 }
1787                 
1788                 // category does not exist -> NOK
1789                 if ( !$manager->existsCategory($catid) )
1790                 {
1791                         return 0;
1792                 }
1793                 
1794                 $blogid = getBlogIDFromCatID($catid);
1795                 
1796                 // no team rights for blog -> NOK
1797                 if (!$this->teamRights($blogid))
1798                 {
1799                         return 0;
1800                 }
1801                 
1802                 // all other cases: OK
1803                 return 1;
1804         }
1805         
1806         /**
1807          * Member::canAlterComment()
1808          * Returns true if this member can edit/delete a commentitem. This can be in the
1809          * following cases:
1810          *        - member is a super-admin
1811          *   - member is the author of the comment
1812          *   - member is admin of the blog associated with the comment
1813          *   - member is author of the item associated with the comment
1814          * 
1815          * @param       integer $commentid      ID of target comment
1816          * @return      boolean delete/edit the comment or not
1817          * 
1818          */
1819         public function canAlterComment($commentid)
1820         {
1821                 if ( $this->isAdmin() )
1822                 {
1823                         return 1;
1824                 }
1825                 
1826                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'
1827                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')
1828                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);
1829                 $res = DB::getRow($query);
1830                 
1831                 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());
1832         }
1833         
1834         /**
1835          * Member::canAlterItem()
1836          * Returns true if this member can edit/delete an item. This is true in the following
1837          * cases: - member is a super-admin
1838          *             - member is the author of the item
1839          *        - member is admin of the the associated blog
1840          * 
1841          * @param       integer $itemid ID of target item
1842          * @return      boolean delete/edit the item or not
1843          * 
1844          */
1845         public function canAlterItem($itemid)
1846         {
1847                 if ($this->isAdmin()) return 1;
1848                 
1849                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);
1850                 $res = DB::getRow($query);
1851                 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);
1852         }
1853         
1854         /**
1855          * Member::canBeDeleted()
1856          * Return true if member can be deleted. This means that there are no items posted by the member left
1857          * 
1858          * @param       void
1859          * @return      boolean whether there is no items or exists
1860          * 
1861          */
1862         public function canBeDeleted()
1863         {
1864                 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());
1865                 return ( $res->rowCount() == 0 );
1866         }
1867         
1868         /**
1869          * Member::canUpdateItem()
1870          * returns true if this member can move/update an item to a given category,
1871          * false if not (see comments fot the tests that are executed)
1872          * 
1873          * @param       integer $itemid
1874          * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)
1875          * @return      boolean whether being able to update the item or not
1876          * 
1877          */
1878         public function canUpdateItem($itemid, $newcat)
1879         {
1880                 global $manager;
1881                 
1882                 // item does not exists -> NOK
1883                 if ( !$manager->existsItem($itemid,1,1) )
1884                 {
1885                         return 0;
1886                 }
1887                 
1888                 // cannot alter item -> NOK
1889                 if (!$this->canAlterItem($itemid))
1890                 {
1891                         return 0;
1892                 }
1893                 
1894                 // if this is a 'newcat' style newcat
1895                 // no blog admin of destination blog -> NOK
1896                 // blog admin of destination blog -> OK
1897                 if ( i18n::strpos($newcat, 'newcat') === 0 )
1898                 {
1899                         // get blogid
1900                         list($blogid) = sscanf($newcat, 'newcat-%d');
1901                         return $this->blogAdminRights($blogid);
1902                 }
1903                 
1904                 // category does not exist -> NOK
1905                 if (!$manager->existsCategory($newcat))
1906                 {
1907                         return 0;
1908                 }
1909                 
1910                 // get item
1911                 $item =& $manager->getItem($itemid,1,1);
1912                 
1913                 // old catid = new catid -> OK
1914                 if ($item['catid'] == $newcat)
1915                 {
1916                         return 1;
1917                 }
1918                 
1919                 // not a valid category -> NOK
1920                 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));
1921                 if ( !$validCat )
1922                 {
1923                         return 0;
1924                 }
1925                 
1926                 // get destination blog
1927                 $item =& $manager->getItem($itemid, 1, 1);
1928                 $source_blogid = $item['blogid'];
1929                 $dest_blogid = getBlogIDFromCatID($newcat);
1930                 
1931                 // not a team member of destination blog -> NOK
1932                 if ( !$this->teamRights($dest_blogid) )
1933                 {
1934                         return 0;
1935                 }
1936                 
1937                 // if member is author of item -> OK
1938                 if ( $item['authorid'] == $this->getID() )
1939                 {
1940                         return 1;
1941                 }
1942                 
1943                 // if member has admin rights on both blogs: OK
1944                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )
1945                 {
1946                         return 1;
1947                 }
1948                 
1949                 // all other cases: NOK
1950                 return 0;
1951         }
1952         
1953         /**
1954          * Member::setCookies()
1955          * Sets the cookies for the member
1956          * 
1957          * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire
1958          *                              at the end of the session in this case.
1959          * @return      void
1960          * 
1961          */
1962         public function setCookies($shared = 0)
1963         {
1964                 global $CONF;
1965                 
1966                 if ( $CONF['SessionCookie'] || $shared )
1967                 {
1968                         $lifetime = 0;
1969                 }
1970                 else
1971                 {
1972                         $lifetime = time()+2592000;
1973                 }
1974                 
1975                 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1976                 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);
1977                 
1978                 // make sure cookies on shared pcs don't get renewed
1979                 if ( $shared )
1980                 {
1981                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);
1982                 }
1983                 return;
1984         }
1985         
1986         /**
1987          * Member::sendActivationLink()
1988          * Send activation mail
1989          * 
1990          * @param       string  $type   activation type
1991          * @param       string  $extra  extra info
1992          * @return      void
1993          */
1994         public function sendActivationLink($type, $extra='')
1995         {
1996                 global $CONF;
1997                 
1998                 if ( !isset($CONF['ActivationDays']) )
1999                 {
2000                         $CONF['ActivationDays'] = 2;
2001                 }
2002                 
2003                 // generate key and URL
2004                 $key = $this->generateActivationEntry($type, $extra);
2005                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;
2006                 
2007                 // choose text to use in mail
2008                 switch ( $type )
2009                 {
2010                         case 'register':
2011                                 $message = _ACTIVATE_REGISTER_MAIL;
2012                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;
2013                                 break;
2014                         case 'forgot':
2015                                 $message = _ACTIVATE_FORGOT_MAIL;
2016                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;
2017                                 break;
2018                         case 'addresschange':
2019                                 $message = _ACTIVATE_CHANGE_MAIL;
2020                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;
2021                                 break;
2022                         default;
2023                 }
2024                 
2025                 // fill out variables in text
2026                 $aVars = array(
2027                         'siteName' => $CONF['SiteName'],
2028                         'siteUrl' => $CONF['IndexURL'],
2029                         'memberName' => $this->getDisplayName(),
2030                         'activationUrl' => $url,
2031                         'activationDays' => $CONF['ActivationDays']
2032                 );
2033                 
2034                 $message = Template::fill($message, $aVars);
2035                 $subject = Template::fill($subject, $aVars);
2036                 
2037                 // send mail
2038                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());
2039                 
2040                 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');
2041                 return;
2042         }
2043         
2044         /**
2045          * Member::getAdminBlogs()
2046          * Returns an array of all blogids for which member has admin rights
2047          * 
2048          * @param       void
2049          * @return      array   weblog IDs in which this member has admin rights
2050          * 
2051          */
2052         public function getAdminBlogs()
2053         {
2054                 $blogs = array();
2055                 
2056                 if ($this->isAdmin())
2057                 {
2058                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');
2059                 }
2060                 else
2061                 {
2062                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();
2063                 }
2064                 
2065                 $res = DB::getResult($query);
2066                 if ( $res->rowCount() > 0 )
2067                 {
2068                         foreach ( $res as $row )
2069                         {
2070                                 array_push($blogs, $row['blogid']);
2071                         }
2072                 }
2073                 return $blogs;
2074         }
2075         
2076         /**
2077          * Member::getTeamBlogs()
2078          * Returns an array of all blogids for which member has team rights
2079          * 
2080          * @param       boolean $incAdmin       whether checking weblog admin rights or not
2081          * @return      array   weblog IDs in which this member join
2082          * 
2083          */
2084         public function getTeamBlogs($incAdmin = 1)
2085         {
2086                 $incAdmin = intval($incAdmin);
2087                 $blogs = array();
2088                 
2089                 if ( $this->isAdmin() && $incAdmin )
2090                 {
2091                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');
2092                 }
2093                 else
2094                 {
2095                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();
2096                 }
2097                 
2098                 $res = DB::getResult($query);
2099                 if ( $res->rowCount() > 0 )
2100                 {
2101                         foreach ( $res as $row )
2102                         {
2103                                 array_push($blogs, $row['blogid']);
2104                         }
2105                 }
2106                 return $blogs;
2107         }
2108         
2109         /**
2110          * Member::getNotifyFromMailAddress()
2111          * 
2112          * Returns an email address from which notification of commenting/karma voting can
2113          * be sent. A suggestion can be given for when the member is not logged in
2114          * 
2115          * @param       String  $suggest
2116          * @return      String  mail address or suggestion
2117          */
2118         public function getNotifyFromMailAddress($suggest = "")
2119         {
2120                 global $CONF;
2121                 if ( $this->isLoggedIn() )
2122                 {
2123                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";
2124                 }
2125                 else if ( NOTIFICATION::address_validation($suggest) )
2126                 {
2127                         return $suggest;
2128                 }
2129                 return $CONF['AdminEmail'];
2130         }
2131         
2132         /**
2133          * Member::write()
2134          * Write data to database
2135          * 
2136          * @param       void
2137          * @return      void
2138          * 
2139          */
2140         public function write()
2141         {
2142                 $query =  'UPDATE '.sql_table('member')
2143                         . ' SET mname=' . DB::quoteValue($this->displayname) . ', '
2144                            . 'mrealname='. DB::quoteValue($this->realname) . ', '
2145                            . 'mpassword='. DB::quoteValue($this->password) . ', '
2146                            . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '
2147                            . 'murl=' . DB::quoteValue($this->url) . ', '
2148                            . 'memail=' . DB::quoteValue($this->email) . ', '
2149                            . 'madmin=' . intval($this->admin) . ', '
2150                            . 'mnotes=' . DB::quoteValue($this->notes) . ', '
2151                            . 'mcanlogin=' . intval($this->canlogin) . ', '
2152                            . 'mlocale=' . DB::quoteValue($this->locale) . ', '
2153                            . 'madminskin=' . DB::quoteValue($this->adminskin) . ', '
2154                            . 'mbkmklt=' . DB::quoteValue($this->bookmarklet) . ', '
2155                            . 'mautosave=' . intval($this->autosave) . ' '
2156                         . 'WHERE mnumber=' . intval($this->id);
2157                 DB::execute($query);
2158                 return;
2159         }
2160         
2161         public function checkCookieKey($key)
2162         {
2163                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );
2164         }
2165         
2166         public function checkPassword($pw)
2167         {
2168                 /* for lower compatibility (md5) */
2169                 if ( strlen($this->password) === 32 )
2170                 {
2171                         return (md5($pw) == $this->password);
2172                 }
2173                 return ($this->hash($pw) == $this->password);
2174         }
2175         
2176         public function getRealName()
2177         {
2178                 return $this->realname;
2179         }
2180         
2181         public function setRealName($name)
2182         {
2183                 $this->realname = $name;
2184         }
2185         
2186         public function getEmail()
2187         {
2188                 return $this->email;
2189         }
2190         
2191         public function setEmail($email)
2192         {
2193                 $this->email = $email;
2194         }
2195         
2196         public function getPassword()
2197         {
2198                 return $this->password;
2199         }
2200         
2201         public function setPassword($pwd)
2202         {
2203                 $this->password = $this->hash($pwd);
2204         }
2205         
2206         public function getCookieKey()
2207         {
2208                 return $this->cookiekey;
2209         }
2210         
2211         /**
2212          * Member::newCookieKey()
2213          * Generate new cookiekey, save it, and return it
2214          * 
2215          * @param       void
2216          * @return      void
2217          * 
2218          */
2219         public function newCookieKey()
2220         {
2221                 mt_srand( (double) microtime() * 1000000);
2222                 $this->cookiekey = $this->hash(uniqid(mt_rand()));
2223                 $this->write();
2224                 return $this->cookiekey;
2225         }
2226         
2227         public function setCookieKey($val)
2228         {
2229                 $this->cookiekey = $val;
2230         }
2231         
2232         public function getURL()
2233         {
2234                 return $this->url;
2235         }
2236         
2237         public function setURL($site)
2238         {
2239                 $this->url = $site;
2240         }
2241         
2242         public function setAdminSkin($skin)
2243         {
2244                 $this->adminskin = $skin;
2245         }
2246
2247         public function setBookmarklet($skin)
2248         {
2249                 $this->bookmarklet = $skin;
2250         }
2251         
2252         public function getAdminSkin()
2253         {
2254                 return $this->adminskin;
2255         }
2256
2257         public function getBookmarklet()
2258         {
2259                 return $this->bookmarklet;
2260         }
2261
2262         public function getLocale()
2263         {
2264                 return $this->locale;
2265         }
2266         
2267         public function setLocale($locale)
2268         {
2269                 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)
2270                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )
2271                 {
2272                         $locale = '';
2273                 }
2274                 $this->locale = $locale;
2275                 return;
2276         }
2277         
2278         public function setDisplayName($nick)
2279         {
2280                 $this->displayname = $nick;
2281         }
2282         
2283         public function getDisplayName()
2284         {
2285                 return $this->displayname;
2286         }
2287         
2288         public function isAdmin()
2289         {
2290                 return $this->admin;
2291         }
2292         
2293         public function setAdmin($val)
2294         {
2295                 $this->admin = $val;
2296         }
2297         
2298         public function canLogin()
2299         {
2300                 return $this->canlogin;
2301         }
2302         
2303         public function setCanLogin($val)
2304         {
2305                 $this->canlogin = $val;
2306         }
2307         
2308         public function getNotes()
2309         {
2310                 return $this->notes;
2311         }
2312         
2313         public function setNotes($val)
2314         {
2315                 $this->notes = $val;
2316         }
2317         
2318         public function getAutosave()
2319         {
2320                 return $this->autosave;
2321         }
2322         
2323         public function setAutosave($val)
2324         {
2325                 $this->autosave = $val;
2326                 return;
2327         }
2328         
2329         /**
2330          * Member::getID()
2331          * 
2332          * @param       void
2333          * @return      integer id of this member object
2334          * 
2335          */
2336         public function getID()
2337         {
2338                 return $this->id;
2339         }
2340         
2341         /**
2342          * Member::exists()
2343          * Returns true if there is a member with the given login name
2344          * 
2345          * @static
2346          * @param       string  $name   target name
2347          * @return      boolean whether target name exists or not
2348          */
2349         public static function exists($name)
2350         {
2351                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));
2352                 return ( $r->rowCount() != 0 );
2353         }
2354         
2355         /**
2356          * Member::existsID()
2357          * Returns true if there is a member with the given ID
2358          * 
2359          * @static
2360          * @param       integer $id     target id
2361          * @return      boolean whether target id exists or not
2362          * 
2363          */
2364         public static function existsID($id)
2365         {
2366                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));
2367                 return ( $r->rowCount() != 0 );
2368         }
2369         
2370         /**
2371          * Member::isNameProtected()
2372          *  Checks if a username is protected.
2373          *  If so, it can not be used on anonymous comments
2374          * 
2375          * @param       string  $name   target name
2376          * @return      boolean whether the name exists or not
2377          * 
2378          */
2379         public function isNameProtected($name)
2380         {
2381                 // extract name
2382                 $name = strip_tags($name);
2383                 $name = trim($name);
2384                 return self::exists($name);
2385         }
2386         
2387         /**
2388          * Member::create()
2389          * Adds a new member
2390          * 
2391          * @static
2392          * @param       String  $name
2393          * @param       String  $realname
2394          * @param       String  $password
2395          * @param       String  $email
2396          * @param       String  $url
2397          * @param       String  $admin
2398          * @param       String  $canlogin
2399          * @param       String  $notes
2400          * @return      String  1 if success, others if fail
2401          */
2402         static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)
2403         {
2404                 if ( !NOTIFICATION::address_validation($email) )
2405                 {
2406                         return _ERROR_BADMAILADDRESS;
2407                 }
2408                 
2409                 /* TODO: this method should be in MEMBER class, not globalfunctions */
2410                 if ( !isValidDisplayName($name) )
2411                 {
2412                         return _ERROR_BADNAME;
2413                 }
2414                 
2415                 if ( self::exists($name) )
2416                 {
2417                         return _ERROR_NICKNAMEINUSE;
2418                 }
2419                 
2420                 if ( !$realname )
2421                 {
2422                         return _ERROR_REALNAMEMISSING;
2423                 }
2424                 
2425                 /* TODO: check the number of characters */
2426                 if ( !$password )
2427                 {
2428                         return _ERROR_PASSWORDMISSING;
2429                 }
2430                 
2431                 /* 
2432                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,
2433                  *  this cause a malformed URL. Let's fix it.
2434                  */
2435                 
2436                 if ( !preg_match('#^https?://#', $url) )
2437                 {
2438                         $url = 'http://' . $url;
2439                 }
2440                 
2441                 $name           = DB::quoteValue($name);
2442                 $realname       = DB::quoteValue($realname);
2443                 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */
2444                 $password       = DB::quoteValue(md5($password));
2445                 $email          = DB::quoteValue($email);
2446                 $url            = DB::quoteValue($url);
2447                 $admin          = (integer) $admin;
2448                 $canlogin       = (integer) $canlogin;
2449                 $notes          = DB::quoteValue($notes);
2450                 
2451                 $query = "INSERT INTO %s"
2452                        . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"
2453                        . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";
2454                 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);
2455                 DB::execute($query);
2456                 
2457                 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);
2458                 
2459                 return 1;
2460         }
2461         
2462         /**
2463          * Member::getActivationInfo()
2464          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)
2465          * 
2466          * @static
2467          * @param       string  $key    activation key
2468          * @return      mixed   return 0 if failed, else return activation table object
2469          * 
2470          */
2471         public static function getActivationInfo($key)
2472         {
2473                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);
2474                 $res = DB::getResult($query);
2475                 
2476                 if ( !$res || ($res->rowCount() == 0) )
2477                 {
2478                         return 0;
2479                 }
2480                 return $res->fetch();
2481         }
2482         
2483         /**
2484          * Member::generateActivationEntry()
2485          * Creates an account activation key
2486          * addresschange -> old email address
2487          * 
2488          * @param       string  $type   one of the following values (determines what to do when activation expires)
2489          *                              'register'      (new member registration)
2490          *                              'forgot'        (forgotton password)
2491          *                              'addresschange' (member address has changed)
2492          * @param       string  $extra  extra info (needed when validation link expires)
2493          * @return      string  activation key
2494          */
2495         public function generateActivationEntry($type, $extra = '')
2496         {
2497                 // clean up old entries
2498                 $this->cleanupActivationTable();
2499                 
2500                 // kill any existing entries for the current member (delete is ok)
2501                 // (only one outstanding activation key can be present for a member)
2502                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));
2503                 
2504                 // indicates if the member can log in while the link is active
2505                 $canLoginWhileActive = false;
2506                 switch ( $type )
2507                 {
2508                         case 'forgot':
2509                                 $canLoginWhileActive = true;
2510                                 break;
2511                         case 'register':
2512                                 break;
2513                         case 'addresschange':
2514                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );
2515                                 break;
2516                 }
2517                 
2518                 $ok = false;
2519                 while ( !$ok )
2520                 {
2521                         // generate a random key
2522                         srand((double)microtime()*1000000);
2523                         $key = $this->hash(uniqid(rand(), true));
2524                         
2525                         // attempt to add entry in database
2526                         // add in database as non-active
2527                         $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';
2528                         $query = sprintf($query
2529                                 , sql_table('activation')
2530                                 , DB::quoteValue($key)
2531                                 , DB::formatDateTime()
2532                                 , intval($this->getID())
2533                                 , DB::quoteValue($type)
2534                                 , DB::quoteValue($extra)
2535                         );
2536                         if ( DB::execute($query) !== FALSE )
2537                                 $ok = true;
2538                 }
2539                 
2540                 // mark member as not allowed to log in
2541                 if ( !$canLoginWhileActive )
2542                 {
2543                         $this->setCanLogin(0);
2544                         $this->write();
2545                 }
2546                 
2547                 // return the key
2548                 return $key;
2549         }
2550         
2551         /**
2552          * Member::activate()
2553          * Inidicates that an activation link has been clicked and any forms displayed
2554          * there have been successfully filled out.
2555          * 
2556          * @param       string  $key    activation key
2557          * @return      boolean
2558          * 
2559          */
2560         public function activate($key)
2561         {
2562                 // get activate info
2563                 $info = self::getActivationInfo($key);
2564                 
2565                 // no active key
2566                 if ( !$info )
2567                 {
2568                         return false;
2569                 }
2570                 
2571                 switch ( $info['vtype'] )
2572                 {
2573                         case 'forgot':
2574                                 // nothing to do
2575                                 break;
2576                         case 'register':
2577                                 // set canlogin value
2578                                 global $CONF;
2579                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));
2580                                 break;
2581                         case 'addresschange':
2582                                 // reset old 'canlogin' value
2583                                 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);
2584                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));
2585                                 break;
2586                 }
2587                 
2588                 // delete from activation table
2589                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));
2590                 
2591                 // success!
2592                 return true;
2593         }
2594         
2595         /**
2596          * Member::cleanupActivationTable()
2597          * Cleans up entries in the activation table. All entries older than 2 days are removed.
2598          * (static)
2599          * 
2600          * @param       void
2601          * @return      void
2602          */
2603         public function cleanupActivationTable()
2604         {
2605                 $actdays = 2;
2606                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )
2607                 {
2608                         $actdays = intval($CONF['ActivationDays']);
2609                 }
2610                 else
2611                 {
2612                         $CONF['ActivationDays'] = 2;
2613                 }
2614                 $boundary = time() - (60 * 60 * 24 * $actdays);
2615                 
2616                 // 1. walk over all entries, and see if special actions need to be performed
2617                 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
2618                 $res = DB::getResult($query);
2619                 
2620                 foreach ( $res as $row )
2621                 {
2622                         switch ( $row['vtype'] )
2623                         {
2624                                 case 'register':
2625                                         // delete all information about this site member. registration is undone because there was
2626                                         // no timely activation
2627                                         include_once($DIR_LIBS . 'ADMIN.php');
2628                                         Admin::deleteOneMember(intval($row['vmember']));
2629                                         break;
2630                                 case 'addresschange':
2631                                         // revert the e-mail address of the member back to old address
2632                                         list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);
2633                                         DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));
2634                                         break;
2635                                 case 'forgot':
2636                                         // delete the activation link and ignore. member can request a new password using the
2637                                         // forgot password link
2638                                         break;
2639                         }
2640                 }
2641                 
2642                 // 2. delete activation entries for real
2643                 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));
2644                 DB::execute($query);
2645                 return;
2646         }
2647         
2648         /**
2649          * Member::$language
2650          * 
2651          * @obsolete
2652          * @param       void
2653          * @return      void
2654          * 
2655          */
2656         public $language = '';
2657         /**
2658          * Member::getLanguage()
2659          * 
2660          * @obsolete
2661          * @param       void
2662          * @return      void
2663          * 
2664          */
2665         public function getLanguage()
2666         {
2667                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )
2668                 {
2669                         $language = '';
2670                 }
2671                 return $language;
2672         }
2673 }
2674 >>>>>>> skinnable-master