OSDN Git Service

MERGE: リビジョン1821。Member::create()のバグ修正
[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='".sql_real_escape_string($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                 $res = sql_query($query);\r
358                 $obj = sql_fetch_object($res);\r
359                 \r
360                 $this->setRealName($obj->mrealname);\r
361                 $this->setEmail($obj->memail);\r
362                 $this->password = $obj->mpassword;\r
363                 $this->setCookieKey($obj->mcookiekey);\r
364                 $this->setURL($obj->murl);\r
365                 $this->setDisplayName($obj->mname);\r
366                 $this->setAdmin($obj->madmin);\r
367                 $this->id = $obj->mnumber;\r
368                 $this->setCanLogin($obj->mcanlogin);\r
369                 $this->setNotes($obj->mnotes);\r
370                 $this->setLocale($obj->mlocale);\r
371                 $this->setAutosave($obj->mautosave);\r
372                 \r
373                 return sql_num_rows($res);\r
374         }\r
375         \r
376         /**\r
377          * Member::isBlogAdmin()\r
378          * Returns true if member is an admin for the given blog\r
379          * (returns false if not a team member)\r
380          * \r
381          * @param       Integer $blogid weblog id\r
382          * @return      Integer weblog admin or not\r
383          * \r
384          */\r
385         public function isBlogAdmin($blogid)\r
386         {\r
387                 $query = 'SELECT tadmin FROM '.sql_table('team').' WHERE'\r
388                 . ' tblog=' . intval($blogid)\r
389                 . ' and tmember='. $this->getID();\r
390                 $res = sql_query($query);\r
391                 if ( sql_num_rows($res) == 0 )\r
392                         return 0;\r
393                 else\r
394                         return ( sql_result($res,0,0) == 1 );\r
395         }\r
396         \r
397         /**\r
398          * Member::blogAdminRights()\r
399          * \r
400          * @param       integer $blogid ID of target weblog\r
401          * @return      boolean whether to have admin rights to the weblog or not\r
402          * \r
403          */\r
404         public function blogAdminRights($blogid)\r
405         {\r
406                 return ($this->isAdmin() || $this->isBlogAdmin($blogid));\r
407         }\r
408         \r
409         /**\r
410          * Member::teamRights()\r
411          * \r
412          * @param       integer $blogid ID of target weblog\r
413          * @return      boolean whether to have admin right to the weblog or not\r
414          * \r
415          */\r
416         public function teamRights($blogid)\r
417         {\r
418                 return ($this->isAdmin() || $this->isTeamMember($blogid));\r
419         }\r
420         \r
421         /**\r
422          * Member::isTeamMember()\r
423          * Returns true if this member is a team member of the given blog\r
424          * \r
425          * @param       integer $blogid ID of target weblog\r
426          * @return      boolean whether to join the weblog or not\r
427          * \r
428          */\r
429         public function isTeamMember($blogid)\r
430         {\r
431                 $query = 'SELECT * FROM '.sql_table('team').' WHERE'\r
432                            . ' tblog=' . intval($blogid)\r
433                            . ' and tmember='. $this->getID();\r
434                 $res = sql_query($query);\r
435                 return (sql_num_rows($res) != 0);\r
436         }\r
437         \r
438         /**\r
439          * Member::canAddItem()\r
440          * \r
441          * @param       integer $catid  ID of target category\r
442          * @return      boolean whether to be able to add items to the category or not\r
443          * \r
444          */\r
445         public function canAddItem($catid)\r
446         {\r
447                 global $manager;\r
448                 \r
449                 // if this is a 'newcat' style newcat\r
450                 // no blog admin of destination blog -> NOK\r
451                 // blog admin of destination blog -> OK\r
452                 if ( i18n::strpos($catid,'newcat') === 0 )\r
453                 {\r
454                         // get blogid\r
455                         list($blogid) = sscanf($catid,"newcat-%d");\r
456                         return $this->blogAdminRights($blogid);\r
457                 }\r
458                 \r
459                 // category does not exist -> NOK\r
460                 if ( !$manager->existsCategory($catid) )\r
461                 {\r
462                         return 0;\r
463                 }\r
464                 \r
465                 $blogid = getBlogIDFromCatID($catid);\r
466                 \r
467                 // no team rights for blog -> NOK\r
468                 if (!$this->teamRights($blogid))\r
469                 {\r
470                         return 0;\r
471                 }\r
472                 \r
473                 // all other cases: OK\r
474                 return 1;\r
475         }\r
476         \r
477         /**\r
478          * Member::canAlterComment()\r
479          * Returns true if this member can edit/delete a commentitem. This can be in the\r
480          * following cases:\r
481          *        - member is a super-admin\r
482          *   - member is the author of the comment\r
483          *   - member is admin of the blog associated with the comment\r
484          *   - member is author of the item associated with the comment\r
485          * \r
486          * @param       integer $commentid      ID of target comment\r
487          * @return      boolean delete/edit the comment or not\r
488          * \r
489          */\r
490         public function canAlterComment($commentid)\r
491         {\r
492                 if ( $this->isAdmin() )\r
493                 {\r
494                         return 1;\r
495                 }\r
496                 \r
497                 $query =  'SELECT citem as itemid, iblog as blogid, cmember as cauthor, iauthor'\r
498                            . ' FROM '.sql_table('comment') .', '.sql_table('item').', '.sql_table('blog')\r
499                            . ' WHERE citem=inumber and iblog=bnumber and cnumber=' . intval($commentid);\r
500                 $res = sql_query($query);\r
501                 $obj = sql_fetch_object($res);\r
502                 \r
503                 return ($obj->cauthor == $this->getID()) or $this->isBlogAdmin($obj->blogid) or ($obj->iauthor == $this->getID());\r
504         }\r
505         \r
506         /**\r
507          * Member::canAlterItem()\r
508          * Returns true if this member can edit/delete an item. This is true in the following\r
509          * cases: - member is a super-admin\r
510          *             - member is the author of the item\r
511          *        - member is admin of the the associated blog\r
512          * \r
513          * @param       integer $itemid ID of target item\r
514          * @return      boolean delete/edit the item or not\r
515          * \r
516          */\r
517         public function canAlterItem($itemid)\r
518         {\r
519                 if ($this->isAdmin()) return 1;\r
520                 \r
521                 $query =  'SELECT iblog, iauthor FROM '.sql_table('item').' WHERE inumber=' . intval($itemid);\r
522                 $res = sql_query($query);\r
523                 $obj = sql_fetch_object($res);\r
524                 return ($obj->iauthor == $this->getID()) or $this->isBlogAdmin($obj->iblog);\r
525         }\r
526         \r
527         /**\r
528          * Member::canBeDeleted()\r
529          * Return true if member can be deleted. This means that there are no items posted by the member left\r
530          * \r
531          * @param       void\r
532          * @return      boolean whether there is no items or exists\r
533          * \r
534          */\r
535         public function canBeDeleted()\r
536         {\r
537                 $res = sql_query('SELECT * FROM '.sql_table('item').' WHERE iauthor=' . $this->getID());\r
538                 return ( sql_num_rows($res) == 0 );\r
539         }\r
540         \r
541         /**\r
542          * Member::canUpdateItem()\r
543          * returns true if this member can move/update an item to a given category,\r
544          * false if not (see comments fot the tests that are executed)\r
545          * \r
546          * @param       integer $itemid\r
547          * @param       string  $newcat (can also be of form 'newcat-x' with x=blogid)\r
548          * @return      boolean whether being able to update the item or not\r
549          * \r
550          */\r
551         public function canUpdateItem($itemid, $newcat)\r
552         {\r
553                 global $manager;\r
554                 \r
555                 // item does not exists -> NOK\r
556                 if ( !$manager->existsItem($itemid,1,1) )\r
557                 {\r
558                         return 0;\r
559                 }\r
560                 \r
561                 // cannot alter item -> NOK\r
562                 if (!$this->canAlterItem($itemid))\r
563                 {\r
564                         return 0;\r
565                 }\r
566                 \r
567                 // if this is a 'newcat' style newcat\r
568                 // no blog admin of destination blog -> NOK\r
569                 // blog admin of destination blog -> OK\r
570                 if ( i18n::strpos($newcat, 'newcat') === 0 )\r
571                 {\r
572                         // get blogid\r
573                         list($blogid) = sscanf($newcat,'newcat-%d');\r
574                         return $this->blogAdminRights($blogid);\r
575                 }\r
576                 \r
577                 // category does not exist -> NOK\r
578                 if (!$manager->existsCategory($newcat))\r
579                 {\r
580                         return 0;\r
581                 }\r
582                 \r
583                 // get item\r
584                 $item =& $manager->getItem($itemid,1,1);\r
585                 \r
586                 // old catid = new catid -> OK\r
587                 if ($item['catid'] == $newcat)\r
588                 {\r
589                         return 1;\r
590                 }\r
591                 \r
592                 // not a valid category -> NOK\r
593                 $validCat = quickQuery('SELECT COUNT(*) AS result FROM '.sql_table('category').' WHERE catid='.intval($newcat));\r
594                 if ( !$validCat )\r
595                 {\r
596                         return 0;\r
597                 }\r
598                 \r
599                 // get destination blog\r
600                 $source_blogid = getBlogIDFromItemID($itemid);\r
601                 $dest_blogid = getBlogIDFromCatID($newcat);\r
602                 \r
603                 // not a team member of destination blog -> NOK\r
604                 if ( !$this->teamRights($dest_blogid) )\r
605                 {\r
606                         return 0;\r
607                 }\r
608                 \r
609                 // if member is author of item -> OK\r
610                 if ( $item['authorid'] == $this->getID() )\r
611                 {\r
612                         return 1;\r
613                 }\r
614                 \r
615                 // if member has admin rights on both blogs: OK\r
616                 if ( ($this->blogAdminRights($dest_blogid)) && ($this->blogAdminRights($source_blogid)) )\r
617                 {\r
618                         return 1;\r
619                 }\r
620                 \r
621                 // all other cases: NOK\r
622                 return 0;\r
623         }\r
624         \r
625         /**\r
626          * Member::setCookies()\r
627          * Sets the cookies for the member\r
628          * \r
629          * @param boolean       $shared set this to 1 when using a shared computer. Cookies will expire\r
630          *                              at the end of the session in this case.\r
631          * @return      void\r
632          * \r
633          */\r
634         public function setCookies($shared = 0)\r
635         {\r
636                 global $CONF;\r
637                 \r
638                 if ( $CONF['SessionCookie'] || $shared )\r
639                 {\r
640                         $lifetime = 0;\r
641                 }\r
642                 else\r
643                 {\r
644                         $lifetime = time()+2592000;\r
645                 }\r
646                 \r
647                 setcookie($CONF['CookiePrefix'] . 'user', $this->getDisplayName(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
648                 setcookie($CONF['CookiePrefix'] . 'loginkey', $this->getCookieKey(), $lifetime, $CONF['CookiePath'], $CONF['CookieDomain'], $CONF['CookieSecure']);\r
649                 \r
650                 // make sure cookies on shared pcs don't get renewed\r
651                 if ( $shared )\r
652                 {\r
653                         setcookie($CONF['CookiePrefix'] .'sharedpc', '1',$lifetime,$CONF['CookiePath'],$CONF['CookieDomain'],$CONF['CookieSecure']);\r
654                 }\r
655                 return;\r
656         }\r
657         \r
658         /**\r
659          * Member::sendActivationLink()\r
660          * Send activation mail\r
661          * \r
662          * @param       string  $type   activation type\r
663          * @param       string  $extra  extra info\r
664          * @return      void\r
665          */\r
666         public function sendActivationLink($type, $extra='')\r
667         {\r
668                 global $CONF;\r
669                 \r
670                 if ( !isset($CONF['ActivationDays']) )\r
671                 {\r
672                         $CONF['ActivationDays'] = 2;\r
673                 }\r
674                 \r
675                 // generate key and URL\r
676                 $key = $this->generateActivationEntry($type, $extra);\r
677                 $url = $CONF['AdminURL'] . 'index.php?action=activate&key=' . $key;\r
678                 \r
679                 // choose text to use in mail\r
680                 switch ( $type )\r
681                 {\r
682                         case 'register':\r
683                                 $message = _ACTIVATE_REGISTER_MAIL;\r
684                                 $subject = _ACTIVATE_REGISTER_MAILTITLE;\r
685                                 break;\r
686                         case 'forgot':\r
687                                 $message = _ACTIVATE_FORGOT_MAIL;\r
688                                 $subject = _ACTIVATE_FORGOT_MAILTITLE;\r
689                                 break;\r
690                         case 'addresschange':\r
691                                 $message = _ACTIVATE_CHANGE_MAIL;\r
692                                 $subject = _ACTIVATE_CHANGE_MAILTITLE;\r
693                                 break;\r
694                         default;\r
695                 }\r
696                 \r
697                 // fill out variables in text\r
698                 $aVars = array(\r
699                         'siteName' => $CONF['SiteName'],\r
700                         'siteUrl' => $CONF['IndexURL'],\r
701                         'memberName' => $this->getDisplayName(),\r
702                         'activationUrl' => $url,\r
703                         'activationDays' => $CONF['ActivationDays']\r
704                 );\r
705                 \r
706                 $message = Template::fill($message, $aVars);\r
707                 $subject = Template::fill($subject, $aVars);\r
708                 \r
709                 // send mail\r
710                 NOTIFICATION::mail($this->getEmail(), $subject ,$message, $CONF['AdminEmail'], i18n::get_current_charset());\r
711                 \r
712                 ActionLog::add(INFO, _ACTIONLOG_ACTIVATIONLINK . ' (' . $this->getDisplayName() . ' / type: ' . $type . ')');\r
713                 return;\r
714         }\r
715         \r
716         /**\r
717          * Member::getAdminBlogs()\r
718          * Returns an array of all blogids for which member has admin rights\r
719          * \r
720          * @param       void\r
721          * @return      array   weblog IDs in which this member has admin rights\r
722          * \r
723          */\r
724         public function getAdminBlogs()\r
725         {\r
726                 $blogs = array();\r
727                 \r
728                 if ($this->isAdmin())\r
729                 {\r
730                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
731                 }\r
732                 else\r
733                 {\r
734                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tadmin=1 and tmember=' . $this->getID();\r
735                 }\r
736                 \r
737                 $res = sql_query($query);\r
738                 if ( sql_num_rows($res) > 0 )\r
739                 {\r
740                         while ( $obj = sql_fetch_object($res) )\r
741                         {\r
742                                 array_push($blogs, $obj->blogid);\r
743                         }\r
744                 }\r
745                 return $blogs;\r
746         }\r
747         \r
748         /**\r
749          * Member::getTeamBlogs()\r
750          * Returns an array of all blogids for which member has team rights\r
751          * \r
752          * @param       boolean $incAdmin       whether checking weblog admin rights or not\r
753          * @return      array   weblog IDs in which this member join\r
754          * \r
755          */\r
756         public function getTeamBlogs($incAdmin = 1)\r
757         {\r
758                 $incAdmin = intval($incAdmin);\r
759                 $blogs = array();\r
760                 \r
761                 if ( $this->isAdmin() && $incAdmin )\r
762                 {\r
763                         $query = 'SELECT bnumber as blogid from '.sql_table('blog');\r
764                 }\r
765                 else\r
766                 {\r
767                         $query = 'SELECT tblog as blogid from '.sql_table('team').' where tmember=' . $this->getID();\r
768                 }\r
769                 \r
770                 $res = sql_query($query);\r
771                 if ( sql_num_rows($res) > 0 )\r
772                 {\r
773                         while ( $obj = sql_fetch_object($res) )\r
774                         {\r
775                                 array_push($blogs, $obj->blogid);\r
776                         }\r
777                 }\r
778                 return $blogs;\r
779         }\r
780         \r
781         /**\r
782          * Member::getNotifyFromMailAddress()\r
783          * \r
784          * Returns an email address from which notification of commenting/karma voting can\r
785          * be sent. A suggestion can be given for when the member is not logged in\r
786          * \r
787          * @param       String  $suggest\r
788          * @return      String  mail address or suggestion\r
789          */\r
790         public function getNotifyFromMailAddress($suggest = "")\r
791         {\r
792                 global $CONF;\r
793                 if ( $this->isLoggedIn() )\r
794                 {\r
795                         return $this->getDisplayName() . " <" . $this->getEmail() . ">";\r
796                 }\r
797                 else if ( NOTIFICATION::address_validation($suggest) )\r
798                 {\r
799                         return $suggest;\r
800                 }\r
801                 return $CONF['AdminEmail'];\r
802         }\r
803         \r
804         /**\r
805          * Member::write()\r
806          * Write data to database\r
807          * \r
808          * @param       void\r
809          * @return      void\r
810          * \r
811          */\r
812         public function write()\r
813         {\r
814                 $query =  'UPDATE '.sql_table('member')\r
815                         . " SET mname='" . sql_real_escape_string($this->displayname) . "', "\r
816                            . "mrealname='". sql_real_escape_string($this->realname) . "', "\r
817                            . "mpassword='". sql_real_escape_string($this->password) . "', "\r
818                            . "mcookiekey='". sql_real_escape_string($this->cookiekey) . "', "\r
819                            . "murl='" . sql_real_escape_string($this->url) . "', "\r
820                            . "memail='" . sql_real_escape_string($this->email) . "', "\r
821                            . "madmin=" . intval($this->admin) . ", "\r
822                            . "mnotes='" . sql_real_escape_string($this->notes) . "', "\r
823                            . "mcanlogin=" . intval($this->canlogin) . ", "\r
824                            . "mlocale='" . sql_real_escape_string($this->locale) . "', "\r
825                            . "mautosave=" . intval($this->autosave) . " "\r
826                         . "WHERE mnumber=" . intval($this->id);\r
827                 sql_query($query);\r
828                 return;\r
829         }\r
830         \r
831         public function checkCookieKey($key)\r
832         {\r
833                 return ( ($key != '') && ( $key == $this->getCookieKey() ) );\r
834         }\r
835         \r
836         public function checkPassword($pw)\r
837         {\r
838                 /* for lower compatibility (md5) */\r
839                 if ( strlen($this->password) === 32 )\r
840                 {\r
841                         return (md5($pw) == $this->password);\r
842                 }\r
843                 return ($this->hash($pw) == $this->password);\r
844         }\r
845         \r
846         public function getRealName()\r
847         {\r
848                 return $this->realname;\r
849         }\r
850         \r
851         public function setRealName($name)\r
852         {\r
853                 $this->realname = $name;\r
854         }\r
855         \r
856         public function getEmail()\r
857         {\r
858                 return $this->email;\r
859         }\r
860         \r
861         public function setEmail($email)\r
862         {\r
863                 $this->email = $email;\r
864         }\r
865         \r
866         public function getPassword()\r
867         {\r
868                 return $this->password;\r
869         }\r
870         \r
871         public function setPassword($pwd)\r
872         {\r
873                 $this->password = $this->hash($pwd);\r
874         }\r
875         \r
876         public function getCookieKey()\r
877         {\r
878                 return $this->cookiekey;\r
879         }\r
880         \r
881         /**\r
882          * Member::newCookieKey()\r
883          * Generate new cookiekey, save it, and return it\r
884          * \r
885          * @param       void\r
886          * @return      void\r
887          * \r
888          */\r
889         public function newCookieKey()\r
890         {\r
891                 mt_srand( (double) microtime() * 1000000);\r
892                 $this->cookiekey = $this->hash(uniqid(mt_rand()));\r
893                 $this->write();\r
894                 return $this->cookiekey;\r
895         }\r
896         \r
897         public function setCookieKey($val)\r
898         {\r
899                 $this->cookiekey = $val;\r
900         }\r
901         \r
902         public function getURL()\r
903         {\r
904                 return $this->url;\r
905         }\r
906         \r
907         public function setURL($site)\r
908         {\r
909                 $this->url = $site;\r
910         }\r
911         \r
912         public function getLocale()\r
913         {\r
914                 return $this->locale;\r
915         }\r
916         \r
917         public function setLocale($locale)\r
918         {\r
919                 if ( !preg_match('#^(.+)_(.+)_(.+)$#', $locale)\r
920                  && ($locale = i18n::convert_old_language_file_name_to_locale($locale)) === FALSE )\r
921                 {\r
922                         $locale = '';\r
923                 }\r
924                 $this->locale = $locale;\r
925                 return;\r
926         }\r
927         \r
928         public function setDisplayName($nick)\r
929         {\r
930                 $this->displayname = $nick;\r
931         }\r
932         \r
933         public function getDisplayName()\r
934         {\r
935                 return $this->displayname;\r
936         }\r
937         \r
938         public function isAdmin()\r
939         {\r
940                 return $this->admin;\r
941         }\r
942         \r
943         public function setAdmin($val)\r
944         {\r
945                 $this->admin = $val;\r
946         }\r
947         \r
948         public function canLogin()\r
949         {\r
950                 return $this->canlogin;\r
951         }\r
952         \r
953         public function setCanLogin($val)\r
954         {\r
955                 $this->canlogin = $val;\r
956         }\r
957         \r
958         public function getNotes()\r
959         {\r
960                 return $this->notes;\r
961         }\r
962         \r
963         public function setNotes($val)\r
964         {\r
965                 $this->notes = $val;\r
966         }\r
967         \r
968         public function getAutosave()\r
969         {\r
970                 return $this->autosave;\r
971         }\r
972         \r
973         public function setAutosave($val)\r
974         {\r
975                 $this->autosave = $val;\r
976                 return;\r
977         }\r
978         \r
979         /**\r
980          * Member::getID()\r
981          * \r
982          * @param       void\r
983          * @return      integer id of this member object\r
984          * \r
985          */\r
986         public function getID()\r
987         {\r
988                 return $this->id;\r
989         }\r
990         \r
991         /**\r
992          * Member::exists()\r
993          * Returns true if there is a member with the given login name\r
994          * \r
995          * @static\r
996          * @param       string  $name   target name\r
997          * @return      boolean whether target name exists or not\r
998          */\r
999         public static function exists($name)\r
1000         {\r
1001                 $r = sql_query('select * FROM '.sql_table('member')." WHERE mname='".sql_real_escape_string($name)."'");\r
1002                 return ( sql_num_rows($r) != 0 );\r
1003         }\r
1004         \r
1005         /**\r
1006          * Member::existsID()\r
1007          * Returns true if there is a member with the given ID\r
1008          * \r
1009          * @static\r
1010          * @param       integer $id     target id\r
1011          * @return      boolean whether target id exists or not\r
1012          * \r
1013          */\r
1014         public static function existsID($id)\r
1015         {\r
1016                 $r = sql_query('select * FROM '.sql_table('member')." WHERE mnumber='".intval($id)."'");\r
1017                 return (sql_num_rows($r) != 0);\r
1018         }\r
1019         \r
1020         /**\r
1021          * Member::isNameProtected()\r
1022          *  Checks if a username is protected.\r
1023          *  If so, it can not be used on anonymous comments\r
1024          * \r
1025          * @param       string  $name   target name\r
1026          * @return      boolean whether the name exists or not\r
1027          * \r
1028          */\r
1029         public function isNameProtected($name)\r
1030         {\r
1031                 // extract name\r
1032                 $name = strip_tags($name);\r
1033                 $name = trim($name);\r
1034                 return self::exists($name);\r
1035         }\r
1036         \r
1037         /**\r
1038          * Member::create()\r
1039          * Adds a new member\r
1040          * \r
1041          * @static\r
1042          * @param       String  $name\r
1043          * @param       String  $realname\r
1044          * @param       String  $password\r
1045          * @param       String  $email\r
1046          * @param       String  $url\r
1047          * @param       String  $admin\r
1048          * @param       String  $canlogin\r
1049          * @param       String  $notes\r
1050          * @return      String  1 if success, others if fail\r
1051          */\r
1052         static public function create($name, $realname, $password, $email, $url, $admin, $canlogin, $notes)\r
1053         {\r
1054                 if ( !NOTIFICATION::address_validation($email) )\r
1055                 {\r
1056                         return _ERROR_BADMAILADDRESS;\r
1057                 }\r
1058                 \r
1059                 /* TODO: this method should be in MEMBER class, not globalfunctions */\r
1060                 if ( !isValidDisplayName($name) )\r
1061                 {\r
1062                         return _ERROR_BADNAME;\r
1063                 }\r
1064                 \r
1065                 if ( self::exists($name) )\r
1066                 {\r
1067                         return _ERROR_NICKNAMEINUSE;\r
1068                 }\r
1069                 \r
1070                 if ( !$realname )\r
1071                 {\r
1072                         return _ERROR_REALNAMEMISSING;\r
1073                 }\r
1074                 \r
1075                 /* TODO: check the number of characters */\r
1076                 if ( !$password )\r
1077                 {\r
1078                         return _ERROR_PASSWORDMISSING;\r
1079                 }\r
1080                 \r
1081                 /* \r
1082                  *  begin if: sometimes user didn't prefix the URL with http:// or https://,\r
1083                  *  this cause a malformed URL. Let's fix it.\r
1084                  */\r
1085                 \r
1086                 if ( !preg_match('#^https?://#', $url) )\r
1087                 {\r
1088                         $url = 'http://' . $url;\r
1089                 }\r
1090                 \r
1091                 $name           = sql_real_escape_string($name);\r
1092                 $realname       = sql_real_escape_string($realname);\r
1093                 /* NOTE: hashed password is automatically updated if the length is 32 bytes when logging in */\r
1094                 $password       = sql_real_escape_string(md5($password));\r
1095                 $email          = sql_real_escape_string($email);\r
1096                 $url            = sql_real_escape_string($url);\r
1097                 $admin          = (integer) $admin;\r
1098                 $canlogin       = (integer) $canlogin;\r
1099                 $notes          = sql_real_escape_string($notes);\r
1100                 \r
1101                 $query = "INSERT INTO %s"\r
1102                        . " (MNAME,MREALNAME,MPASSWORD,MEMAIL,MURL, MADMIN, MCANLOGIN, MNOTES)"\r
1103                        . " VALUES ('%s','%s','%s','%s','%s',%d, %d, '%s')";\r
1104                 $query = sprintf($query, sql_table('member'), $name, $realname, $password, $email, $url, $admin, $canlogin, $notes);\r
1105                 sql_query($query);\r
1106                 \r
1107                 ActionLog::add(INFO, _ACTIONLOG_NEWMEMBER . ' ' . $name);\r
1108                 \r
1109                 return 1;\r
1110         }\r
1111         \r
1112         /**\r
1113          * Member::getActivationInfo()\r
1114          * Returns activation info for a certain key (an object with properties vkey, vmember, ...)\r
1115          * \r
1116          * @static\r
1117          * @param       string  $key    activation key\r
1118          * @return      mixed   return 0 if failed, else return activation table object\r
1119          * \r
1120          */\r
1121         public static function getActivationInfo($key)\r
1122         {\r
1123                 $query = 'SELECT * FROM ' . sql_table('activation') . ' WHERE vkey=\'' . sql_real_escape_string($key). '\'';\r
1124                 $res = sql_query($query);\r
1125                 \r
1126                 if ( !$res || (sql_num_rows($res) == 0) )\r
1127                 {\r
1128                         return 0;\r
1129                 }\r
1130                 return sql_fetch_object($res);\r
1131         }\r
1132         \r
1133         /**\r
1134          * Member::generateActivationEntry()\r
1135          * Creates an account activation key\r
1136          * addresschange -> old email address\r
1137          * \r
1138          * @param       string  $type   one of the following values (determines what to do when activation expires)\r
1139          *                              'register'      (new member registration)\r
1140          *                              'forgot'        (forgotton password)\r
1141          *                              'addresschange' (member address has changed)\r
1142          * @param       string  $extra  extra info (needed when validation link expires)\r
1143          * @return      string  activation key\r
1144          */\r
1145         public function generateActivationEntry($type, $extra = '')\r
1146         {\r
1147                 // clean up old entries\r
1148                 $this->cleanupActivationTable();\r
1149                 \r
1150                 // kill any existing entries for the current member (delete is ok)\r
1151                 // (only one outstanding activation key can be present for a member)\r
1152                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vmember=' . intval($this->getID()));\r
1153                 \r
1154                 // indicates if the member can log in while the link is active\r
1155                 $canLoginWhileActive = false;\r
1156                 switch ( $type )\r
1157                 {\r
1158                         case 'forgot':\r
1159                                 $canLoginWhileActive = true;\r
1160                                 break;\r
1161                         case 'register':\r
1162                                 break;\r
1163                         case 'addresschange':\r
1164                                 $extra = $extra . '/' . ( $this->canLogin() ? '1' : '0' );\r
1165                                 break;\r
1166                 }\r
1167                 \r
1168                 $ok = false;\r
1169                 while ( !$ok )\r
1170                 {\r
1171                         // generate a random key\r
1172                         srand((double)microtime()*1000000);\r
1173                         $key = $this->hash(uniqid(rand(), true));\r
1174                         \r
1175                         // attempt to add entry in database\r
1176                         // add in database as non-active\r
1177                         $query = 'INSERT INTO ' . sql_table('activation'). ' (vkey, vtime, vmember, vtype, vextra) ';\r
1178                         $query .= 'VALUES (\'' . sql_real_escape_string($key). '\', \'' . date('Y-m-d H:i:s',time()) . '\', \'' . intval($this->getID()). '\', \'' . sql_real_escape_string($type). '\', \'' . sql_real_escape_string($extra). '\')';\r
1179                         if ( sql_query($query) )\r
1180                                 $ok = true;\r
1181                 }\r
1182                 \r
1183                 // mark member as not allowed to log in\r
1184                 if ( !$canLoginWhileActive )\r
1185                 {\r
1186                         $this->setCanLogin(0);\r
1187                         $this->write();\r
1188                 }\r
1189                 \r
1190                 // return the key\r
1191                 return $key;\r
1192         }\r
1193         \r
1194         /**\r
1195          * Member::activate()\r
1196          * Inidicates that an activation link has been clicked and any forms displayed\r
1197          * there have been successfully filled out.\r
1198          * \r
1199          * @param       string  $key    activation key\r
1200          * @return      boolean\r
1201          * \r
1202          */\r
1203         public function activate($key)\r
1204         {\r
1205                 // get activate info\r
1206                 $info = self::getActivationInfo($key);\r
1207                 \r
1208                 // no active key\r
1209                 if ( !$info )\r
1210                 {\r
1211                         return false;\r
1212                 }\r
1213                 \r
1214                 switch ( $info->vtype )\r
1215                 {\r
1216                         case 'forgot':\r
1217                                 // nothing to do\r
1218                                 break;\r
1219                         case 'register':\r
1220                                 // set canlogin value\r
1221                                 global $CONF;\r
1222                                 sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($CONF['NewMemberCanLogon']). ' WHERE mnumber=' . intval($info->vmember));\r
1223                                 break;\r
1224                         case 'addresschange':\r
1225                                 // reset old 'canlogin' value\r
1226                                 list($oldEmail, $oldCanLogin) = preg_split('#/#', $info->vextra);\r
1227                                 sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ' WHERE mnumber=' . intval($info->vmember));\r
1228                                 break;\r
1229                 }\r
1230                 \r
1231                 // delete from activation table\r
1232                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vkey=\'' . sql_real_escape_string($key) . '\'');\r
1233                 \r
1234                 // success!\r
1235                 return true;\r
1236         }\r
1237         \r
1238         /**\r
1239          * Member::cleanupActivationTable()\r
1240          * Cleans up entries in the activation table. All entries older than 2 days are removed.\r
1241          * (static)\r
1242          * \r
1243          * @param       void\r
1244          * @return      void\r
1245          */\r
1246         public function cleanupActivationTable()\r
1247         {\r
1248                 $actdays = 2;\r
1249                 if ( isset($CONF['ActivationDays']) && intval($CONF['ActivationDays']) > 0 )\r
1250                 {\r
1251                         $actdays = intval($CONF['ActivationDays']);\r
1252                 }\r
1253                 else\r
1254                 {\r
1255                         $CONF['ActivationDays'] = 2;\r
1256                 }\r
1257                 $boundary = time() - (60 * 60 * 24 * $actdays);\r
1258                 \r
1259                 // 1. walk over all entries, and see if special actions need to be performed\r
1260                 $res = sql_query('SELECT * FROM ' . sql_table('activation') . ' WHERE vtime < \'' . date('Y-m-d H:i:s',$boundary) . '\'');\r
1261                 \r
1262                 while ( $o = sql_fetch_object($res) )\r
1263                 {\r
1264                         switch ( $o->vtype )\r
1265                         {\r
1266                                 case 'register':\r
1267                                         // delete all information about this site member. registration is undone because there was\r
1268                                         // no timely activation\r
1269                                         include_once($DIR_LIBS . 'ADMIN.php');\r
1270                                         Admin::deleteOneMember(intval($o->vmember));\r
1271                                         break;\r
1272                                 case 'addresschange':\r
1273                                         // revert the e-mail address of the member back to old address\r
1274                                         list($oldEmail, $oldCanLogin) = preg_split('#/#', $o->vextra);\r
1275                                         sql_query('UPDATE ' . sql_table('member') . ' SET mcanlogin=' . intval($oldCanLogin). ', memail=\'' . sql_real_escape_string($oldEmail). '\' WHERE mnumber=' . intval($o->vmember));\r
1276                                         break;\r
1277                                 case 'forgot':\r
1278                                         // delete the activation link and ignore. member can request a new password using the\r
1279                                         // forgot password link\r
1280                                         break;\r
1281                         }\r
1282                 }\r
1283                 \r
1284                 // 2. delete activation entries for real\r
1285                 sql_query('DELETE FROM ' . sql_table('activation') . ' WHERE vtime < \'' . date('Y-m-d H:i:s',$boundary) . '\'');\r
1286                 return;\r
1287         }\r
1288         \r
1289         /**\r
1290          * Member::$language\r
1291          * \r
1292          * @obsolete\r
1293          * @param       void\r
1294          * @return      void\r
1295          * \r
1296          */\r
1297         public $language = '';\r
1298         /**\r
1299          * Member::getLanguage()\r
1300          * \r
1301          * @obsolete\r
1302          * @param       void\r
1303          * @return      void\r
1304          * \r
1305          */\r
1306         public function getLanguage()\r
1307         {\r
1308                 if ( ($language = i18n::convert_locale_to_old_language_file_name($this->locale)) === FALSE )\r
1309                 {\r
1310                         $language = '';\r
1311                 }\r
1312                 return $language;\r
1313         }\r
1314 }\r