4 +-----------------------------------------------------------------------+
5 | program/include/rcube_user.inc |
7 | This file is part of the Roundcube Webmail client |
8 | Copyright (C) 2005-2010, The Roundcube Dev Team |
9 | Licensed under the GNU GPL |
12 | This class represents a system user linked and provides access |
13 | to the related database records. |
15 +-----------------------------------------------------------------------+
16 | Author: Thomas Bruederli <roundcube@gmail.com> |
17 +-----------------------------------------------------------------------+
19 $Id: rcube_user.php 5183 2011-09-06 17:18:12Z alec $
25 * Class representing a system user
28 * @author Thomas Bruederli <roundcube@gmail.com>
37 * Holds database connection.
50 const SEARCH_ADDRESSBOOK = 1;
51 const SEARCH_MAIL = 2;
56 * @param int $id User id
57 * @param array $sql_arr SQL result set
59 function __construct($id = null, $sql_arr = null)
61 $this->rc = rcmail::get_instance();
62 $this->db = $this->rc->get_dbh();
64 if ($id && !$sql_arr) {
65 $sql_result = $this->db->query(
66 "SELECT * FROM ".get_table_name('users')." WHERE user_id = ?", $id);
67 $sql_arr = $this->db->fetch_assoc($sql_result);
70 if (!empty($sql_arr)) {
71 $this->ID = $sql_arr['user_id'];
72 $this->data = $sql_arr;
73 $this->language = $sql_arr['language'];
79 * Build a user name string (as e-mail address)
81 * @param string $part Username part (empty or 'local' or 'domain')
82 * @return string Full user name or its part
84 function get_username($part = null)
86 if ($this->data['username']) {
87 list($local, $domain) = explode('@', $this->data['username']);
89 // at least we should always have the local part
90 if ($part == 'local') {
93 // if no domain was provided...
95 $domain = $this->rc->config->mail_domain($this->data['mail_host']);
98 if ($part == 'domain') {
103 return $local . '@' . $domain;
113 * Get the preferences saved for this user
115 * @return array Hash array with prefs
119 if (!empty($this->language))
120 $prefs = array('language' => $this->language);
123 // Preferences from session (write-master is unavailable)
124 if (!empty($_SESSION['preferences'])) {
125 // Check last write attempt time, try to write again (every 5 minutes)
126 if ($_SESSION['preferences_time'] < time() - 5 * 60) {
127 $saved_prefs = unserialize($_SESSION['preferences']);
128 $this->rc->session->remove('preferences');
129 $this->rc->session->remove('preferences_time');
130 $this->save_prefs($saved_prefs);
133 $this->data['preferences'] = $_SESSION['preferences'];
137 if ($this->data['preferences']) {
138 $prefs += (array)unserialize($this->data['preferences']);
147 * Write the given user prefs to the user's record
149 * @param array $a_user_prefs User prefs to save
150 * @return boolean True on success, False on failure
152 function save_prefs($a_user_prefs)
157 $config = $this->rc->config;
158 $old_prefs = (array)$this->get_prefs();
160 // merge (partial) prefs array with existing settings
161 $save_prefs = $a_user_prefs + $old_prefs;
162 unset($save_prefs['language']);
164 // don't save prefs with default values if they haven't been changed yet
165 foreach ($a_user_prefs as $key => $value) {
166 if (!isset($old_prefs[$key]) && ($value == $config->get($key)))
167 unset($save_prefs[$key]);
170 $save_prefs = serialize($save_prefs);
173 "UPDATE ".get_table_name('users').
174 " SET preferences = ?".
176 " WHERE user_id = ?",
178 $_SESSION['language'],
181 $this->language = $_SESSION['language'];
184 if ($this->db->affected_rows() !== false) {
185 $config->set_user_prefs($a_user_prefs);
186 $this->data['preferences'] = $save_prefs;
188 if (isset($_SESSION['preferences'])) {
189 $this->rc->session->remove('preferences');
190 $this->rc->session->remove('preferences_time');
194 // Update error, but we are using replication (we have read-only DB connection)
195 // and we are storing session not in the SQL database
196 // we can store preferences in session and try to write later (see get_prefs())
197 else if ($this->db->is_replicated() && $config->get('session_storage', 'db') != 'db') {
198 $_SESSION['preferences'] = $save_prefs;
199 $_SESSION['preferences_time'] = time();
200 $config->set_user_prefs($a_user_prefs);
201 $this->data['preferences'] = $save_prefs;
209 * Get default identity of this user
211 * @param int $id Identity ID. If empty, the default identity is returned
212 * @return array Hash array with all cols of the identity record
214 function get_identity($id = null)
216 $result = $this->list_identities($id ? sprintf('AND identity_id = %d', $id) : '');
222 * Return a list of all identities linked with this user
224 * @param string $sql_add Optional WHERE clauses
225 * @return array List of identities
227 function list_identities($sql_add = '')
231 $sql_result = $this->db->query(
232 "SELECT * FROM ".get_table_name('identities').
233 " WHERE del <> 1 AND user_id = ?".
234 ($sql_add ? " ".$sql_add : "").
235 " ORDER BY ".$this->db->quoteIdentifier('standard')." DESC, name ASC, identity_id ASC",
238 while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
239 $result[] = $sql_arr;
247 * Update a specific identity record
249 * @param int $iid Identity ID
250 * @param array $data Hash array with col->value pairs to save
251 * @return boolean True if saved successfully, false if nothing changed
253 function update_identity($iid, $data)
258 $query_cols = $query_params = array();
260 foreach ((array)$data as $col => $value) {
261 $query_cols[] = $this->db->quoteIdentifier($col) . ' = ?';
262 $query_params[] = $value;
264 $query_params[] = $iid;
265 $query_params[] = $this->ID;
267 $sql = "UPDATE ".get_table_name('identities').
268 " SET changed = ".$this->db->now().", ".join(', ', $query_cols).
269 " WHERE identity_id = ?".
273 call_user_func_array(array($this->db, 'query'),
274 array_merge(array($sql), $query_params));
276 return $this->db->affected_rows();
281 * Create a new identity record linked with this user
283 * @param array $data Hash array with col->value pairs to save
284 * @return int The inserted identity ID or false on error
286 function insert_identity($data)
291 unset($data['user_id']);
293 $insert_cols = $insert_values = array();
294 foreach ((array)$data as $col => $value) {
295 $insert_cols[] = $this->db->quoteIdentifier($col);
296 $insert_values[] = $value;
298 $insert_cols[] = 'user_id';
299 $insert_values[] = $this->ID;
301 $sql = "INSERT INTO ".get_table_name('identities').
302 " (changed, ".join(', ', $insert_cols).")".
303 " VALUES (".$this->db->now().", ".join(', ', array_pad(array(), sizeof($insert_values), '?')).")";
305 call_user_func_array(array($this->db, 'query'),
306 array_merge(array($sql), $insert_values));
308 return $this->db->insert_id('identities');
313 * Mark the given identity as deleted
315 * @param int $iid Identity ID
316 * @return boolean True if deleted successfully, false if nothing changed
318 function delete_identity($iid)
323 $sql_result = $this->db->query(
324 "SELECT count(*) AS ident_count FROM ".get_table_name('identities').
325 " WHERE user_id = ? AND del <> 1",
328 $sql_arr = $this->db->fetch_assoc($sql_result);
330 // we'll not delete last identity
331 if ($sql_arr['ident_count'] <= 1)
335 "UPDATE ".get_table_name('identities').
336 " SET del = 1, changed = ".$this->db->now().
337 " WHERE user_id = ?".
338 " AND identity_id = ?",
342 return $this->db->affected_rows();
347 * Make this identity the default one for this user
349 * @param int $iid The identity ID
351 function set_default($iid)
353 if ($this->ID && $iid) {
355 "UPDATE ".get_table_name('identities').
356 " SET ".$this->db->quoteIdentifier('standard')." = '0'".
357 " WHERE user_id = ?".
358 " AND identity_id <> ?".
367 * Update user's last_login timestamp
373 "UPDATE ".get_table_name('users').
374 " SET last_login = ".$this->db->now().
375 " WHERE user_id = ?",
382 * Clear the saved object state
392 * Find a user record matching the given name and host
394 * @param string $user IMAP user name
395 * @param string $host IMAP host name
396 * @return rcube_user New user instance
398 static function query($user, $host)
400 $dbh = rcmail::get_instance()->get_dbh();
402 // query for matching user name
403 $query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host = ? AND %s = ?";
404 $sql_result = $dbh->query(sprintf($query, 'username'), $host, $user);
406 // query for matching alias
407 if (!($sql_arr = $dbh->fetch_assoc($sql_result))) {
408 $sql_result = $dbh->query(sprintf($query, 'alias'), $host, $user);
409 $sql_arr = $dbh->fetch_assoc($sql_result);
412 // user already registered -> overwrite username
414 return new rcube_user($sql_arr['user_id'], $sql_arr);
421 * Create a new user record and return a rcube_user instance
423 * @param string $user IMAP user name
424 * @param string $host IMAP host
425 * @return rcube_user New user instance
427 static function create($user, $host)
431 $rcmail = rcmail::get_instance();
433 // try to resolve user in virtuser table and file
434 if ($email_list = self::user2email($user, false, true)) {
435 $user_email = is_array($email_list[0]) ? $email_list[0]['email'] : $email_list[0];
438 $data = $rcmail->plugins->exec_hook('user_create',
439 array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email, 'host'=>$host));
441 // plugin aborted this operation
445 $user_name = $data['user_name'];
446 $user_email = $data['user_email'];
448 $dbh = $rcmail->get_dbh();
451 "INSERT INTO ".get_table_name('users').
452 " (created, last_login, username, mail_host, alias, language)".
453 " VALUES (".$dbh->now().", ".$dbh->now().", ?, ?, ?, ?)",
454 strip_newlines($user),
455 strip_newlines($host),
456 strip_newlines($data['alias'] ? $data['alias'] : $user_email),
457 strip_newlines($data['language'] ? $data['language'] : $_SESSION['language']));
459 if ($user_id = $dbh->insert_id('users')) {
460 // create rcube_user instance to make plugin hooks work
461 $user_instance = new rcube_user($user_id);
462 $rcmail->user = $user_instance;
464 $mail_domain = $rcmail->config->mail_domain($host);
466 if ($user_email == '') {
467 $user_email = strpos($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain);
469 if ($user_name == '') {
470 $user_name = $user != $user_email ? $user : '';
473 if (empty($email_list))
474 $email_list[] = strip_newlines($user_email);
475 // identities_level check
476 else if (count($email_list) > 1 && $rcmail->config->get('identities_level', 0) > 1)
477 $email_list = array($email_list[0]);
479 // create new identities records
481 foreach ($email_list as $row) {
484 if (is_array($row)) {
488 $record['email'] = $row;
491 if (empty($record['name']))
492 $record['name'] = $user_name;
493 $record['name'] = strip_newlines($record['name']);
494 $record['user_id'] = $user_id;
495 $record['standard'] = $standard;
497 $plugin = $rcmail->plugins->exec_hook('identity_create',
498 array('login' => true, 'record' => $record));
500 if (!$plugin['abort'] && $plugin['record']['email']) {
501 $rcmail->user->insert_identity($plugin['record']);
512 'message' => "Failed to create new user"), true, false);
515 return $user_id ? $user_instance : false;
520 * Resolve username using a virtuser plugins
522 * @param string $email E-mail address to resolve
523 * @return string Resolved IMAP username
525 static function email2user($email)
527 $rcmail = rcmail::get_instance();
528 $plugin = $rcmail->plugins->exec_hook('email2user',
529 array('email' => $email, 'user' => NULL));
531 return $plugin['user'];
536 * Resolve e-mail address from virtuser plugins
538 * @param string $user User name
539 * @param boolean $first If true returns first found entry
540 * @param boolean $extended If true returns email as array (email and name for identity)
541 * @return mixed Resolved e-mail address string or array of strings
543 static function user2email($user, $first=true, $extended=false)
545 $rcmail = rcmail::get_instance();
546 $plugin = $rcmail->plugins->exec_hook('user2email',
547 array('email' => NULL, 'user' => $user,
548 'first' => $first, 'extended' => $extended));
550 return empty($plugin['email']) ? NULL : $plugin['email'];
555 * Return a list of saved searches linked with this user
557 * @param int $type Search type
559 * @return array List of saved searches indexed by search ID
561 function list_searches($type)
563 $plugin = $this->rc->plugins->exec_hook('saved_search_list', array('type' => $type));
565 if ($plugin['abort']) {
566 return (array) $plugin['result'];
571 $sql_result = $this->db->query(
572 "SELECT search_id AS id, ".$this->db->quoteIdentifier('name')
573 ." FROM ".get_table_name('searches')
574 ." WHERE user_id = ?"
575 ." AND ".$this->db->quoteIdentifier('type')." = ?"
576 ." ORDER BY ".$this->db->quoteIdentifier('name'),
577 (int) $this->ID, (int) $type);
579 while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
580 $sql_arr['data'] = unserialize($sql_arr['data']);
581 $result[$sql_arr['id']] = $sql_arr;
589 * Return saved search data.
591 * @param int $id Row identifier
595 function get_search($id)
597 $plugin = $this->rc->plugins->exec_hook('saved_search_get', array('id' => $id));
599 if ($plugin['abort']) {
600 return $plugin['result'];
603 $sql_result = $this->db->query(
604 "SELECT ".$this->db->quoteIdentifier('name')
605 .", ".$this->db->quoteIdentifier('data')
606 .", ".$this->db->quoteIdentifier('type')
607 ." FROM ".get_table_name('searches')
608 ." WHERE user_id = ?"
609 ." AND search_id = ?",
610 (int) $this->ID, (int) $id);
612 while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
615 'name' => $sql_arr['name'],
616 'type' => $sql_arr['type'],
617 'data' => unserialize($sql_arr['data']),
626 * Deletes given saved search record
628 * @param int $sid Search ID
630 * @return boolean True if deleted successfully, false if nothing changed
632 function delete_search($sid)
638 "DELETE FROM ".get_table_name('searches')
639 ." WHERE user_id = ?"
640 ." AND search_id = ?",
641 (int) $this->ID, $sid);
643 return $this->db->affected_rows();
648 * Create a new saved search record linked with this user
650 * @param array $data Hash array with col->value pairs to save
652 * @return int The inserted search ID or false on error
654 function insert_search($data)
659 $insert_cols[] = 'user_id';
660 $insert_values[] = (int) $this->ID;
661 $insert_cols[] = $this->db->quoteIdentifier('type');
662 $insert_values[] = (int) $data['type'];
663 $insert_cols[] = $this->db->quoteIdentifier('name');
664 $insert_values[] = $data['name'];
665 $insert_cols[] = $this->db->quoteIdentifier('data');
666 $insert_values[] = serialize($data['data']);
668 $sql = "INSERT INTO ".get_table_name('searches')
669 ." (".join(', ', $insert_cols).")"
670 ." VALUES (".join(', ', array_pad(array(), sizeof($insert_values), '?')).")";
672 call_user_func_array(array($this->db, 'query'),
673 array_merge(array($sql), $insert_values));
675 return $this->db->insert_id('searches');