alecpl
2010-03-01 929a508d801d9434c8d98dccd0311e3a707303ba
commit | author | age
fba1f5 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/rcube_user.inc                                        |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
cbbef3 8  | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
fba1f5 9  | Licensed under the GNU GPL                                            |
T 10  |                                                                       |
11  | PURPOSE:                                                              |
12  |   This class represents a system user linked and provides access      |
13  |   to the related database records.                                    |
14  |                                                                       |
15  +-----------------------------------------------------------------------+
16  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
17  +-----------------------------------------------------------------------+
18
638fb8 19  $Id$
fba1f5 20
T 21 */
22
23
24 /**
25  * Class representing a system user
26  *
45f56c 27  * @package    Core
fba1f5 28  * @author     Thomas Bruederli <roundcube@gmail.com>
T 29  */
30 class rcube_user
31 {
197601 32   public $ID = null;
T 33   public $data = null;
29111b 34   public $language = null;
197601 35   
T 36   private $db = null;
fba1f5 37   
T 38   
39   /**
40    * Object constructor
41    *
42    * @param object DB Database connection
43    */
44   function __construct($id = null, $sql_arr = null)
45   {
197601 46     $this->db = rcmail::get_instance()->get_dbh();
fba1f5 47     
T 48     if ($id && !$sql_arr)
49     {
929a50 50       $sql_result = $this->db->query("SELECT * FROM ".get_table_name('users')." WHERE user_id=?", $id);
197601 51       $sql_arr = $this->db->fetch_assoc($sql_result);
fba1f5 52     }
T 53     
54     if (!empty($sql_arr))
55     {
56       $this->ID = $sql_arr['user_id'];
57       $this->data = $sql_arr;
197601 58       $this->language = $sql_arr['language'];
fba1f5 59     }
T 60   }
61
95987c 62
fba1f5 63   /**
T 64    * Build a user name string (as e-mail address)
65    *
66    * @return string Full user name
67    */
68   function get_username()
69   {
70     return $this->data['username'] ? $this->data['username'] . (!strpos($this->data['username'], '@') ? '@'.$this->data['mail_host'] : '') : false;
71   }
72   
73   
74   /**
75    * Get the preferences saved for this user
76    *
77    * @return array Hash array with prefs
78    */
79   function get_prefs()
80   {
d6869a 81     if (!empty($this->language))
A 82       $prefs = array('language' => $this->language);
155329 83     
fba1f5 84     if ($this->ID && $this->data['preferences'])
155329 85       $prefs += (array)unserialize($this->data['preferences']);
T 86     
87     return $prefs;
fba1f5 88   }
T 89   
90   
91   /**
92    * Write the given user prefs to the user's record
93    *
286420 94    * @param array User prefs to save
fba1f5 95    * @return boolean True on success, False on failure
T 96    */
97   function save_prefs($a_user_prefs)
98   {
99     if (!$this->ID)
100       return false;
286420 101       
T 102     $config = rcmail::get_instance()->config;
103     $old_prefs = (array)$this->get_prefs();
fba1f5 104
T 105     // merge (partial) prefs array with existing settings
286420 106     $save_prefs = $a_user_prefs + $old_prefs;
T 107     unset($save_prefs['language']);
108     
109     // don't save prefs with default values if they haven't been changed yet
110     foreach ($a_user_prefs as $key => $value) {
111       if (!isset($old_prefs[$key]) && ($value == $config->get($key)))
112         unset($save_prefs[$key]);
113     }
114     
197601 115     $this->db->query(
fba1f5 116       "UPDATE ".get_table_name('users')."
T 117        SET    preferences=?,
118               language=?
119        WHERE  user_id=?",
286420 120       serialize($save_prefs),
197601 121       $_SESSION['language'],
fba1f5 122       $this->ID);
T 123
197601 124     $this->language = $_SESSION['language'];
286420 125     if ($this->db->affected_rows()) {
b545d3 126       $config->set_user_prefs($a_user_prefs);
fba1f5 127       return true;
T 128     }
129
130     return false;
131   }
132   
133   
134   /**
135    * Get default identity of this user
136    *
137    * @param int  Identity ID. If empty, the default identity is returned
ed205f 138    * @return array Hash array with all cols of the identity record
fba1f5 139    */
T 140   function get_identity($id = null)
141   {
ed205f 142     $result = $this->list_identities($id ? sprintf('AND identity_id=%d', $id) : '');
T 143     return $result[0];
fba1f5 144   }
T 145   
146   
147   /**
148    * Return a list of all identities linked with this user
149    *
150    * @return array List of identities
151    */
152   function list_identities($sql_add = '')
153   {
154     // get contacts from DB
197601 155     $sql_result = $this->db->query(
fba1f5 156       "SELECT * FROM ".get_table_name('identities')."
929a50 157        WHERE del<>1 AND user_id=?
fba1f5 158        $sql_add
fc1a10 159        ORDER BY ".$this->db->quoteIdentifier('standard')." DESC, name ASC, identity_id ASC",
fba1f5 160       $this->ID);
T 161     
ed205f 162     $result = array();
T 163     while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
164       $result[] = $sql_arr;
165     }
166     
167     return $result;
fba1f5 168   }
T 169   
170   
171   /**
172    * Update a specific identity record
173    *
174    * @param int    Identity ID
175    * @param array  Hash array with col->value pairs to save
176    * @return boolean True if saved successfully, false if nothing changed
177    */
178   function update_identity($iid, $data)
179   {
180     if (!$this->ID)
181       return false;
182     
55f54e 183     $query_cols = $query_params = array();
fba1f5 184     
T 185     foreach ((array)$data as $col => $value)
186     {
55f54e 187       $query_cols[] = $this->db->quoteIdentifier($col) . '=?';
A 188       $query_params[] = $value;
fba1f5 189     }
55f54e 190     $query_params[] = $iid;
A 191     $query_params[] = $this->ID;
192
193     $sql = "UPDATE ".get_table_name('identities')."
194        SET ".join(', ', $query_cols)."
fba1f5 195        WHERE  identity_id=?
T 196        AND    user_id=?
55f54e 197        AND    del<>1";
A 198
199     call_user_func_array(array($this->db, 'query'),
200                         array_merge(array($sql), $query_params));
fba1f5 201     
197601 202     return $this->db->affected_rows();
fba1f5 203   }
T 204   
205   
206   /**
207    * Create a new identity record linked with this user
208    *
209    * @param array  Hash array with col->value pairs to save
210    * @return int  The inserted identity ID or false on error
211    */
212   function insert_identity($data)
213   {
214     if (!$this->ID)
215       return false;
216
333c48 217     unset($data['user_id']);
A 218
fba1f5 219     $insert_cols = $insert_values = array();
T 220     foreach ((array)$data as $col => $value)
221     {
197601 222       $insert_cols[] = $this->db->quoteIdentifier($col);
55f54e 223       $insert_values[] = $value;
fba1f5 224     }
55f54e 225     $insert_cols[] = 'user_id';
A 226     $insert_values[] = $this->ID;
fba1f5 227
55f54e 228     $sql = "INSERT INTO ".get_table_name('identities')."
A 229         (".join(', ', $insert_cols).")
230        VALUES (".join(', ', array_pad(array(), sizeof($insert_values), '?')).")";
231
232     call_user_func_array(array($this->db, 'query'),
233                         array_merge(array($sql), $insert_values));
fba1f5 234
6b7e8e 235     return $this->db->insert_id('identities');
fba1f5 236   }
T 237   
238   
239   /**
240    * Mark the given identity as deleted
241    *
242    * @param int  Identity ID
243    * @return boolean True if deleted successfully, false if nothing changed
244    */
245   function delete_identity($iid)
246   {
247     if (!$this->ID)
248       return false;
e3a0af 249
197601 250     $sql_result = $this->db->query("SELECT count(*) AS ident_count FROM " .
e3a0af 251       get_table_name('identities') .
d45176 252       " WHERE user_id = ? AND del <> 1",
e3a0af 253       $this->ID);
T 254
197601 255     $sql_arr = $this->db->fetch_assoc($sql_result);
e3a0af 256     if ($sql_arr['ident_count'] <= 1)
T 257       return false;
fba1f5 258     
197601 259     $this->db->query(
fba1f5 260       "UPDATE ".get_table_name('identities')."
T 261        SET    del=1
262        WHERE  user_id=?
263        AND    identity_id=?",
264       $this->ID,
265       $iid);
266
197601 267     return $this->db->affected_rows();
fba1f5 268   }
T 269   
270   
271   /**
272    * Make this identity the default one for this user
273    *
274    * @param int The identity ID
275    */
276   function set_default($iid)
277   {
278     if ($this->ID && $iid)
279     {
197601 280       $this->db->query(
fba1f5 281         "UPDATE ".get_table_name('identities')."
197601 282          SET ".$this->db->quoteIdentifier('standard')."='0'
fba1f5 283          WHERE  user_id=?
T 284          AND    identity_id<>?
285          AND    del<>1",
286         $this->ID,
287         $iid);
288     }
289   }
290   
291   
292   /**
293    * Update user's last_login timestamp
294    */
295   function touch()
296   {
297     if ($this->ID)
298     {
197601 299       $this->db->query(
fba1f5 300         "UPDATE ".get_table_name('users')."
197601 301          SET    last_login=".$this->db->now()."
fba1f5 302          WHERE  user_id=?",
T 303         $this->ID);
304     }
305   }
306   
307   
308   /**
309    * Clear the saved object state
310    */
311   function reset()
312   {
313     $this->ID = null;
314     $this->data = null;
315   }
316   
317   
318   /**
319    * Find a user record matching the given name and host
320    *
321    * @param string IMAP user name
322    * @param string IMAP host name
323    * @return object rcube_user New user instance
324    */
197601 325   static function query($user, $host)
fba1f5 326   {
197601 327     $dbh = rcmail::get_instance()->get_dbh();
fba1f5 328     
ba0e78 329     // query for matching user name
T 330     $query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host=? AND %s=?";
331     $sql_result = $dbh->query(sprintf($query, 'username'), $host, $user);
332     
333     // query for matching alias
334     if (!($sql_arr = $dbh->fetch_assoc($sql_result))) {
335       $sql_result = $dbh->query(sprintf($query, 'alias'), $host, $user);
336       $sql_arr = $dbh->fetch_assoc($sql_result);
337     }
338     
fba1f5 339     // user already registered -> overwrite username
ba0e78 340     if ($sql_arr)
fba1f5 341       return new rcube_user($sql_arr['user_id'], $sql_arr);
T 342     else
343       return false;
344   }
345   
346   
347   /**
348    * Create a new user record and return a rcube_user instance
349    *
350    * @param string IMAP user name
351    * @param string IMAP host
352    * @return object rcube_user New user instance
353    */
197601 354   static function create($user, $host)
fba1f5 355   {
cc97ea 356     $user_name  = '';
fba1f5 357     $user_email = '';
197601 358     $rcmail = rcmail::get_instance();
cc97ea 359
fccdf1 360     // try to resolve user in virtuser table and file
984e97 361     if ($email_list = self::user2email($user, false, true)) {
A 362       $user_email = is_array($email_list[0]) ? $email_list[0][0] : $email_list[0];
fccdf1 363     }
A 364
365     $data = $rcmail->plugins->exec_hook('create_user',
366     array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email));
367
f879f4 368     // plugin aborted this operation
T 369     if ($data['abort'])
370       return false;
cc97ea 371
fccdf1 372     $user_name = $data['user_name'];
A 373     $user_email = $data['user_email'];
fba1f5 374
fccdf1 375     $dbh = $rcmail->get_dbh();
cc97ea 376
197601 377     $dbh->query(
fba1f5 378       "INSERT INTO ".get_table_name('users')."
T 379         (created, last_login, username, mail_host, alias, language)
197601 380        VALUES (".$dbh->now().", ".$dbh->now().", ?, ?, ?, ?)",
fba1f5 381       strip_newlines($user),
T 382       strip_newlines($host),
69f18a 383       strip_newlines($data['alias'] ? $data['alias'] : $user_email),
197601 384       $_SESSION['language']);
fba1f5 385
6b7e8e 386     if ($user_id = $dbh->insert_id('users'))
fba1f5 387     {
07722a 388       // create rcube_user instance to make plugin hooks work
T 389       $user_instance = new rcube_user($user_id);
390       $rcmail->user = $user_instance;
391
83a763 392       $mail_domain = $rcmail->config->mail_domain($host);
fba1f5 393
T 394       if ($user_email=='')
395         $user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
396
cc97ea 397       if ($user_name == '') {
T 398         $user_name = $user != $user_email ? $user : '';
399       }
fba1f5 400
942069 401       if (empty($email_list))
fccdf1 402         $email_list[] = strip_newlines($user_email);
ac9927 403       // identities_level check
fccdf1 404       else if (count($email_list) > 1 && $rcmail->config->get('identities_level', 0) > 1)
ac9927 405         $email_list = array($email_list[0]);
A 406
407       // create new identities records
942069 408       $standard = 1;
1301e9 409       foreach ($email_list as $row) {
A 410         if (is_array($row)) {
411           $email = $row[0];
412           $name = $row[1] ? $row[1] : $user_name;
08c8c3 413         }
T 414         else {
415           $email = $row;
416           $name = $user_name;
417         }
adc0bf 418
08c8c3 419         $plugin = $rcmail->plugins->exec_hook('create_identity', array(
f879f4 420           'login' => true,
08c8c3 421           'record' => array(
T 422             'user_id' => $user_id,
423             'name' => strip_newlines($name),
424             'email' => $email,
425             'standard' => $standard,
426           ),
427         ));
f879f4 428           
adc0bf 429         if (!$plugin['abort'] && $plugin['record']['email']) {
333c48 430           $rcmail->user->insert_identity($plugin['record']);
f879f4 431         }
cc97ea 432         $standard = 0;
fba1f5 433       }
T 434     }
435     else
436     {
437       raise_error(array(
438         'code' => 500,
439         'type' => 'php',
440         'line' => __LINE__,
441         'file' => __FILE__,
442         'message' => "Failed to create new user"), true, false);
443     }
444     
07722a 445     return $user_id ? $user_instance : false;
fba1f5 446   }
T 447   
448   
449   /**
942069 450    * Resolve username using a virtuser file
fba1f5 451    *
T 452    * @param string E-mail address to resolve
453    * @return string Resolved IMAP username
454    */
197601 455   static function email2user($email)
fba1f5 456   {
23a2ee 457     $r = self::findinvirtual('/^' . preg_quote($email, '/') . '\s/');
fba1f5 458
T 459     for ($i=0; $i<count($r); $i++)
460     {
e20033 461       $data = trim($r[$i]);
fba1f5 462       $arr = preg_split('/\s+/', $data);
T 463       if (count($arr) > 0)
942069 464         return trim($arr[count($arr)-1]);
fba1f5 465     }
T 466
942069 467     return NULL;
fba1f5 468   }
T 469
470
471   /**
942069 472    * Resolve e-mail address from virtuser file/table
fba1f5 473    *
T 474    * @param string User name
942069 475    * @param boolean If true returns first found entry
1301e9 476    * @param boolean If true returns email as array (email and name for identity)
942069 477    * @return mixed Resolved e-mail address string or array of strings
fba1f5 478    */
1301e9 479   static function user2email($user, $first=true, $extended=false)
fba1f5 480   {
942069 481     $result = array();
A 482     $rcmail = rcmail::get_instance();
483     $dbh = $rcmail->get_dbh();
fba1f5 484
942069 485     // SQL lookup
A 486     if ($virtuser_query = $rcmail->config->get('virtuser_query')) {
487       $sql_result = $dbh->query(preg_replace('/%u/', $dbh->escapeSimple($user), $virtuser_query));
488       while ($sql_arr = $dbh->fetch_array($sql_result))
489         if (strpos($sql_arr[0], '@')) {
1301e9 490           $result[] = ($extended && count($sql_arr) > 1) ? $sql_arr : $sql_arr[0];
cc97ea 491           if ($first)
T 492             return $result[0];
493         }
942069 494     }
A 495     // File lookup
23a2ee 496     $r = self::findinvirtual('/\s' . preg_quote($user, '/') . '\s*$/');
fba1f5 497     for ($i=0; $i<count($r); $i++)
T 498     {
499       $data = $r[$i];
500       $arr = preg_split('/\s+/', $data);
942069 501       if (count($arr) > 0 && strpos($arr[0], '@'))
fba1f5 502       {
942069 503         $result[] = trim(str_replace('\\@', '@', $arr[0]));
A 504
cc97ea 505         if ($first)
942069 506           return $result[0];
fba1f5 507       }
T 508     }
942069 509     
A 510     return empty($result) ? NULL : $result;
fba1f5 511   }
83a763 512   
T 513   
514   /**
942069 515    * Find matches of the given pattern in virtuser file
83a763 516    * 
T 517    * @param string Regular expression to search for
518    * @return array Matching entries
519    */
520   private static function findinvirtual($pattern)
521   {
522     $result = array();
523     $virtual = null;
524     
525     if ($virtuser_file = rcmail::get_instance()->config->get('virtuser_file'))
526       $virtual = file($virtuser_file);
527     
528     if (empty($virtual))
529       return $result;
530     
531     // check each line for matches
532     foreach ($virtual as $line)
533     {
534       $line = trim($line);
535       if (empty($line) || $line{0}=='#')
536         continue;
537         
23a2ee 538       if (preg_match($pattern, $line))
83a763 539         $result[] = $line;
T 540     }
541     
542     return $result;
543   }
fba1f5 544
T 545 }
546
547