source: github/program/include/rcube_user.php @ 94a5a24

HEADcourier-fixdev-browser-capabilitiespdorelease-0.7release-0.8
Last change on this file since 94a5a24 was 94a5a24, checked in by thomascube <thomas@…>, 21 months ago

Fallback to mail_domain in LDAP variable replacements; add 'host' to 'user_create' hook arguments (#1488024)

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