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

Benjamin Mako Hill || Want to submit a patch?