* Fixed REG_BADRPT error in isValidEmail() that prevented registration
[scuttle] / services / userservice.php
1 <?php
2 class UserService {
3     var $db;
4
5     function &getInstance(&$db) {
6         static $instance;
7         if (!isset($instance))
8             $instance =& new UserService($db);
9         return $instance;
10     }
11
12     var $fields = array(
13         'primary'   =>  'uId',
14         'username'  =>  'username',
15         'password'  =>  'password'
16     );
17     var $profileurl;
18     var $tablename;
19     var $sessionkey;
20     var $cookiekey;
21     var $cookietime = 1209600; // 2 weeks
22
23     function UserService(&$db) {
24         $this->db =& $db;
25         $this->tablename = $GLOBALS['tableprefix'] .'users';
26         $this->sessionkey = $GLOBALS['cookieprefix'] .'-currentuserid';
27         $this->cookiekey = $GLOBALS['cookieprefix'] .'-login';
28         $this->profileurl = createURL('profile', '%2$s');
29     }
30
31     function _checkdns($host) {
32         if (function_exists('checkdnsrr')) {
33             return checkdnsrr($host);
34         } else {
35             return $this->_checkdnsrr($host);
36         }
37     }
38
39     function _checkdnsrr($host, $type = "MX") {
40         if(!empty($host)) {
41             @exec("nslookup -type=$type $host", $output);
42             while(list($k, $line) = each($output)) {
43                 if(eregi("^$host", $line)) {
44                     return true;
45                 }
46             }
47             return false;
48         }
49     }
50
51     function _getuser($fieldname, $value) {
52         $query = 'SELECT * FROM '. $this->getTableName() .' WHERE '. $fieldname .' = "'. $this->db->sql_escape($value) .'"';
53
54         if (! ($dbresult =& $this->db->sql_query($query)) ) {
55             message_die(GENERAL_ERROR, 'Could not get user', '', __LINE__, __FILE__, $query, $this->db);
56             return false;
57         }
58
59         if ($row =& $this->db->sql_fetchrow($dbresult))
60             return $row;
61         else
62             return false;
63     }
64
65     function _randompassword() {
66         $password = mt_rand(1, 99999999);
67         $password = substr(md5($password), mt_rand(0, 19), mt_rand(6, 12));
68         return $password;
69     }
70
71     function _updateuser($uId, $fieldname, $value) {
72         $updates = array ($fieldname => $value);
73         $sql = 'UPDATE '. $this->getTableName() .' SET '. $this->db->sql_build_array('UPDATE', $updates) .' WHERE '. $this->getFieldName('primary') .'='. intval($uId);
74
75         // Execute the statement.
76         $this->db->sql_transaction('begin');
77         if (!($dbresult = & $this->db->sql_query($sql))) {
78             $this->db->sql_transaction('rollback');
79             message_die(GENERAL_ERROR, 'Could not update user', '', __LINE__, __FILE__, $sql, $this->db);
80             return false;
81         }
82         $this->db->sql_transaction('commit');
83
84         // Everything worked out, so return true.
85         return true;
86     }
87
88     function getProfileUrl($id, $username) {
89         return sprintf($this->profileurl, urlencode($id), urlencode($username));
90     }
91
92     function getUserByUsername($username) {
93         return $this->_getuser($this->getFieldName('username'), $username);
94     }
95
96     function getUser($id) {
97         return $this->_getuser($this->getFieldName('primary'), $id);
98     }
99
100     function isLoggedOn() {
101         return ($this->getCurrentUserId() !== false);
102     }
103
104     function &getCurrentUser($refresh = FALSE, $newval = NULL) {
105         static $currentuser;
106         if (!is_null($newval)) //internal use only: reset currentuser
107             $currentuser = $newval;
108         else if ($refresh || !isset($currentuser)) {
109             if ($id = $this->getCurrentUserId())
110                 $currentuser = $this->getUser($id);
111             else
112                 return;
113         }
114         return $currentuser;
115     }
116
117     function isAdmin($userid) {
118         return false; //not implemented yet
119     }
120
121     function getCurrentUserId() {
122         if (isset($_SESSION[$this->getSessionKey()])) {
123             return $_SESSION[$this->getSessionKey()];
124         } else if (isset($_COOKIE[$this->getCookieKey()])) {
125             $cook = split(':', $_COOKIE[$this->getCookieKey()]);
126             //cookie looks like this: 'id:md5(username+password)'
127             $query = 'SELECT * FROM '. $this->getTableName() .
128                      ' WHERE MD5(CONCAT('.$this->getFieldName('username') .
129                                      ', '.$this->getFieldName('password') .
130                      ')) = \''.$this->db->sql_escape($cook[1]).'\' AND '.
131                      $this->getFieldName('primary'). ' = '. $this->db->sql_escape($cook[0]);
132
133             if (! ($dbresult =& $this->db->sql_query($query)) ) {
134                 message_die(GENERAL_ERROR, 'Could not get user', '', __LINE__, __FILE__, $query, $this->db);
135                 return false;
136             }
137
138             if ($row = $this->db->sql_fetchrow($dbresult)) {
139                 $_SESSION[$this->getSessionKey()] = $row[$this->getFieldName('primary')];
140                 return $_SESSION[$this->getSessionKey()];
141             }
142         }
143         return false;
144     }
145
146     function login($username, $password, $remember = FALSE, $path = '/') {
147         $password = $this->sanitisePassword($password);
148         $query = 'SELECT '. $this->getFieldName('primary') .' FROM '. $this->getTableName() .' WHERE '. $this->getFieldName('username') .' = "'. $this->db->sql_escape($username) .'" AND '. $this->getFieldName('password') .' = "'. $this->db->sql_escape($password) .'"';
149
150         if (! ($dbresult =& $this->db->sql_query($query)) ) {
151             message_die(GENERAL_ERROR, 'Could not get user', '', __LINE__, __FILE__, $query, $this->db);
152             return false;
153         }
154
155         if ($row =& $this->db->sql_fetchrow($dbresult)) {
156             $id = $_SESSION[$this->getSessionKey()] = $row[$this->getFieldName('primary')];
157             if ($remember) {
158                 $cookie = $id .':'. md5($username.$password);
159                 setcookie($this->cookiekey, $cookie, time() + $this->cookietime, $path);
160             }
161             return true;
162         } else {
163             return false;
164         }
165     }
166
167     function logout($path = '/') {
168         @setcookie($this->cookiekey, NULL, time() - 1, $path);
169         unset($_COOKIE[$this->cookiekey]);
170         session_unset();
171         $this->getCurrentUser(TRUE, false);
172     }
173
174     function getWatchlist($uId) {
175         // Gets the list of user IDs being watched by the given user.
176         $query = 'SELECT watched FROM '. $GLOBALS['tableprefix'] .'watched WHERE uId = '. intval($uId);
177
178         if (! ($dbresult =& $this->db->sql_query($query)) ) {
179             message_die(GENERAL_ERROR, 'Could not get watchlist', '', __LINE__, __FILE__, $query, $this->db);
180             return false;
181         }
182
183         $arrWatch = array();
184         if ($this->db->sql_numrows($dbresult) == 0)
185             return $arrWatch;
186         while ($row =& $this->db->sql_fetchrow($dbresult))
187             $arrWatch[] = $row['watched'];
188         return $arrWatch;
189     }
190
191     function getWatchNames($uId, $watchedby = false) {
192         // Gets the list of user names being watched by the given user.
193         // - If $watchedby is false get the list of users that $uId watches
194         // - If $watchedby is true get the list of users that watch $uId
195         if ($watchedby) {
196             $table1 = 'b';
197             $table2 = 'a';
198         } else {
199             $table1 = 'a';
200             $table2 = 'b';
201         }
202         $query = 'SELECT '. $table1 .'.'. $this->getFieldName('username') .' FROM '. $GLOBALS['tableprefix'] .'watched AS W, '. $this->getTableName() .' AS a, '. $this->getTableName() .' AS b WHERE W.watched = a.'. $this->getFieldName('primary') .' AND W.uId = b.'. $this->getFieldName('primary') .' AND '. $table2 .'.'. $this->getFieldName('primary') .' = '. intval($uId) .' ORDER BY '. $table1 .'.'. $this->getFieldName('username');
203
204         if (!($dbresult =& $this->db->sql_query($query))) {
205             message_die(GENERAL_ERROR, 'Could not get watchlist', '', __LINE__, __FILE__, $query, $this->db);
206             return false;
207         }
208
209         $arrWatch = array();
210         if ($this->db->sql_numrows($dbresult) == 0) {
211             return $arrWatch;
212         }
213         while ($row =& $this->db->sql_fetchrow($dbresult)) {
214             $arrWatch[] = $row[$this->getFieldName('username')];
215         }
216         return $arrWatch;
217     }
218
219     function getWatchStatus($watcheduser, $currentuser) {
220         // Returns true if the current user is watching the given user, and false otherwise.
221         $query = 'SELECT watched FROM '. $GLOBALS['tableprefix'] .'watched AS W INNER JOIN '. $this->getTableName() .' AS U ON U.'. $this->getFieldName('primary') .' = W.watched WHERE U.'. $this->getFieldName('primary') .' = '. intval($watcheduser) .' AND W.uId = '. intval($currentuser);
222         
223         if (! ($dbresult =& $this->db->sql_query($query)) ) {
224             message_die(GENERAL_ERROR, 'Could not get watchstatus', '', __LINE__, __FILE__, $query, $this->db);
225             return false;
226         }
227
228         $arrWatch = array();
229         if ($this->db->sql_numrows($dbresult) == 0)
230             return false;
231         else 
232             return true;
233     }
234
235     function setWatchStatus($subjectUserID) {
236         if (!is_numeric($subjectUserID))
237             return false;
238
239         $currentUserID = $this->getCurrentUserId();
240         $watched = $this->getWatchStatus($subjectUserID, $currentUserID);
241
242         if ($watched) {
243             $sql = 'DELETE FROM '. $GLOBALS['tableprefix'] .'watched WHERE uId = '. intval($currentUserID) .' AND watched = '. intval($subjectUserID);
244             if (!($dbresult =& $this->db->sql_query($sql))) {
245                 $this->db->sql_transaction('rollback');
246                 message_die(GENERAL_ERROR, 'Could not add user to watch list', '', __LINE__, __FILE__, $sql, $this->db);
247                 return false;
248             }
249         } else {
250             $values = array(
251                 'uId' => intval($currentUserID),
252                 'watched' => intval($subjectUserID)
253             ); 
254             $sql = 'INSERT INTO '. $GLOBALS['tableprefix'] .'watched '. $this->db->sql_build_array('INSERT', $values);
255             if (!($dbresult =& $this->db->sql_query($sql))) {
256                 $this->db->sql_transaction('rollback');
257                 message_die(GENERAL_ERROR, 'Could not add user to watch list', '', __LINE__, __FILE__, $sql, $this->db);
258                 return false;
259             }
260         }
261
262         $this->db->sql_transaction('commit');
263         return true;
264     }
265
266     function addUser($username, $password, $email) {
267         // Set up the SQL UPDATE statement.
268         $datetime = gmdate('Y-m-d H:i:s', time());
269         $password = $this->sanitisePassword($password);
270         $values = array('username' => $username, 'password' => $password, 'email' => $email, 'uDatetime' => $datetime, 'uModified' => $datetime);
271         $sql = 'INSERT INTO '. $this->getTableName() .' '. $this->db->sql_build_array('INSERT', $values);
272
273         // Execute the statement.
274         $this->db->sql_transaction('begin');
275         if (!($dbresult = & $this->db->sql_query($sql))) {
276             $this->db->sql_transaction('rollback');
277             message_die(GENERAL_ERROR, 'Could not insert user', '', __LINE__, __FILE__, $sql, $this->db);
278             return false;
279         }
280         $this->db->sql_transaction('commit');
281
282         // Everything worked out, so return true.
283         return true;
284     }
285
286     function updateUser($uId, $password, $name, $email, $homepage, $uContent) {
287         if (!is_numeric($uId))
288             return false;
289
290         // Set up the SQL UPDATE statement.
291         $moddatetime = gmdate('Y-m-d H:i:s', time());
292         if ($password == '')
293             $updates = array ('uModified' => $moddatetime, 'name' => $name, 'email' => $email, 'homepage' => $homepage, 'uContent' => $uContent);
294         else
295             $updates = array ('uModified' => $moddatetime, 'password' => $this->sanitisePassword($password), 'name' => $name, 'email' => $email, 'homepage' => $homepage, 'uContent' => $uContent);
296         $sql = 'UPDATE '. $this->getTableName() .' SET '. $this->db->sql_build_array('UPDATE', $updates) .' WHERE '. $this->getFieldName('primary') .'='. intval($uId);
297
298         // Execute the statement.
299         $this->db->sql_transaction('begin');
300         if (!($dbresult = & $this->db->sql_query($sql))) {
301             $this->db->sql_transaction('rollback');
302             message_die(GENERAL_ERROR, 'Could not update user', '', __LINE__, __FILE__, $sql, $this->db);
303             return false;
304         }
305         $this->db->sql_transaction('commit');
306
307         // Everything worked out, so return true.
308         return true;
309     }
310
311     function sanitisePassword($password) {
312         return sha1(trim($password));
313     }
314
315     function generatePassword($uId) {
316         if (!is_numeric($uId))
317             return false;
318
319         $password = $this->_randompassword();
320
321         if ($this->_updateuser($uId, $this->getFieldName('password'), $this->sanitisePassword($password)))
322             return $password;
323         else
324             return false;
325     }
326
327     function isReserved($username) {
328         if (in_array($username, $GLOBALS['reservedusers'])) {
329             return true;
330         } else {
331             return false;
332         }
333     }
334
335     function isValidEmail($email) {
336         if (preg_match("/^((?:(?:(?:\w[\.\-\+_]?)*)\w)+)\@((?:(?:(?:\w[\.\-_]?){0,62})\w)+)\.(\w{2,6})$/i", $email) > 0) {
337             list($emailUser, $emailDomain) = split("@", $email);
338
339             // Check if the email domain has a DNS record
340             if ($this->_checkdns($emailDomain)) {
341                 return true;
342             }
343         }
344         return false;
345     }
346
347     // Properties
348     function getTableName()       { return $this->tablename; }
349     function setTableName($value) { $this->tablename = $value; }
350
351     function getFieldName($field)         { return $this->fields[$field]; }
352     function setFieldName($field, $value) { $this->fields[$field] = $value; }
353
354     function getSessionKey()       { return $this->sessionkey; }
355     function setSessionKey($value) { $this->sessionkey = $value; }
356
357     function getCookieKey()       { return $this->cookiekey; }
358     function setCookieKey($value) { $this->cookiekey = $value; }
359 }
360 ?>

Benjamin Mako Hill || Want to submit a patch?