OSDN Git Service

CHANGE: getBlogIDFromItemID()を使わずにManager::getItem()を使うよう変更
[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         private $locale = '';\r
41         \r
42         /**\r
43          * Member::__construct()\r
44          * Constructor for a member object\r
45          * \r
46          * @param       Void\r
47          * @return      Void\r
48          * \r
49          */\r
50         public function __construct()\r
51         {\r
52                 return;\r
53         }\r
54         \r
55         /**\r
56          * Member::createFromName()\r
57          * Create a member object for a given displayname\r
58          * \r
59          * @static\r
60          * @param       String  $displayname    login name\r
61          * @return      Object  member object\r
62          * \r
63          */\r
64         public static function &createFromName($displayname)\r
65         {\r
66                 $mem = new Member();\r
67                 $mem->readFromName($displayname);\r
68                 return $mem;\r
69         }\r
70         \r
71         /**\r
72          * Member::createFromID()\r
73          * Create a member object for a given ID\r
74          * \r
75          * @static\r
76          * @param       Integer $id     id for member\r
77          */\r
78         public static function &createFromID($id)\r
79         {\r
80                 $mem = new Member();\r
81                 $mem->readFromID($id);\r
82                 return $mem;\r
83         }\r
84         \r
85         /**\r
86          * Member::readFromName()\r
87          * Read member table in database\r
88          * \r
89          * @param       String  $displayname    login name\r
90          * @return      Object  SQL resource\r
91          * \r
92          */\r
93         public function readFromName($displayname)\r
94         {\r
95                 return $this->read('mname='.DB::quoteValue($displayname));\r
96         }\r
97         \r
98         /**\r
99          * Member::readFromID()\r
100          * Read member table in database\r
101          * \r
102          * @param       Integer $id     id for member\r
103          * @return      Object  SQL resource\r
104          * \r
105          */\r
106         public function readFromID($id)\r
107         {\r
108                 return $this->read("mnumber=" . intval($id));\r
109         }\r
110         \r
111         /**\r
112          * Member::hash()\r
113          * hash the target string\r
114          * \r
115          * @param       String  $string target string\r
116          * @return      Void    hashed string\r
117          */\r
118         public function hash($string)\r
119         {\r
120                 switch ( $this->algorism )\r
121                 {\r
122                         case 'md5':\r
123                         default:\r
124                                 $string = md5($string);\r
125                 }\r
126                 return $string;\r
127         }\r
128         \r
129         /**\r
130          * Member::set_cookie_salt()\r
131          * \r
132          * @param       integer $key    secureCookieKey value\r
133          * @return      void\r
134          * \r
135          */\r
136         private function set_cookie_salt($key = 0)\r
137         {\r
138                 if ( !$key )\r
139                 {\r
140                         $key = 24;\r
141                 }\r
142                 \r
143                 switch( $key )\r
144                 {\r
145                         case 8:\r
146                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
147                                 break;\r
148                         case 16:\r
149                                 $this->cookie_salt = preg_replace('/\.[0-9]+\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
150                                 break;\r
151                         case 24:\r
152                                 $this->cookie_salt = preg_replace('/\.[0-9]+$/', '', serverVar('REMOTE_ADDR'));\r
153                                 break;\r
154                         case 32:\r
155                                 $this->cookie_salt = serverVar('REMOTE_ADDR');\r
156                                 break;\r
157                         default:\r
158                                 $this->cookie_salt = 'none';\r
159                 }\r
160                 return;\r
161         }\r
162         \r
163         /**\r
164          * Member::login()\r
165          * Tries to login as a given user.\r
166          * Returns true when succeeded, returns false when failed\r
167          * 3.40 adds CustomLogin event\r
168          * \r
169          * @param       String  $login  login name for member\r
170          * @param       String  $password       password for member\r
171          * @param       Integer $shared whether the user agent is shared or not\r
172          * \r
173          */\r
174         public function login($login, $password, $shared=1)\r
175         {\r
176                 global $CONF, $errormessage, $manager;\r
177                 \r
178                 /* TODO: validation for $login, $password, $shared */\r
179                 if ( $login == '' || $password == '' )\r
180                 {\r
181                         return 0;\r
182                 }\r
183                 /* limiting the length of password to avoid hash collision */\r
184                 $password=i18n::substr($password, 0, 40);\r
185                 \r
186                 /* \r
187                  * generate cookie salt from secure cookie key settings\r
188                 * (either 'none', 0, 8, 16, 24, or 32)\r
189                 */\r
190                 if ( !$this->cookie_salt )\r
191                 {\r
192                         $salt = 0;\r
193                         if ( array_key_exists('secureCookieKey', $CONF) )\r
194                         {\r
195                                 $salt = $CONF['secureCookieKey'];\r
196                         }\r
197                         $this->set_cookie_salt($salt);\r
198                 }\r
199                 \r
200                 $success = 0;\r
201                 $allowlocal = 1;\r
202                 $manager->notify('CustomLogin', array('login' => &$login, 'password'=>&$password, 'success'=>&$success, 'allowlocal'=>&$allowlocal));\r
203                 \r
204                 $this->loggedin = 0;\r
205                 if ( $success )\r
206                 {\r
207                         $this->loggedin = ( $this->readFromName($login) );\r
208                 }\r
209                 elseif ( $allowlocal )\r
210                 {\r
211                         $this->loggedin = ( $this->readFromName($login) && $this->checkPassword($password) );\r
212                 }\r
213                 \r
214                 /* login failed */\r
215                 if ( !$this->loggedin )\r
216                 {\r
217                         $trimlogin = trim($login);\r
218                         if ( empty($trimlogin) )\r
219                         {\r
220                                 $errormessage = "Please enter a username.";\r
221                         }\r
222                         else\r
223                         {\r
224                                 $errormessage = 'Login failed for ' . $login;\r
225                         }\r
226                         $manager->notify('LoginFailed', array('username' => $login) );\r
227                         ActionLog::add(INFO, $errormessage);\r
228                 }\r
229                 /* login success */\r
230                 else\r
231                 {\r
232                         /* For lower compatibility */\r
233                         if ( strlen($this->password) === 32 )\r
234                         {\r
235                                 $this->password = $this->hash($password);\r
236                         }\r
237                         \r
238                         $this->newCookieKey();\r
239                         $this->setCookies($shared);\r
240                         \r
241                         if ( $this->cookie_salt !== 'none' )\r
242                         {\r
243                                 /* secure cookie key */\r
244                                 $this->setCookieKey($this->hash($this->getCookieKey() . $this->cookie_salt));\r
245                                 $this->write();\r
246                         }\r
247                         \r
248                         $errormessage = '';\r
249                         $manager->notify('LoginSuccess', array('member' => &$member, 'username' => $login) );\r
250                         ActionLog::add(INFO, "Login successful for $login (sharedpc=$shared)");\r
251                 }\r
252                 \r
253                 return $this->loggedin;\r
254         }\r
255         \r
256         /**\r
257          * Member::cookielogin()\r
258          * Login using cookie key\r
259          * \r
260          * @param       String  $login  not used\r
261          * @param       String  $cookiekey      not used\r
262          * @return      Boolean login or not\r
263          */\r
264         public function cookielogin($login='', $cookiekey='')\r
265         {\r
266                 global $CONF, $manager;\r
267                 \r
268                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
269                 {\r
270                         /* Cookie Authentication */\r
271                         $ck = cookieVar("{$CONF['CookiePrefix']}loginkey");\r
272                                 \r
273                         /* TODO: validation for each cookie values */\r
274                                 \r
275                         /* limiting the length of password to avoid hash collision */\r
276                         $ck = i18n::substr($ck,0,32);\r
277                                 \r
278                         /* \r
279                          * generate cookie salt from secure cookie key settings\r
280                         * (either 'none', 0, 8, 16, 24, or 32)\r
281                         */\r
282                         if ( !$this->cookie_salt )\r
283                         {\r
284                                 $salt = 0;\r
285                                 if ( array_key_exists('secureCookieKey', $CONF) )\r
286                                 {\r
287                                         $salt = $CONF['secureCookieKey'];\r
288                                 }\r
289                                 $this->set_cookie_salt($salt);\r
290                         }\r
291                         \r
292                         if ( $this->cookie_salt !== 'none' )\r
293                         {\r
294                                 $ck = $this->hash($ck . $this->cookie_salt);\r
295                         }\r
296                         $this->loggedin = ( $this->readFromName(cookieVar("{$CONF['CookiePrefix']}user")) && $this->checkCookieKey($ck) );\r
297                         unset($ck);\r
298                                 \r
299                         /* renew cookies when not on a shared computer */\r
300                         if ( $this->loggedin && (cookieVar($CONF['CookiePrefix'] . 'sharedpc') != 1) )\r
301                         {\r
302                                 $this->setCookieKey(cookieVar("{$CONF['CookiePrefix']}loginkey"));\r
303                                 $this->setCookies();\r
304                         }\r
305                 }\r
306                 return $this->loggedin;\r
307         }\r
308         \r
309         /**\r
310          * Member::logout()\r
311          * logout and expire cookie\r
312          * \r
313          * @param       Void\r
314          * @return      Void\r
315          */\r
316         public function logout()\r
317         {\r
318                 global $CONF, $manager;\r
319                 \r
320                 if ( !headers_sent() && cookieVar("{$CONF['CookiePrefix']}user") )\r
321                 {\r
322                         /* remove cookies on logout */\r
323                         setcookie("{$CONF['CookiePrefix']}user", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
324                         setcookie("{$CONF['CookiePrefix']}loginkey", '', (time() - 2592000), $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
325                         $manager->notify('Logout', array('username' => cookieVar("{$CONF['CookiePrefix']}user") ) );\r
326                 }\r
327                 \r
328                 $this->loggedin = 0;\r
329                 return;\r
330         }\r
331         \r
332         /**\r
333          * Member::isLoggedIn()\r
334          * return member is loggedin or not\r
335          * \r
336          * @param       Void\r
337          * @return      Void\r
338          */\r
339         public function isLoggedIn()\r
340         {\r
341                 return $this->loggedin;\r
342         }\r
343         \r
344         /**\r
345          * MEMBER:read()\r
346          * Read member information from the database\r
347          * \r
348          * @param       String  $where  where statement\r
349          * @return      Resource        SQL resource\r
350          * \r
351          */\r
352         public function read($where)\r
353         {\r
354                 // read info\r
355                 $query =  'SELECT * FROM '.sql_table('member') . ' WHERE ' . $where;\r
356                 \r
357                 $row = DB::getRow($query);\r
358                 \r
359                 $this->setRealName($row['mrealname']);\r
360                 $this->setEmail($row['memail']);\r
361                 $this->password = $row['mpassword'];\r
362                 $this->setCookieKey($row['mcookiekey']);\r
363                 $this->setURL($row['murl']);\r
364                 $this->setDisplayName($row['mname']);\r
365                 $this->setAdmin($row['madmin']);\r
366                 $this->id = $row['mnumber'];\r
367                 $this->setCanLogin($row['mcanlogin']);\r
368                 $this->setNotes($row['mnotes']);\r
369                 $this->setLocale($row['mlocale']);\r
370                 $this->setAutosave($row['mautosave']);\r
371                 \r
372                 return $row ? TRUE : FALSE;\r
373         }\r
374         \r
375         /**\r
376          * Member::isBlogAdmin()\r
377          * Returns true if member is an admin for the given blog\r
378          * (returns false if not a team member)\r
379          * \r
380          * @param       Integer $blogid weblog id\r
381          * @return      Integer weblog admin or not\r
382          * \r
383          */\r
384         public function isBlogAdmin($blogid)\r
385         {\r
386                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'\r
387                 . ' tblog=' . intval($blogid)\r
388                 . ' and tmember='. $this->getID();\r
389                 $res = DB::getValue($query);\r
390                 if ( $res )\r
391                         return ($res == 1);\r
392                 else\r
393                         return 0;\r
394         }\r
395         \r
396         /**\r
397          * Member::blogAdminRights()\r
398          * \r
399          * @param       integer $blogid ID of target weblog\r
400          * @return      boolean whether to have admin rights to the weblog or not\r
401          * \r
402          */\r
403         public function blogAdminRights($blogid)\r
404         {\r
405                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));\r
406         }\r
407         \r
408         /**\r
409          * Member::teamRights()\r
410          * \r
411          * @param       integer $blogid ID of target weblog\r
412          * @return      boolean whether to have admin right to the weblog or not\r
413          * \r
414          */\r
415         public function teamRights($blogid)\r
416         {\r
417                 return ($this->isAdmin() || $this->isTeamMember($blogid));\r
418         }\r
419         \r
420         /**\r
421          * Member::isTeamMember()\r
422          * Returns true if this member is a team member of the given blog\r
423          * \r
424          * @param       integer $blogid ID of target weblog\r
425          * @return      boolean whether to join the weblog or not\r
426          * \r
427          */\r
428         public function isTeamMember($blogid)\r
429         {\r
430                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'\r
431                            . ' tblog=' . intval($blogid)\r
432                            . ' and tmember='. $this->getID();\r
433                 $res = DB::getResult($query);\r
434                 return ($res->rowCount() != 0);\r
435         }\r
436         \r
437         /**\r
438          * Member::canAddItem()\r
439          * \r
440          * @param       integer $catid  ID of target category\r
441          * @return      boolean whether to be able to add items to the category or not\r
442          * \r
443          */\r
444         public function canAddItem($catid)\r
445         {\r
446                 global $manager;\r
447                 \r
448                 // if this is a 'newcat' style newcat\r
449                 // no blog admin of destination blog -> NOK\r
450                 // blog admin of destination blog -> OK\r
451                 if ( i18n::strpos($catid,'newcat') === 0 )\r
452                 {\r
453                         // get blogid\r
454                         list($blogid) = sscanf($catid,"newcat-%d");\r
455                         return $this->blogAdminRights($blogid);\r
456                 }\r
457                 \r
458                 // category does not exist -> NOK\r
459                 if ( !$manager->existsCategory($catid) )\r
460                 {\r
461                         return 0;\r
462                 }\r
463                 \r
464                 $blogid = getBlogIDFromCatID($catid);\r
465                 \r
466                 // no team rights for blog -> NOK\r
467                 if (!$this->teamRights($blogid))\r
468                 {\r
469                         return 0;\r
470                 }\r
471                 \r
472                 // all other cases: OK\r
473                 return 1;\r
474         }\r
475         \r
476         /**\r
477          * Member::canAlterComment()\r
478          * Returns true if this member can edit/delete a commentitem. This can be in the\r
479          * following cases:\r
480          *        - member is a super-admin\r
481          *   - member is the author of the comment\r
482          *   - member is admin of the blog associated with the comment\r
483          *   - member is author of the item associated with the comment\r
484          * \r
485          * @param       integer $commentid      ID of target comment\r
486          * @return      boolean delete/edit the comment or not\r
487          * \r
488          */\r
489         public function canAlterComment($commentid)\r
490         {\r
491                 if ( $this->isAdmin() )\r
492                 {\r
493                         return 1;\r
494                 }\r
495                 \r
496                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'\r
497                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')\r
498                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);\r
499                 $res = DB::getRow($query);\r
500                 \r
501                 return ($res['cauthor'] == $this->getID()) or $this->isBlogAdmin($res['blogid']) or ($res['iauthor'] == $this->getID());\r
502         }\r
503         \r
504         /**\r
505          * Member::canAlterItem()\r
506          * Returns true if this member can edit/delete an item. This is true in the following\r
507          * cases: - member is a super-admin\r
508          *             - member is the author of the item\r
509          *        - member is admin of the the associated blog\r
510          * \r
511          * @param       integer $itemid ID of target item\r
512          * @return      boolean delete/edit the item or not\r
513          * \r
514          */\r
515         public function canAlterItem($itemid)\r
516         {\r
517                 if ($this->isAdmin()) return 1;\r
518                 \r
519                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);\r
520                 $res = DB::getRow($query);\r
521                 return ($res['iauthor'] == $this->getID()) or $this->isBlogAdmin($res['iblog']);\r
522         }\r
523         \r
524         /**\r
525          * Member::canBeDeleted()\r
526          * Return true if member can be deleted. This means that there are no items posted by the member left\r
527          * \r
528          * @param       void\r
529          * @return      boolean whether there is no items or exists\r
530          * \r
531          */\r
532         public function canBeDeleted()\r
533         {\r
534                 $res = DB::getResult('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());\r
535                 return ( $res->rowCount() == 0 );\r
536         }\r
537         \r
538         /**\r
539          * Member::canUpdateItem()\r
540          * returns true if this member can move/update an item to a given category,\r
541          * false if not (see comments fot the tests that are executed)\r
542          * \r
543          * @param       integer $itemid\r
544          * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)\r
545          * @return      boolean whether being able to update the item or not\r
546          * \r
547          */\r
548         public function canUpdateItem($itemid, $newcat)\r
549         {\r
550                 global $manager;\r
551                 \r
552                 // item does not exists -> NOK\r
553                 if ( !$manager->existsItem($itemid, 1, 1) )\r
554                 {\r
555                         return 0;\r
556                 }\r
557                 \r
558                 // cannot alter item -> NOK\r
559                 if (!$this->canAlterItem($itemid))\r
560                 {\r
561                         return 0;\r
562                 }\r
563                 \r
564                 // if this is a 'newcat' style newcat\r
565                 // no blog admin of destination blog -> NOK\r
566                 // blog admin of destination blog -> OK\r
567                 if ( i18n::strpos($newcat, 'newcat') === 0 )\r
568                 {\r
569                         // get blogid\r
570                         list($blogid) = sscanf($newcat, 'newcat-%d');\r
571                         return $this->blogAdminRights($blogid);\r
572                 }\r
573                 \r
574                 // category does not exist -> NOK\r
575                 if (!$manager->existsCategory($newcat))\r
576                 {\r
577                         return 0;\r
578                 }\r
579                 \r
580                 // get item\r
581                 $item =& $manager->getItem($itemid, 1, 1);\r
582                 \r
583                 // old catid = new catid -> OK\r
584                 if ($item['catid'] == $newcat)\r
585                 {\r
586                         return 1;\r
587                 }\r
588                 \r
589                 // not a valid category -> NOK\r
590                 $validCat = DB::getValue('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));\r
591                 if ( !$validCat )\r
592                 {\r
593                         return 0;\r
594                 }\r
595                 \r
596                 // get destination blog\r
597                 $item =& $manager->getItem($itemid, 1, 1);\r
598                 $source_blogid = $item['blogid'];\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