OSDN Git Service

FIX:変数名の誤記を修正
[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                 $source_blogid = getBlogIDFromItemID($itemid);\r
598                 $dest_blogid = getBlogIDFromCatID($newcat);\r
599                 \r
600                 // not a team member of destination blog -> NOK\r
601                 if ( !$this->teamRights($dest_blogid) )\r
602                 {\r
603                         return 0;\r
604                 }\r
605                 \r
606                 // if member is author of item -> OK\r
607                 if ( $item['authorid'] == $this->getID() )\r
608                 {\r
609                         return 1;\r
610                 }\r
611                 \r
612                 // if member has admin rights on both blogs: OK\r
613                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )\r
614                 {\r
615                         return 1;\r
616                 }\r
617                 \r
618                 // all other cases: NOK\r
619                 return 0;\r
620         }\r
621         \r
622         /**\r
623          * Member::setCookies()\r
624          * Sets the cookies for the member\r
625          * \r
626          * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire\r
627          *                              at the end of the session in this case.\r
628          * @return      void\r
629          * \r
630          */\r
631         public function setCookies($shared = 0)\r
632         {\r
633                 global $CONF;\r
634                 \r
635                 if ( $CONF['SessionCookie'] || $shared )\r
636                 {\r
637                         $lifetime = 0;\r
638                 }\r
639                 else\r
640                 {\r
641                         $lifetime = time()+2592000;\r
642                 }\r
643                 \r
644                 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
645                 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
646                 \r
647                 // make sure cookies on shared pcs don't get renewed\r
648                 if ( $shared )\r
649                 {\r
650                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);\r
651                 }\r
652                 return;\r
653         }\r
654         \r
655         /**\r
656          * Member::sendActivationLink()\r
657          * Send activation mail\r
658          * \r
659          * @param       string  $type   activation type\r
660          * @param       string  $extra  extra info\r
661          * @return      void\r
662          */\r
663         public function sendActivationLink($type, $extra='')\r
664         {\r
665                 global $CONF;\r
666                 \r
667                 if ( !isset($CONF['ActivationDays']) )\r
668                 {\r
669                         $CONF['ActivationDays'] = 2;\r
670                 }\r
671                 \r
672                 // generate key and URL\r
673                 $key = $this->generateActivationEntry($type, $extra);\r
674                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;\r
675                 \r
676                 // choose text to use in mail\r
677                 switch ( $type )\r
678                 {\r
679                         case 'register':\r
680                                 $message = _ACTIVATE_REGISTER_MAIL;\r
681                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;\r
682                                 break;\r
683                         case 'forgot':\r
684                                 $message = _ACTIVATE_FORGOT_MAIL;\r
685                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;\r
686                                 break;\r
687                         case 'addresschange':\r
688                                 $message = _ACTIVATE_CHANGE_MAIL;\r
689                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;\r
690                                 break;\r
691                         default;\r
692                 }\r
693                 \r
694                 // fill out variables in text\r
695                 $aVars = array(\r
696                         'siteName' => $CONF['SiteName'],\r
697                         'siteUrl' => $CONF['IndexURL'],\r
698                         'memberName' => $this->getDisplayName(),\r
699                         'activationUrl' => $url,\r
700                         'activationDays' => $CONF['ActivationDays']\r
701                 );\r
702                 \r
703                 $message = Template::fill($message, $aVars);\r
704                 $subject = Template::fill($subject, $aVars);\r
705                 \r
706                 // send mail\r
707                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());\r
708                 \r
709                 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');\r
710                 return;\r
711         }\r
712         \r
713         /**\r
714          * Member::getAdminBlogs()\r
715          * Returns an array of all blogids for which member has admin rights\r
716          * \r
717          * @param       void\r
718          * @return      array   weblog IDs in which this member has admin rights\r
719          * \r
720          */\r
721         public function getAdminBlogs()\r
722         {\r
723                 $blogs = array();\r
724                 \r
725                 if ($this->isAdmin())\r
726                 {\r
727                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
728                 }\r
729                 else\r
730                 {\r
731                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();\r
732                 }\r
733                 \r
734                 $res = DB::getResult($query);\r
735                 if ( $res->rowCount() > 0 )\r
736                 {\r
737                         foreach ( $res as $row )\r
738                         {\r
739                                 array_push($blogs, $row['blogid']);\r
740                         }\r
741                 }\r
742                 return $blogs;\r
743         }\r
744         \r
745         /**\r
746          * Member::getTeamBlogs()\r
747          * Returns an array of all blogids for which member has team rights\r
748          * \r
749          * @param       boolean $incAdmin       whether checking weblog admin rights or not\r
750          * @return      array   weblog IDs in which this member join\r
751          * \r
752          */\r
753         public function getTeamBlogs($incAdmin = 1)\r
754         {\r
755                 $incAdmin = intval($incAdmin);\r
756                 $blogs = array();\r
757                 \r
758                 if ( $this->isAdmin() && $incAdmin )\r
759                 {\r
760                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
761                 }\r
762                 else\r
763                 {\r
764                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();\r
765                 }\r
766                 \r
767                 $res = DB::getResult($query);\r
768                 if ( $res->rowCount() > 0 )\r
769                 {\r
770                         foreach ( $res as $row )\r
771                         {\r
772                                 array_push($blogs, $row['blogid']);\r
773                         }\r
774                 }\r
775                 return $blogs;\r
776         }\r
777         \r
778         /**\r
779          * Member::getNotifyFromMailAddress()\r
780          * \r
781          * Returns an email address from which notification of commenting/karma voting can\r
782          * be sent. A suggestion can be given for when the member is not logged in\r
783          * \r
784          * @param       String  $suggest\r
785          * @return      String  mail address or suggestion\r
786          */\r
787         public function getNotifyFromMailAddress($suggest = "")\r
788         {\r
789                 global $CONF;\r
790                 if ( $this->isLoggedIn() )\r
791                 {\r
792                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";\r
793                 }\r
794                 else if ( NOTIFICATION::address_validation($suggest) )\r
795                 {\r
796                         return $suggest;\r
797                 }\r
798                 return $CONF['AdminEmail'];\r
799         }\r
800         \r
801         /**\r
802          * Member::write()\r
803          * Write data to database\r
804          * \r
805          * @param       void\r
806          * @return      void\r
807          * \r
808          */\r
809         public function write()\r
810         {\r
811                 $query =  'UPDATE '.sql_table('member')\r
812                         . ' SET mname=' . DB::quoteValue($this->displayname) . ', '\r
813                            . 'mrealname='. DB::quoteValue($this->realname) . ', '\r
814                            . 'mpassword='. DB::quoteValue($this->password) . ', '\r
815                            . 'mcookiekey='. DB::quoteValue($this->cookiekey) . ', '\r
816                            . 'murl=' . DB::quoteValue($this->url) . ', '\r
817                            . 'memail=' . DB::quoteValue($this->email) . ', '\r
818                            . 'madmin=' . intval($this->admin) . ', '\r
819                            . 'mnotes=' . DB::quoteValue($this->notes) . ', '\r
820                            . 'mcanlogin=' . intval($this->canlogin) . ', '\r
821                            . 'mlocale=' . DB::quoteValue($this->locale) . ', '\r
822                            . 'mautosave=' . intval($this->autosave) . ' '\r
823                         . 'WHERE mnumber=' . intval($this->id);\r
824                 DB::execute($query);\r
825                 return;\r
826         }\r
827         \r
828         public function checkCookieKey($key)\r
829         {\r
830                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );\r
831         }\r
832         \r
833         public function checkPassword($pw)\r
834         {\r
835                 /* for lower compatibility (md5) */\r
836                 if ( strlen($this->password) === 32 )\r
837                 {\r
838                         return (md5($pw) == $this->password);\r
839                 }\r
840                 return ($this->hash($pw) == $this->password);\r
841         }\r
842         \r
843         public function getRealName()\r
844         {\r
845                 return $this->realname;\r
846         }\r
847         \r
848         public function setRealName($name)\r
849         {\r
850                 $this->realname = $name;\r
851         }\r
852         \r
853         public function getEmail()\r
854         {\r
855                 return $this->email;\r
856         }\r
857         \r
858         public function setEmail($email)\r
859         {\r
860                 $this->email = $email;\r
861         }\r
862         \r
863         public function getPassword()\r
864         {\r
865                 return $this->password;\r
866         }\r
867         \r
868         public function setPassword($pwd)\r
869         {\r
870                 $this->password = $this->hash($pwd);\r
871         }\r
872         \r
873         public function getCookieKey()\r
874         {\r
875                 return $this->cookiekey;\r
876         }\r
877         \r
878         /**\r
879          * Member::newCookieKey()\r
880          * Generate new cookiekey, save it, and return it\r
881          * \r
882          * @param       void\r
883          * @return      void\r
884          * \r
885          */\r
886         public function newCookieKey()\r
887         {\r
888                 mt_srand( (double) microtime() * 1000000);\r
889                 $this->cookiekey = $this->hash(uniqid(mt_rand()));\r
890                 $this->write();\r
891                 return $this->cookiekey;\r
892         }\r
893         \r
894         public function setCookieKey($val)\r
895         {\r
896                 $this->cookiekey = $val;\r
897         }\r
898         \r
899         public function getURL()\r
900         {\r
901                 return $this->url;\r
902         }\r
903         \r
904         public function setURL($site)\r
905         {\r
906                 $this->url = $site;\r
907         }\r
908         \r
909         public function getLocale()\r
910         {\r
911                 return $this->locale;\r
912         }\r
913         \r
914         public function setLocale($locale)\r
915         {\r
916                 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)\r
917                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )\r
918                 {\r
919                         $locale = '';\r
920                 }\r
921                 $this->locale = $locale;\r
922                 return;\r
923         }\r
924         \r
925         public function setDisplayName($nick)\r
926         {\r
927                 $this->displayname = $nick;\r
928         }\r
929         \r
930         public function getDisplayName()\r
931         {\r
932                 return $this->displayname;\r
933         }\r
934         \r
935         public function isAdmin()\r
936         {\r
937                 return $this->admin;\r
938         }\r
939         \r
940         public function setAdmin($val)\r
941         {\r
942                 $this->admin = $val;\r
943         }\r
944         \r
945         public function canLogin()\r
946         {\r
947                 return $this->canlogin;\r
948         }\r
949         \r
950         public function setCanLogin($val)\r
951         {\r
952                 $this->canlogin = $val;\r
953         }\r
954         \r
955         public function getNotes()\r
956         {\r
957                 return $this->notes;\r
958         }\r
959         \r
960         public function setNotes($val)\r
961         {\r
962                 $this->notes = $val;\r
963         }\r
964         \r
965         public function getAutosave()\r
966         {\r
967                 return $this->autosave;\r
968         }\r
969         \r
970         public function setAutosave($val)\r
971         {\r
972                 $this->autosave = $val;\r
973                 return;\r
974         }\r
975         \r
976         /**\r
977          * Member::getID()\r
978          * \r
979          * @param       void\r
980          * @return      integer id of this member object\r
981          * \r
982          */\r
983         public function getID()\r
984         {\r
985                 return $this->id;\r
986         }\r
987         \r
988         /**\r
989          * Member::exists()\r
990          * Returns true if there is a member with the given login name\r
991          * \r
992          * @static\r
993          * @param       string  $name   target name\r
994          * @return      boolean whether target name exists or not\r
995          */\r
996         public static function exists($name)\r
997         {\r
998                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mname=' . DB::quoteValue($name));\r
999                 return ( $r->rowCount() != 0 );\r
1000         }\r
1001         \r
1002         /**\r
1003          * Member::existsID()\r
1004          * Returns true if there is a member with the given ID\r
1005          * \r
1006          * @static\r
1007          * @param       integer $id     target id\r
1008          * @return      boolean whether target id exists or not\r
1009          * \r
1010          */\r
1011         public static function existsID($id)\r
1012         {\r
1013                 $r = DB::getResult('SELECT * FROM ' . sql_table('member') . ' WHERE mnumber=' . intval($id));\r
1014                 return ( $r->rowCount() != 0 );\r
1015         }\r
1016         \r
1017         /**\r
1018          * Member::isNameProtected()\r
1019          *  Checks if a username is protected.\r
1020          *  If so, it can not be used on anonymous comments\r
1021          * \r
1022          * @param       string  $name   target name\r
1023          * @return      boolean whether the name exists or not\r
1024          * \r
1025          */\r
1026         public function isNameProtected($name)\r
1027         {\r
1028                 // extract name\r
1029                 $name = strip_tags($name);\r
1030                 $name = trim($name);\r
1031                 return self::exists($name);\r
1032         }\r
1033         \r
1034         /**\r
1035          * Member::create()\r
1036          * Adds a new member\r
1037          * \r
1038          * @static\r
1039          * @param       String  $name\r
1040          * @param       String  $realname\r
1041          * @param       String  $password\r
1042          * @param       String  $email\r
1043          * @param       String  $url\r
1044          * @param       String  $admin\r
1045          * @param       String  $canlogin\r
1046          * @param       String  $notes\r
1047          * @return      String  1 if success, others if fail\r
1048          */\r
1049         static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)\r
1050         {\r
1051                 if ( !NOTIFICATION::address_validation($email) )\r
1052                 {\r
1053                         return _ERROR_BADMAILADDRESS;\r
1054                 }\r
1055                 \r
1056                 /* TODO: this method should be in MEMBER class, not globalfunctions */\r
1057                 if ( !isValidDisplayName($name) )\r
1058                 {\r
1059                         return _ERROR_BADNAME;\r
1060                 }\r
1061                 \r
1062                 if ( self::exists($name) )\r
1063                 {\r
1064                         return _ERROR_NICKNAMEINUSE;\r
1065                 }\r
1066                 \r
1067                 if ( !$realname )\r
1068                 {\r
1069                         return _ERROR_REALNAMEMISSING;\r
1070                 }\r
1071                 \r
1072                 /* TODO: check the number of characters */\r
1073                 if ( !$password )\r
1074                 {\r
1075                         return _ERROR_PASSWORDMISSING;\r
1076                 }\r
1077                 \r
1078                 /* \r
1079                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,\r
1080                  *  this cause a malformed URL. Let's fix it.\r
1081                  */\r
1082                 \r
1083                 if ( !preg_match('#^https?://#', $url) )\r
1084                 {\r
1085                         $url = 'http://' . $url;\r
1086                 }\r
1087                 \r
1088                 $name           = DB::quoteValue($name);\r
1089                 $realname       = DB::quoteValue($realname);\r
1090                 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */\r
1091                 $password       = DB::quoteValue(md5($password));\r
1092                 $email          = DB::quoteValue($email);\r
1093                 $url            = DB::quoteValue($url);\r
1094                 $admin          = (integer) $admin;\r
1095                 $canlogin       = (integer) $canlogin;\r
1096                 $notes          = DB::quoteValue($notes);\r
1097                 \r
1098                 $query = "INSERT INTO %s"\r
1099                        . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"\r
1100                        . " VALUES (%s, %s, %s, %s, %s, %d, %d, %s)";\r
1101                 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);\r
1102                 DB::execute($query);\r
1103                 \r
1104                 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);\r
1105                 \r
1106                 return 1;\r
1107         }\r
1108         \r
1109         /**\r
1110          * Member::getActivationInfo()\r
1111          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)\r
1112          * \r
1113          * @static\r
1114          * @param       string  $key    activation key\r
1115          * @return      mixed   return 0 if failed, else return activation table object\r
1116          * \r
1117          */\r
1118         public static function getActivationInfo($key)\r
1119         {\r
1120                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key);\r
1121                 $res = DB::getResult($query);\r
1122                 \r
1123                 if ( !$res || ($res->rowCount() == 0) )\r
1124                 {\r
1125                         return 0;\r
1126                 }\r
1127                 return $res->fetch();\r
1128         }\r
1129         \r
1130         /**\r
1131          * Member::generateActivationEntry()\r
1132          * Creates an account activation key\r
1133          * addresschange -> old email address\r
1134          * \r
1135          * @param       string  $type   one of the following values (determines what to do when activation expires)\r
1136          *                              'register'      (new member registration)\r
1137          *                              'forgot'        (forgotton password)\r
1138          *                              'addresschange' (member address has changed)\r
1139          * @param       string  $extra  extra info (needed when validation link expires)\r
1140          * @return      string  activation key\r
1141          */\r
1142         public function generateActivationEntry($type, $extra = '')\r
1143         {\r
1144                 // clean up old entries\r
1145                 $this->cleanupActivationTable();\r
1146                 \r
1147                 // kill any existing entries for the current member (delete is ok)\r
1148                 // (only one outstanding activation key can be present for a member)\r
1149                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));\r
1150                 \r
1151                 // indicates if the member can log in while the link is active\r
1152                 $canLoginWhileActive = false;\r
1153                 switch ( $type )\r
1154                 {\r
1155                         case 'forgot':\r
1156                                 $canLoginWhileActive = true;\r
1157                                 break;\r
1158                         case 'register':\r
1159                                 break;\r
1160                         case 'addresschange':\r
1161                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );\r
1162                                 break;\r
1163                 }\r
1164                 \r
1165                 $ok = false;\r
1166                 while ( !$ok )\r
1167                 {\r
1168                         // generate a random key\r
1169                         srand((double)microtime()*1000000);\r
1170                         $key = $this->hash(uniqid(rand(), true));\r
1171                         \r
1172                         // attempt to add entry in database\r
1173                         // add in database as non-active\r
1174                         $query = 'INSERT INTO %s (vkey, vtime, vmember, vtype, vextra) VALUES (%s, %s, %d, %s, %s)';\r
1175                         $query = sprintf($query\r
1176                                 , sql_table('activation')\r
1177                                 , DB::quoteValue($key)\r
1178                                 , DB::formatDateTime()\r
1179                                 , intval($this->getID())\r
1180                                 , DB::quoteValue($type)\r
1181                                 , DB::quoteValue($extra)\r
1182                         );\r
1183                         if ( DB::execute($query) !== FALSE )\r
1184                                 $ok = true;\r
1185                 }\r
1186                 \r
1187                 // mark member as not allowed to log in\r
1188                 if ( !$canLoginWhileActive )\r
1189                 {\r
1190                         $this->setCanLogin(0);\r
1191                         $this->write();\r
1192                 }\r
1193                 \r
1194                 // return the key\r
1195                 return $key;\r
1196         }\r
1197         \r
1198         /**\r
1199          * Member::activate()\r
1200          * Inidicates that an activation link has been clicked and any forms displayed\r
1201          * there have been successfully filled out.\r
1202          * \r
1203          * @param       string  $key    activation key\r
1204          * @return      boolean\r
1205          * \r
1206          */\r
1207         public function activate($key)\r
1208         {\r
1209                 // get activate info\r
1210                 $info = self::getActivationInfo($key);\r
1211                 \r
1212                 // no active key\r
1213                 if ( !$info )\r
1214                 {\r
1215                         return false;\r
1216                 }\r
1217                 \r
1218                 switch ( $info['vtype'] )\r
1219                 {\r
1220                         case 'forgot':\r
1221                                 // nothing to do\r
1222                                 break;\r
1223                         case 'register':\r
1224                                 // set canlogin value\r
1225                                 global $CONF;\r
1226                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info['vmember']));\r
1227                                 break;\r
1228                         case 'addresschange':\r
1229                                 // reset old 'canlogin' value\r
1230                                 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info['vextra']);\r
1231                                 DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info['vmember']));\r
1232                                 break;\r
1233                 }\r
1234                 \r
1235                 // delete from activation table\r
1236                 DB::execute('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=' . DB::quoteValue($key));\r
1237                 \r
1238                 // success!\r
1239                 return true;\r
1240         }\r
1241         \r
1242         /**\r
1243          * Member::cleanupActivationTable()\r
1244          * Cleans up entries in the activation table. All entries older than 2 days are removed.\r
1245          * (static)\r
1246          * \r
1247          * @param       void\r
1248          * @return      void\r
1249          */\r
1250         public function cleanupActivationTable()\r
1251         {\r
1252                 $actdays = 2;\r
1253                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )\r
1254                 {\r
1255                         $actdays = intval($CONF['ActivationDays']);\r
1256                 }\r
1257                 else\r
1258                 {\r
1259                         $CONF['ActivationDays'] = 2;\r
1260                 }\r
1261                 $boundary = time() - (60 * 60 * 24 * $actdays);\r
1262                 \r
1263                 // 1. walk over all entries, and see if special actions need to be performed\r
1264                 $query = sprintf('SELECT * FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1265                 $res = DB::getResult($query);\r
1266                 \r
1267                 foreach ( $res as $row )\r
1268                 {\r
1269                         switch ( $row['vtype'] )\r
1270                         {\r
1271                                 case 'register':\r
1272                                         // delete all information about this site member. registration is undone because there was\r
1273                                         // no timely activation\r
1274                                         include_once($DIR_LIBS . 'ADMIN.php');\r
1275                                         Admin::deleteOneMember(intval($row['vmember']));\r
1276                                         break;\r
1277                                 case 'addresschange':\r
1278                                         // revert the e-mail address of the member back to old address\r
1279                                         list($oldEmail, $oldCanLogin) = preg_split('#/#', $row['vextra']);\r
1280                                         DB::execute('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=' . DB::quoteValue($oldEmail). ' WHERE mnumber=' . intval($row['vmember']));\r
1281                                         break;\r
1282                                 case 'forgot':\r
1283                                         // delete the activation link and ignore. member can request a new password using the\r
1284                                         // forgot password link\r
1285                                         break;\r
1286                         }\r
1287                 }\r
1288                 \r
1289                 // 2. delete activation entries for real\r
1290                 $query = sprintf('DELETE FROM %s WHERE vtime < %s', sql_table('activation'), DB::formatDateTime($boundary));\r
1291                 DB::execute($query);\r
1292                 return;\r
1293         }\r
1294         \r
1295         /**\r
1296          * Member::$language\r
1297          * \r
1298          * @obsolete\r
1299          * @param       void\r
1300          * @return      void\r
1301          * \r
1302          */\r
1303         public $language = '';\r
1304         /**\r
1305          * Member::getLanguage()\r
1306          * \r
1307          * @obsolete\r
1308          * @param       void\r
1309          * @return      void\r
1310          * \r
1311          */\r
1312         public function getLanguage()\r
1313         {\r
1314                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )\r
1315                 {\r
1316                         $language = '';\r
1317                 }\r
1318                 return $language;\r
1319         }\r
1320 }\r