OSDN Git Service

CHANGE: Adminクラスのスキン関係メソッドの整理・統合
[nucleus-jp/nucleus-next.git] / nucleus / libs / MEMBER.php
1 <?php\r
2 \r
3 /* \r
4  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
5  * Copyright (C) 2002-2009 The Nucleus Group\r
6  * \r
7  * This program is free software; you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License\r
9  * as published by the Free Software Foundation; either version 2\r
10  * of the License, or (at your option) any later version.\r
11  * (see nucleus/documentation/index.html#license for more info)\r
12  */\r
13 /**\r
14  * A class representing site members\r
15  * \r
16  * @license http://nucleuscms.org/license.txt GNU General Public License\r
17  * @copyright Copyright (C) 2002-2009 The Nucleus Group\r
18  * @version $Id: MEMBER.php 1616 2012-01-08 09:48:15Z sakamocchi $\r
19  */\r
20 class Member\r
21 {\r
22         // 1 when authenticated, 0 when not\r
23         public $loggedin = 0;\r
24         public $password;               // not the actual password, but rather a MD5 hash\r
25         private $algorism = 'md5';\r
26         \r
27         public $cookiekey;              // value that should also be in the client cookie to allow authentication\r
28         private $cookie_salt = FALSE;\r
29         \r
30         // member info\r
31         public $id = -1;\r
32         public $realname;\r
33         public $displayname;\r
34         public $email;\r
35         public $url;\r
36         public $admin = 0;                      // (either 0 or 1)\r
37         public $canlogin = 0;           // (either 0 or 1)\r
38         public $notes;\r
39         public $autosave = 1;           // if the member use the autosave draft function\r
40         public $adminskin = 0;          // if the member use the autosave draft function\r
41         public $bookmarklet = 0;                // if the member use the autosave draft function\r
42         private $locale = '';\r
43         \r
44         /**\r
45          * Member::__construct()\r
46          * Constructor for a member object\r
47          * \r
48          * @param       Void\r
49          * @return      Void\r
50          * \r
51          */\r
52         public function __construct()\r
53         {\r
54                 return;\r
55         }\r
56         \r
57         /**\r
58          * Member::createFromName()\r
59          * Create a member object for a given displayname\r
60          * \r
61          * @static\r
62          * @param       String  $displayname    login name\r
63          * @return      Object  member object\r
64          * \r
65          */\r
66         public static function &createFromName($displayname)\r
67         {\r
68                 $mem = new Member();\r
69                 $mem->readFromName($displayname);\r
70                 return $mem;\r
71         }\r
72         \r
73         /**\r
74          * Member::createFromID()\r
75          * Create a member object for a given ID\r
76          * \r
77          * @static\r
78          * @param       Integer $id     id for member\r
79          */\r
80         public static function &createFromID($id)\r
81         {\r
82                 $mem = new Member();\r
83                 $mem->readFromID($id);\r
84                 return $mem;\r
85         }\r
86         \r
87         /**\r
88          * Member::readFromName()\r
89          * Read member table in database\r
90          * \r
91          * @param       String  $displayname    login name\r
92          * @return      Object  SQL resource\r
93          * \r
94          */\r
95         public function readFromName($displayname)\r
96         {\r
97                 return $this->read('mname='.DB::quoteValue($displayname));\r
98         }\r
99         \r
100         /**\r
101          * Member::readFromID()\r
102          * Read member table in database\r
103          * \r
104          * @param       Integer $id     id for member\r
105          * @return      Object  SQL resource\r
106          * \r
107          */\r
108         public function readFromID($id)\r
109         {\r
110                 return $this->read("mnumber=" . intval($id));\r
111         }\r
112         \r
113         /**\r
114          * Member::hash()\r
115          * hash the target string\r
116          * \r
117          * @param       String  $string target string\r
118          * @return      Void    hashed string\r
119          */\r
120         public function hash($string)\r
121         {\r
122                 switch ( $this->algorism )\r
123                 {\r
124                         case 'md5':\r
125                         default:\r
126                                 $string = md5($string);\r
127                 }\r
128                 return $string;\r
129         }\r
130         \r
131         /**\r
132          * Member::set_cookie_salt()\r
133          * \r
134          * @param       integer $key    secureCookieKey value\r
135          * @return      void\r
136          * \r
137          */\r
138         private function set_cookie_salt($key = 0)\r
139         {\r
140                 if ( !$key )\r
141                 {\r
142                         $key = 24;\r
143                 }\r
144                 \r
145                 switch( $key )\r
146                 {\r
147                         case 8:\r
148                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
149                                 break;\r
150                         case 16:\r
151                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
152                                 break;\r
153                         case 24:\r
154                                 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
155                                 break;\r
156                         case 32:\r
157                                 $this->cookie_salt = serverVar('REMOTE_ADDR');\r
158                                 break;\r
159                         default:\r
160                                 $this->cookie_salt = 'none';\r
161                 }\r
162                 return;\r
163         }\r
164         \r
165         /**\r
166          * Member::login()\r
167          * Tries to login as a given user.\r
168          * Returns true when succeeded, returns false when failed\r
169          * 3.40 adds CustomLogin event\r
170          * \r
171          * @param       String  $login  login name for member\r
172          * @param       String  $password       password for member\r
173          * @param       Integer $shared whether the user agent is shared or not\r
174          * \r
175          */\r
176         public function login($login, $password, $shared=1)\r
177         {\r
178                 global $CONF, $errormessage, $manager;\r
179                 \r
180                 /* TODO: validation for $login, $password, $shared */\r
181                 if ( $login == '' || $password == '' )\r
182                 {\r
183                         return 0;\r
184                 }\r
185                 /* limiting the length of password to avoid hash collision */\r
186                 $password=i18n::substr($password, 0, 40);\r
187                 \r
188                 /* \r
189                  * generate cookie salt from secure cookie key settings\r
190                 * (either 'none', 0, 8, 16, 24, or 32)\r
191                 */\r
192                 if ( !$this->cookie_salt )\r
193                 {\r
194                         $salt = 0;\r
195                         if ( array_key_exists('secureCookieKey', $CONF) )\r
196                         {\r
197                                 $salt = $CONF['secureCookieKey'];\r
198                         }\r
199                         $this->set_cookie_salt($salt);\r
200                 }\r
201                 \r
202                 $success = 0;\r
203                 $allowlocal = 1;\r
204                 $manager->notify('CustomLogin', array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal));\r
205                 \r
206                 $this->loggedin = 0;\r
207                 if ( $success )\r
208                 {\r
209                         $this->loggedin = ( $this->readFromName($login) );\r
210                 }\r
211                 elseif ( $allowlocal )\r
212                 {\r
213                         $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );\r
214                 }\r
215                 \r
216                 /* login failed */\r
217                 if ( !$this->loggedin )\r
218                 {\r
219                         $trimlogin = trim($login);\r
220                         if ( empty($trimlogin) )\r
221                         {\r
222                                 $errormessage = "Please enter a username.";\r
223                         }\r
224                         else\r
225                         {\r
226                                 $errormessage = 'Login failed for ' . $login;\r
227                         }\r
228                         $manager->notify('LoginFailed', array('username' => $login) );\r
229                         ActionLog::add(INFO, $errormessage);\r
230                 }\r
231                 /* login success */\r
232                 else\r
233                 {\r
234                         /* For lower compatibility */\r
235                         if ( strlen($this->password) === 32 )\r
236                         {\r
237                                 $this->password = $this->hash($password);\r
238                         }\r
239                         \r
240                         $this->newCookieKey();\r
241                         $this->setCookies($shared);\r
242                         \r
243                         if ( $this->cookie_salt !== 'none' )\r
244                         {\r
245                                 /* secure cookie key */\r
246                                 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));\r
247                                 $this->write();\r
248                         }\r
249                         \r
250                         $errormessage = '';\r
251                         $manager->notify('LoginSuccess', array('member' => &$member, 'username' => $login) );\r
252                         ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");\r
253                 }\r
254                 \r
255                 return $this->loggedin;\r
256         }\r
257         \r
258         /**\r
259          * Member::cookielogin()\r
260          * Login using cookie key\r
261          * \r
262          * @param       String  $login  not used\r
263          * @param       String  $cookiekey      not used\r
264          * @return      Boolean login or not\r
265          */\r
266         public function cookielogin($login='', $cookiekey='')\r
267         {\r
268                 global $CONF, $manager;\r
269                 \r
270                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
271                 {\r
272                         /* Cookie Authentication */\r
273                         $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");\r
274                                 \r
275                         /* TODO: validation for each cookie values */\r
276                                 \r
277                         /* limiting the length of password to avoid hash collision */\r
278                         $ck = i18n::substr($ck,0,32);\r
279                                 \r
280                         /* \r
281                          * generate cookie salt from secure cookie key settings\r
282                         * (either 'none', 0, 8, 16, 24, or 32)\r
283                         */\r
284                         if ( !$this->cookie_salt )\r
285                         {\r
286                                 $salt = 0;\r
287                                 if ( array_key_exists('secureCookieKey', $CONF) )\r
288                                 {\r
289                                         $salt = $CONF['secureCookieKey'];\r
290                                 }\r
291                                 $this->set_cookie_salt($salt);\r
292                         }\r
293                         \r
294                         if ( $this->cookie_salt !== 'none' )\r
295                         {\r
296                                 $ck = $this->hash($ck . $this->cookie_salt);\r
297                         }\r
298                         $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );\r
299                         unset($ck);\r
300                                 \r
301                         /* renew cookies when not on a shared computer */\r
302                         if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )\r
303                         {\r
304                                 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));\r
305                                 $this->setCookies();\r
306                         }\r
307                 }\r
308                 return $this->loggedin;\r
309         }\r
310         \r
311         /**\r
312          * Member::logout()\r
313          * logout and expire cookie\r
314          * \r
315          * @param       Void\r
316          * @return      Void\r
317          */\r
318         public function logout()\r
319         {\r
320                 global $CONF, $manager;\r
321                 \r
322                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
323                 {\r
324                         /* remove cookies on logout */\r
325                         setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
326                         setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
327                         $manager->notify('Logout', array('username' => cookieVar("{$CONF['CookiePrefix']}user") ) );\r
328                 }\r
329                 \r
330                 $this->loggedin = 0;\r
331                 return;\r
332         }\r
333         \r
334         /**\r
335          * Member::isLoggedIn()\r
336          * return member is loggedin or not\r
337          * \r
338          * @param       Void\r
339          * @return      Void\r
340          */\r
341         public function isLoggedIn()\r
342         {\r
343                 return $this->loggedin;\r
344         }\r
345         \r
346         /**\r
347          * MEMBER:read()\r
348          * Read member information from the database\r
349          * \r
350          * @param       String  $where  where statement\r
351          * @return      Resource        SQL resource\r
352          * \r
353          */\r
354         public function read($where)\r
355         {\r
356                 // read info\r
357                 $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;\r
358                 \r
359                 $row = DB::getRow($query);\r
360                 \r
361                 $this->setRealName($row['mrealname']);\r
362                 $this->setEmail($row['memail']);\r
363                 $this->password = $row['mpassword'];\r
364                 $this->setCookieKey($row['mcookiekey']);\r
365                 $this->setURL($row['murl']);\r
366                 $this->setDisplayName($row['mname']);\r
367                 $this->setAdmin($row['madmin']);\r
368                 $this->id = $row['mnumber'];\r
369                 $this->setCanLogin($row['mcanlogin']);\r
370                 $this->setNotes($row['mnotes']);\r
371                 $this->setLocale($row['mlocale']);\r
372                 $this->setAutosave($row['mautosave']);\r
373                 $this->setAdminSkin($row['madminskin']);\r
374                 $this->setBookmarklet($row['mbkmklt']);\r
375 \r
376                 return $row ? TRUE : FALSE;\r
377         }\r
378         \r
379         /**\r
380          * Member::isBlogAdmin()\r
381          * Returns true if member is an admin for the given blog\r
382          * (returns false if not a team member)\r
383          * \r
384          * @param       Integer $blogid weblog id\r
385          * @return      Integer weblog admin or not\r
386          * \r
387          */\r
388         public function isBlogAdmin($blogid)\r
389         {\r
390                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'\r
391                 . ' tblog=' . intval($blogid)\r
392                 . ' and tmember='. $this->getID();\r
393                 $res = DB::getValue($query);\r
394                 if ( $res )\r
395                         return ($res == 1);\r
396                 else\r
397                         return 0;\r
398         }\r
399         \r
400         /**\r
401          * Member::blogAdminRights()\r
402          * \r
403          * @param       integer $blogid ID of target weblog\r
404          * @return      boolean whether to have admin rights to the weblog or not\r
405          * \r
406          */\r
407         public function blogAdminRights($blogid)\r
408         {\r
409                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));\r
410         }\r
411         \r
412         /**\r
413          * Member::teamRights()\r
414          * \r
415          * @param       integer $blogid ID of target weblog\r
416          * @return      boolean whether to have admin right to the weblog or not\r
417          * \r
418          */\r
419         public function teamRights($blogid)\r
420         {\r
421                 return ($this->isAdmin() || $this->isTeamMember($blogid));\r
422         }\r
423         \r
424         /**\r
425          * Member::isTeamMember()\r
426          * Returns true if this member is a team member of the given blog\r
427          * \r
428          * @param       integer $blogid ID of target weblog\r
429          * @return      boolean whether to join the weblog or not\r
430          * \r
431          */\r
432         public function isTeamMember($blogid)\r
433         {\r
434                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'\r
435                            . ' tblog=' . intval($blogid)\r
436                            . ' and tmember='. $this->getID();\r
437                 $res = DB::getResult($query);\r
438                 return ($res->rowCount() != 0);\r
439         }\r
440         \r
441         /**\r
442          * Member::canAddItem()\r
443          * \r
444          * @param       integer $catid  ID of target category\r
445          * @return      boolean whether to be able to add items to the category or not\r
446          * \r
447          */\r
448         public function canAddItem($catid)\r
449         {\r
450                 global $manager;\r
451                 \r
452                 // if this is a 'newcat' style newcat\r
453                 // no blog admin of destination blog -> NOK\r
454                 // blog admin of destination blog -> OK\r
455                 if ( i18n::strpos($catid,'newcat') === 0 )\r
456                 {\r
457                         // get blogid\r
458                         list($blogid) = sscanf($catid,"newcat-%d");\r
459                         return $this->blogAdminRights($blogid);\r
460                 }\r
461                 \r
462                 // category does not exist -> NOK\r
463                 if ( !$manager->existsCategory($catid) )\r
464                 {\r
465                         return 0;\r
466                 }\r
467                 \r
468                 $blogid = getBlogIDFromCatID($catid);\r
469                 \r
470                 // no team rights for blog -> NOK\r
471                 if (!$this->teamRights($blogid))\r
472                 {\r
473                         return 0;\r
474                 }\r
475                 \r
476                 // all other cases: OK\r
477                 return 1;\r
478         }\r
479         \r
480         /**\r
481          * Member::canAlterComment()\r
482          * Returns true if this member can edit/delete a commentitem. This can be in the\r
483          * following cases:\r
484          *        - member is a super-admin\r
485          *   - member is the author of the comment\r
486          *   - member is admin of the blog associated with the comment\r
487          *   - member is author of the item associated with the comment\r
488          * \r
489          * @param       integer $commentid      ID of target comment\r
490          * @return      boolean delete/edit the comment or not\r
491          * \r
492          */\r
493         public function canAlterComment($commentid)\r
494         {\r
495                 if ( $this->isAdmin() )\r
496                 {\r
497                         return 1;\r
498                 }\r
499                 \r
500                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'\r
501                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')\r
502                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);\r
503                 $res = DB::getRow($query);\r
504                 \r
505                 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());\r
506         }\r
507         \r
508         /**\r
509          * Member::canAlterItem()\r
510          * Returns true if this member can edit/delete an item. This is true in the following\r
511          * cases: - member is a super-admin\r
512          *             - member is the author of the item\r
513          *        - member is admin of the the associated blog\r
514          * \r
515          * @param       integer $itemid ID of target item\r
516          * @return      boolean delete/edit the item or not\r
517          * \r
518          */\r
519         public function canAlterItem($itemid)\r
520         {\r
521                 if ($this->isAdmin()) return 1;\r
522                 \r
523                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);\r
524                 $res = DB::getRow($query);\r
525                 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);\r
526         }\r
527         \r
528         /**\r
529          * Member::canBeDeleted()\r
530          * Return true if member can be deleted. This means that there are no items posted by the member left\r
531          * \r
532          * @param       void\r
533          * @return      boolean whether there is no items or exists\r
534          * \r
535          */\r
536         public function canBeDeleted()\r
537         {\r
538                 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());\r
539                 return ( $res->rowCount() == 0 );\r
540         }\r
541         \r
542         /**\r
543          * Member::canUpdateItem()\r
544          * returns true if this member can move/update an item to a given category,\r
545          * false if not (see comments fot the tests that are executed)\r
546          * \r
547          * @param       integer $itemid\r
548          * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)\r
549          * @return      boolean whether being able to update the item or not\r
550          * \r
551          */\r
552         public function canUpdateItem($itemid, $newcat)\r
553         {\r
554                 global $manager;\r
555                 \r
556                 // item does not exists -> NOK\r
557                 if ( !$manager->existsItem($itemid, 1, 1) )\r
558                 {\r
559                         return 0;\r
560                 }\r
561                 \r
562                 // cannot alter item -> NOK\r
563                 if (!$this->canAlterItem($itemid))\r
564                 {\r
565                         return 0;\r
566                 }\r
567                 \r
568                 // if this is a 'newcat' style newcat\r
569                 // no blog admin of destination blog -> NOK\r
570                 // blog admin of destination blog -> OK\r
571                 if ( i18n::strpos($newcat, 'newcat') === 0 )\r
572                 {\r
573                         // get blogid\r
574                         list($blogid) = sscanf($newcat, 'newcat-%d');\r
575                         return $this->blogAdminRights($blogid);\r
576                 }\r
577                 \r
578                 // category does not exist -> NOK\r
579                 if (!$manager->existsCategory($newcat))\r
580                 {\r
581                         return 0;\r
582                 }\r
583                 \r
584                 // get item\r
585                 $item =& $manager->getItem($itemid, 1, 1);\r
586                 \r
587                 // old catid = new catid -> OK\r
588                 if ($item['catid'] == $newcat)\r
589                 {\r
590                         return 1;\r
591                 }\r
592                 \r
593                 // not a valid category -> NOK\r
594                 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));\r
595                 if ( !$validCat )\r
596                 {\r
597                         return 0;\r
598                 }\r
599                 \r
600                 // get destination blog\r
601                 $item =& $manager->getItem($itemid, 1, 1);\r
602                 $source_blogid = $item['blogid'];\r
603                 $dest_blogid = getBlogIDFromCatID($newcat);\r
604                 \r
605                 // not a team member of destination blog -> NOK\r
606                 if ( !$this->teamRights($dest_blogid) )\r
607                 {\r
608                         return 0;\r
609                 }\r
610                 \r
611                 // if member is author of item -> OK\r
612                 if ( $item['authorid'] == $this->getID() )\r
613                 {\r
614                         return 1;\r
615                 }\r
616                 \r
617                 // if member has admin rights on both blogs: OK\r
618                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )\r
619                 {\r
620                         return 1;\r
621                 }\r
622                 \r
623                 // all other cases: NOK\r
624                 return 0;\r
625         }\r
626         \r
627         /**\r
628          * Member::setCookies()\r
629          * Sets the cookies for the member\r
630          * \r
631          * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire\r
632          *                              at the end of the session in this case.\r
633          * @return      void\r
634          * \r
635          */\r
636         public function setCookies($shared = 0)\r
637         {\r
638                 global $CONF;\r
639                 \r
640                 if ( $CONF['SessionCookie'] || $shared )\r
641                 {\r
642                         $lifetime = 0;\r
643                 }\r
644                 else\r
645                 {\r
646                         $lifetime = time()+2592000;\r
647                 }\r
648                 \r
649                 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
650                 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
651                 \r
652                 // make sure cookies on shared pcs don't get renewed\r
653                 if ( $shared )\r
654                 {\r
655                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);\r
656                 }\r
657                 return;\r
658         }\r
659         \r
660         /**\r
661          * Member::sendActivationLink()\r
662          * Send activation mail\r
663          * \r
664          * @param       string  $type   activation type\r
665          * @param       string  $extra  extra info\r
666          * @return      void\r
667          */\r
668         public function sendActivationLink($type, $extra='')\r
669         {\r
670                 global $CONF;\r
671                 \r
672                 if ( !isset($CONF['ActivationDays']) )\r
673                 {\r
674                         $CONF['ActivationDays'] = 2;\r
675                 }\r
676                 \r
677                 // generate key and URL\r
678                 $key = $this->generateActivationEntry($type, $extra);\r
679                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;\r
680                 \r
681                 // choose text to use in mail\r
682                 switch ( $type )\r
683                 {\r
684                         case 'register':\r
685                                 $message = _ACTIVATE_REGISTER_MAIL;\r
686                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;\r
687                                 break;\r
688                         case 'forgot':\r
689                                 $message = _ACTIVATE_FORGOT_MAIL;\r
690                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;\r
691                                 break;\r
692                         case 'addresschange':\r
693                                 $message = _ACTIVATE_CHANGE_MAIL;\r
694                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;\r
695                                 break;\r
696                         default;\r
697                 }\r
698                 \r
699                 // fill out variables in text\r
700                 $aVars = array(\r
701                         'siteName' => $CONF['SiteName'],\r
702                         'siteUrl' => $CONF['IndexURL'],\r
703                         'memberName' => $this->getDisplayName(),\r
704                         'activationUrl' => $url,\r
705                         'activationDays' => $CONF['ActivationDays']\r
706                 );\r
707                 \r
708                 $message = Template::fill($message, $aVars);\r
709                 $subject = Template::fill($subject, $aVars);\r
710                 \r
711                 // send mail\r
712                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());\r
713                 \r
714                 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');\r
715                 return;\r
716         }\r
717         \r
718         /**\r
719          * Member::getAdminBlogs()\r
720          * Returns an array of all blogids for which member has admin rights\r
721          * \r
722          * @param       void\r
723          * @return      array   weblog IDs in which this member has admin rights\r
724          * \r
725          */\r
726         public function getAdminBlogs()\r
727         {\r
728                 $blogs = array();\r
729                 \r
730                 if ($this->isAdmin())\r
731                 {\r
732                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
733                 }\r
734                 else\r
735                 {\r
736                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();\r
737                 }\r
738                 \r
739                 $res = DB::getResult($query);\r
740                 if ( $res->rowCount() > 0 )\r
741                 {\r
742                         foreach ( $res as $row )\r
743                         {\r
744                                 array_push($blogs, $row['blogid']);\r
745                         }\r
746                 }\r
747                 return $blogs;\r
748         }\r
749         \r
750         /**\r
751          * Member::getTeamBlogs()\r
752          * Returns an array of all blogids for which member has team rights\r
753          * \r
754          * @param       boolean $incAdmin       whether checking weblog admin rights or not\r
755          * @return      array   weblog IDs in which this member join\r
756          * \r
757          */\r
758         public function getTeamBlogs($incAdmin = 1)\r
759         {\r
760                 $incAdmin = intval($incAdmin);\r
761                 $blogs = array();\r
762                 \r
763                 if ( $this->isAdmin() && $incAdmin )\r
764                 {\r
765                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
766                 }\r
767                 else\r
768                 {\r
769                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();\r
770                 }\r
771                 \r
772                 $res = DB::getResult($query);\r
773                 if ( $res->rowCount() > 0 )\r
774                 {\r
775                         foreach ( $res as $row )\r
776                         {\r
777                                 array_push($blogs, $row['blogid']);\r
778                         }\r
779                 }\r
780                 return $blogs;\r
781         }\r
782         \r
783         /**\r
784          * Member::getNotifyFromMailAddress()\r
785          * \r
786          * Returns an email address from which notification of commenting/karma voting can\r
787          * be sent. A suggestion can be given for when the member is not logged in\r
788          * \r
789          * @param       String  $suggest\r
790          * @return      String  mail address or suggestion\r
791          */\r
792         public function getNotifyFromMailAddress($suggest = "")\r
793         {\r
794                 global $CONF;\r
795                 if ( $this->isLoggedIn() )\r
796                 {\r
797                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";\r
798                 }\r
799                 else if ( NOTIFICATION::address_validation($suggest) )\r
800                 {\r
801                         return $suggest;\r
802                 }\r
803                 return $CONF['AdminEmail'];\r
804         }\r
805         \r
806         /**\r
807          * Member::write()\r
808          * Write data to database\r
809          * \r
810          * @param       void\r
811          * @return      void\r
812          * \r
813          */\r
814         public function write()\r
815         {\r
816                 $query =  'UPDATE '.sql_table('member')\r
817                         . ' SET mname=' . DB::quoteValue($this->displayname) . ', '\r
818                            . 'mrealname='. DB::quoteValue($this->realname) . ', '\r
819                            . 'mpassword='. DB::quoteValue($this->password) . ', '\r
820                            . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '\r
821                            . 'murl=' . DB::quoteValue($this->url) . ', '\r
822                            . 'memail=' . DB::quoteValue($this->email) . ', '\r
823                            . 'madmin=' . intval($this->admin) . ', '\r
824                            . 'mnotes=' . DB::quoteValue($this->notes) . ', '\r
825                            . 'mcanlogin=' . intval($this->canlogin) . ', '\r
826                            . 'mlocale=' . DB::quoteValue($this->locale) . ', '\r
827                            . 'madminskin=' . DB::quoteValue($this->adminskin) . ', '\r
828                            . 'mbkmklt=' . DB::quoteValue($this->bookmarklet) . ', '\r
829                            . 'mautosave=' . intval($this->autosave) . ' '\r
830                         . 'WHERE mnumber=' . intval($this->id);\r
831                 DB::execute($query);\r
832                 return;\r
833         }\r
834         \r
835         public function checkCookieKey($key)\r
836         {\r
837                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );\r
838         }\r
839         \r
840         public function checkPassword($pw)\r
841         {\r
842                 /* for lower compatibility (md5) */\r
843                 if ( strlen($this->password) === 32 )\r
844                 {\r
845                         return (md5($pw) == $this->password);\r
846                 }\r
847                 return ($this->hash($pw) == $this->password);\r
848         }\r
849         \r
850         public function getRealName()\r
851         {\r
852                 return $this->realname;\r
853         }\r
854         \r
855         public function setRealName($name)\r
856         {\r
857                 $this->realname = $name;\r
858         }\r
859         \r
860         public function getEmail()\r
861         {\r
862                 return $this->email;\r
863         }\r
864         \r
865         public function setEmail($email)\r
866         {\r
867                 $this->email = $email;\r
868         }\r
869         \r
870         public function getPassword()\r
871         {\r
872                 return $this->password;\r
873         }\r
874         \r
875         public function setPassword($pwd)\r
876         {\r
877                 $this->password = $this->hash($pwd);\r
878         }\r
879         \r
880         public function getCookieKey()\r
881         {\r
882                 return $this->cookiekey;\r
883         }\r
884         \r
885         /**\r
886          * Member::newCookieKey()\r
887          * Generate new cookiekey, save it, and return it\r
888          * \r
889          * @param       void\r
890          * @return      void\r
891          * \r
892          */\r
893         public function newCookieKey()\r
894         {\r
895                 mt_srand( (double) microtime() * 1000000);\r
896                 $this->cookiekey = $this->hash(uniqid(mt_rand()));\r
897                 $this->write();\r
898                 return $this->cookiekey;\r
899         }\r
900         \r
901         public function setCookieKey($val)\r
902         {\r
903                 $this->cookiekey = $val;\r
904         }\r
905         \r
906         public function getURL()\r
907         {\r
908                 return $this->url;\r
909         }\r
910         \r
911         public function setURL($site)\r
912         {\r
913                 $this->url = $site;\r
914         }\r
915         \r
916         public function setAdminSkin($skin)\r
917         {\r
918                 $this->adminskin = $skin;\r
919         }\r
920 \r
921         public function setBookmarklet($skin)\r
922         {\r
923                 $this->bookmarklet = $skin;\r
924         }\r
925         \r
926         public function getAdminSkin()\r
927         {\r
928                 return $this->adminskin;\r
929         }\r
930 \r
931         public function getBookmarklet()\r
932         {\r
933                 return $this->bookmarklet;\r
934         }\r
935 \r
936         public function getLocale()\r
937         {\r
938                 return $this->locale;\r
939         }\r
940         \r
941         public function setLocale($locale)\r
942         {\r
943                 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)\r
944                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )\r
945                 {\r
946                         $locale = '';\r
947                 }\r
948                 $this->locale = $locale;\r
949                 return;\r
950         }\r
951         \r
952         public function setDisplayName($nick)\r
953         {\r
954                 $this->displayname = $nick;\r
955         }\r
956         \r
957         public function getDisplayName()\r
958         {\r
959                 return $this->displayname;\r
960         }\r
961         \r
962         public function isAdmin()\r
963         {\r
964                 return $this->admin;\r
965         }\r
966         \r
967         public function setAdmin($val)\r
968         {\r
969                 $this->admin = $val;\r
970         }\r
971         \r
972         public function canLogin()\r
973         {\r
974                 return $this->canlogin;\r
975         }\r
976         \r
977         public function setCanLogin($val)\r
978         {\r
979                 $this->canlogin = $val;\r
980         }\r
981         \r
982         public function getNotes()\r
983         {\r
984                 return $this->notes;\r
985         }\r
986         \r
987         public function setNotes($val)\r
988         {\r
989                 $this->notes = $val;\r
990         }\r
991         \r
992         public function getAutosave()\r
993         {\r
994                 return $this->autosave;\r
995         }\r
996         \r
997         public function setAutosave($val)\r
998         {\r
999                 $this->autosave = $val;\r
1000                 return;\r
1001         }\r
1002         \r
1003         /**\r
1004          * Member::getID()\r
1005          * \r
1006          * @param       void\r
1007          * @return      integer id of this member object\r
1008          * \r
1009          */\r
1010         public function getID()\r
1011         {\r
1012                 return $this->id;\r
1013         }\r
1014         \r
1015         /**\r
1016          * Member::exists()\r
1017          * Returns true if there is a member with the given login name\r
1018          * \r
1019          * @static\r
1020          * @param       string  $name   target name\r
1021          * @return      boolean whether target name exists or not\r
1022          */\r
1023         public static function exists($name)\r
1024         {\r
1025                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));\r
1026                 return ( $r->rowCount() != 0 );\r
1027         }\r
1028         \r
1029         /**\r
1030          * Member::existsID()\r
1031          * Returns true if there is a member with the given ID\r
1032          * \r
1033          * @static\r
1034          * @param       integer $id     target id\r
1035          * @return      boolean whether target id exists or not\r
1036          * \r
1037          */\r
1038         public static function existsID($id)\r
1039         {\r
1040                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));\r
1041                 return ( $r->rowCount() != 0 );\r
1042         }\r
1043         \r
1044         /**\r
1045          * Member::isNameProtected()\r
1046          *  Checks if a username is protected.\r
1047          *  If so, it can not be used on anonymous comments\r
1048          * \r
1049          * @param       string  $name   target name\r
1050          * @return      boolean whether the name exists or not\r
1051          * \r
1052          */\r
1053         public function isNameProtected($name)\r
1054         {\r
1055                 // extract name\r
1056                 $name = strip_tags($name);\r
1057                 $name = trim($name);\r
1058                 return self::exists($name);\r
1059         }\r
1060         \r
1061         /**\r
1062          * Member::create()\r
1063          * Adds a new member\r
1064          * \r
1065          * @static\r
1066          * @param       String  $name\r
1067          * @param       String  $realname\r
1068          * @param       String  $password\r
1069          * @param       String  $email\r
1070          * @param       String  $url\r
1071          * @param       String  $admin\r
1072          * @param       String  $canlogin\r
1073          * @param       String  $notes\r
1074          * @return      String  1 if success, others if fail\r
1075          */\r
1076         static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)\r
1077         {\r
1078                 if ( !NOTIFICATION::address_validation($email) )\r
1079                 {\r
1080                         return _ERROR_BADMAILADDRESS;\r
1081                 }\r
1082                 \r
1083                 /* TODO: this method should be in MEMBER class, not globalfunctions */\r
1084                 if ( !isValidDisplayName($name) )\r
1085                 {\r
1086                         return _ERROR_BADNAME;\r
1087                 }\r
1088                 \r
1089                 if ( self::exists($name) )\r
1090                 {\r
1091                         return _ERROR_NICKNAMEINUSE;\r
1092                 }\r
1093                 \r
1094                 if ( !$realname )\r
1095                 {\r
1096                         return _ERROR_REALNAMEMISSING;\r
1097                 }\r
1098                 \r
1099                 /* TODO: check the number of characters */\r
1100                 if ( !$password )\r
1101                 {\r
1102                         return _ERROR_PASSWORDMISSING;\r
1103                 }\r
1104                 \r
1105                 /* \r
1106                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,\r
1107                  *  this cause a malformed URL. Let's fix it.\r
1108                  */\r
1109                 \r
1110                 if ( !preg_match('#^https?://#', $url) )\r
1111                 {\r
1112                         $url = 'http://' . $url;\r
1113                 }\r
1114                 \r
1115                 $name           = DB::quoteValue($name);\r
1116                 $realname       = DB::quoteValue($realname);\r
1117                 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */\r
1118                 $password       = DB::quoteValue(md5($password));\r
1119                 $email          = DB::quoteValue($email);\r
1120                 $url            = DB::quoteValue($url);\r
1121                 $admin          = (integer) $admin;\r
1122                 $canlogin       = (integer) $canlogin;\r
1123                 $notes          = DB::quoteValue($notes);\r
1124                 \r
1125                 $query = "INSERT INTO %s"\r
1126                        . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"\r
1127                        . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";\r
1128                 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);\r
1129                 DB::execute($query);\r
1130                 \r
1131                 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);\r
1132                 \r
1133                 return 1;\r
1134         }\r
1135         \r
1136         /**\r
1137          * Member::getActivationInfo()\r
1138          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)\r
1139          * \r
1140          * @static\r
1141          * @param       string  $key    activation key\r
1142          * @return      mixed   return 0 if failed, else return activation table object\r
1143          * \r
1144          */\r
1145         public static function getActivationInfo($key)\r
1146         {\r
1147                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);\r
1148                 $res = DB::getResult($query);\r
1149                 \r
1150                 if ( !$res || ($res->rowCount() == 0) )\r
1151                 {\r
1152                         return 0;\r
1153                 }\r
1154                 return $res->fetch();\r
1155         }\r
1156         \r
1157         /**\r
1158          * Member::generateActivationEntry()\r
1159          * Creates an account activation key\r
1160          * addresschange -> old email address\r
1161          * \r
1162          * @param       string  $type   one of the following values (determines what to do when activation expires)\r
1163          *                              'register'      (new member registration)\r
1164          *                              'forgot'        (forgotton password)\r
1165          *                              'addresschange' (member address has changed)\r
1166          * @param       string  $extra  extra info (needed when validation link expires)\r
1167          * @return      string  activation key\r
1168          */\r
1169         public function generateActivationEntry($type, $extra = '')\r
1170         {\r
1171                 // clean up old entries\r
1172                 $this->cleanupActivationTable();\r
1173                 \r
1174                 // kill any existing entries for the current member (delete is ok)\r
1175                 // (only one outstanding activation key can be present for a member)\r
1176                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));\r
1177                 \r
1178                 // indicates if the member can log in while the link is active\r
1179                 $canLoginWhileActive = false;\r
1180                 switch ( $type )\r
1181                 {\r
1182                         case 'forgot':\r
1183                                 $canLoginWhileActive = true;\r
1184                                 break;\r
1185                         case 'register':\r
1186                                 break;\r
1187                         case 'addresschange':\r
1188                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );\r
1189                                 break;\r
1190                 }\r
1191                 \r
1192                 $ok = false;\r
1193                 while ( !$ok )\r
1194                 {\r
1195                         // generate a random key\r
1196                         srand((double)microtime()*1000000);\r
1197                         $key = $this->hash(uniqid(rand(), true));\r
1198                         \r
1199                         // attempt to add entry in database\r
1200                         // add in database as non-active\r
1201                         $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';\r
1202                         $query = sprintf($query\r
1203                                 , sql_table('activation')\r
1204                                 , DB::quoteValue($key)\r
1205                                 , DB::formatDateTime()\r
1206                                 , intval($this->getID())\r
1207                                 , DB::quoteValue($type)\r
1208                                 , DB::quoteValue($extra)\r
1209                         );\r
1210                         if ( DB::execute($query) !== FALSE )\r
1211                                 $ok = true;\r
1212                 }\r
1213                 \r
1214                 // mark member as not allowed to log in\r
1215                 if ( !$canLoginWhileActive )\r
1216                 {\r
1217                         $this->setCanLogin(0);\r
1218                         $this->write();\r
1219                 }\r
1220                 \r
1221                 // return the key\r
1222                 return $key;\r
1223         }\r
1224         \r
1225         /**\r
1226          * Member::activate()\r
1227          * Inidicates that an activation link has been clicked and any forms displayed\r
1228          * there have been successfully filled out.\r
1229          * \r
1230          * @param       string  $key    activation key\r
1231          * @return      boolean\r
1232          * \r
1233          */\r
1234         public function activate($key)\r
1235         {\r
1236                 // get activate info\r
1237                 $info = self::getActivationInfo($key);\r
1238                 \r
1239                 // no active key\r
1240                 if ( !$info )\r
1241                 {\r
1242                         return false;\r
1243                 }\r
1244                 \r
1245                 switch ( $info['vtype'] )\r
1246                 {\r
1247                         case 'forgot':\r
1248                                 // nothing to do\r
1249                                 break;\r
1250                         case 'register':\r
1251                                 // set canlogin value\r
1252                                 global $CONF;\r
1253                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));\r
1254                                 break;\r
1255                         case 'addresschange':\r
1256                                 // reset old 'canlogin' value\r
1257                                 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);\r
1258                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));\r
1259                                 break;\r
1260                 }\r
1261                 \r
1262                 // delete from activation table\r
1263                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));\r
1264                 \r
1265                 // success!\r
1266                 return true;\r
1267         }\r
1268         \r
1269         /**\r
1270          * Member::cleanupActivationTable()\r
1271          * Cleans up entries in the activation table. All entries older than 2 days are removed.\r
1272          * (static)\r
1273          * \r
1274          * @param       void\r
1275          * @return      void\r
1276          */\r
1277         public function cleanupActivationTable()\r
1278         {\r
1279                 $actdays = 2;\r
1280                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )\r
1281                 {\r
1282                         $actdays = intval($CONF['ActivationDays']);\r
1283                 }\r
1284                 else\r
1285                 {\r
1286                         $CONF['ActivationDays'] = 2;\r
1287                 }\r
1288                 $boundary = time() - (60 * 60 * 24 * $actdays);\r
1289                 \r
1290                 // 1. walk over all entries, and see if special actions need to be performed\r
1291                 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1292                 $res = DB::getResult($query);\r
1293                 \r
1294                 foreach ( $res as $row )\r
1295                 {\r
1296                         switch ( $row['vtype'] )\r
1297                         {\r
1298                                 case 'register':\r
1299                                         // delete all information about this site member. registration is undone because there was\r
1300                                         // no timely activation\r
1301                                         include_once($DIR_LIBS . 'ADMIN.php');\r
1302                                         Admin::deleteOneMember(intval($row['vmember']));\r
1303                                         break;\r
1304                                 case 'addresschange':\r
1305                                         // revert the e-mail address of the member back to old address\r
1306                                         list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);\r
1307                                         DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));\r
1308                                         break;\r
1309                                 case 'forgot':\r
1310                                         // delete the activation link and ignore. member can request a new password using the\r
1311                                         // forgot password link\r
1312                                         break;\r
1313                         }\r
1314                 }\r
1315                 \r
1316                 // 2. delete activation entries for real\r
1317                 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1318                 DB::execute($query);\r
1319                 return;\r
1320         }\r
1321         \r
1322         /**\r
1323          * Member::$language\r
1324          * \r
1325          * @obsolete\r
1326          * @param       void\r
1327          * @return      void\r
1328          * \r
1329          */\r
1330         public $language = '';\r
1331         /**\r
1332          * Member::getLanguage()\r
1333          * \r
1334          * @obsolete\r
1335          * @param       void\r
1336          * @return      void\r
1337          * \r
1338          */\r
1339         public function getLanguage()\r
1340         {\r
1341                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )\r
1342                 {\r
1343                         $language = '';\r
1344                 }\r
1345                 return $language;\r
1346         }\r
1347 }\r