diff --git a/plugins/kolab_auth/kolab_auth.php b/plugins/kolab_auth/kolab_auth.php index 6a184afc..f566cff4 100644 --- a/plugins/kolab_auth/kolab_auth.php +++ b/plugins/kolab_auth/kolab_auth.php @@ -1,890 +1,890 @@ * * Copyright (C) 2011-2013, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ class kolab_auth extends rcube_plugin { static $ldap; private $username; private $data = array(); public function init() { $rcmail = rcube::get_instance(); $this->load_config(); $this->require_plugin('libkolab'); $this->add_hook('authenticate', array($this, 'authenticate')); $this->add_hook('startup', array($this, 'startup')); $this->add_hook('ready', array($this, 'ready')); $this->add_hook('user_create', array($this, 'user_create')); // Hook for password change $this->add_hook('password_ldap_bind', array($this, 'password_ldap_bind')); // Hooks related to "Login As" feature $this->add_hook('template_object_loginform', array($this, 'login_form')); $this->add_hook('storage_connect', array($this, 'imap_connect')); $this->add_hook('managesieve_connect', array($this, 'imap_connect')); $this->add_hook('smtp_connect', array($this, 'smtp_connect')); $this->add_hook('identity_form', array($this, 'identity_form')); // Hook to modify some configuration, e.g. ldap $this->add_hook('config_get', array($this, 'config_get')); // Hook to modify logging directory $this->add_hook('write_log', array($this, 'write_log')); $this->username = $_SESSION['username'] ?? null; // Enable debug logs (per-user), when logged as another user if (!empty($_SESSION['kolab_auth_admin']) && $rcmail->config->get('kolab_auth_auditlog')) { $rcmail->config->set('debug_level', 1); $rcmail->config->set('smtp_log', true); $rcmail->config->set('log_logins', true); $rcmail->config->set('log_session', true); $rcmail->config->set('memcache_debug', true); $rcmail->config->set('imap_debug', true); $rcmail->config->set('ldap_debug', true); $rcmail->config->set('smtp_debug', true); $rcmail->config->set('sql_debug', true); // SQL debug need to be set directly on DB object // setting config variable will not work here because // the object is already initialized/configured if ($db = $rcmail->get_dbh()) { $db->set_debug(true); } } } /** * Ready hook handler */ public function ready($args) { $rcmail = rcube::get_instance(); // Store user unique identifier for freebusy_session_auth feature if (!($uniqueid = $rcmail->config->get('kolab_uniqueid'))) { $uniqueid = $_SESSION['kolab_auth_uniqueid']; if (!$uniqueid) { // Find user record in LDAP if (($ldap = self::ldap()) && $ldap->ready) { if ($record = $ldap->get_user_record($rcmail->get_user_name(), $_SESSION['kolab_host'])) { $uniqueid = $record['uniqueid']; } } } if ($uniqueid) { $uniqueid = md5($uniqueid); $rcmail->user->save_prefs(array('kolab_uniqueid' => $uniqueid)); } } // Set/update freebusy_session_auth entry if ($uniqueid && empty($_SESSION['kolab_auth_admin']) && ($ttl = $rcmail->config->get('freebusy_session_auth')) ) { if ($ttl === true) { $ttl = $rcmail->config->get('session_lifetime', 0) * 60; if (!$ttl) { $ttl = 10 * 60; } } $rcmail->config->set('freebusy_auth_cache', 'db'); $rcmail->config->set('freebusy_auth_cache_ttl', $ttl); if ($cache = $rcmail->get_cache_shared('freebusy_auth', false)) { $key = md5($uniqueid . ':' . rcube_utils::remote_addr() . ':' . $rcmail->get_user_name()); $value = $cache->get($key); $deadline = new DateTime('now', new DateTimeZone('UTC')); // We don't want to do the cache update on every request // do it once in a 1/10 of the ttl if ($value) { $value = new DateTime($value); $value->sub(new DateInterval('PT' . intval($ttl * 9/10) . 'S')); if ($value > $deadline) { return; } } $deadline->add(new DateInterval('PT' . $ttl . 'S')); $cache->set($key, $deadline->format(DateTime::ISO8601)); } } } /** * Startup hook handler */ public function startup($args) { // Check access rights when logged in as another user if (!empty($_SESSION['kolab_auth_admin']) && $args['task'] != 'login' && $args['task'] != 'logout') { // access to specified task is forbidden, // redirect to the first task on the list if (!empty($_SESSION['kolab_auth_allowed_tasks'])) { $tasks = (array)$_SESSION['kolab_auth_allowed_tasks']; if (!in_array($args['task'], $tasks) && !in_array('*', $tasks)) { header('Location: ?_task=' . array_shift($tasks)); die; } // add script that will remove disabled taskbar buttons if (!in_array('*', $tasks)) { $this->add_hook('render_page', array($this, 'render_page')); } } } // load per-user settings $this->load_user_role_plugins_and_settings(); return $args; } /** * Modify some configuration according to LDAP user record */ public function config_get($args) { // Replaces ldap_vars (%dc, etc) in public kolab ldap addressbooks // config based on the users base_dn. (for multi domain support) if ($args['name'] == 'ldap_public' && !empty($args['result'])) { $rcmail = rcube::get_instance(); $kolab_books = (array) $rcmail->config->get('kolab_auth_ldap_addressbooks'); foreach ($args['result'] as $name => $config) { if (in_array($name, $kolab_books) || in_array('*', $kolab_books)) { $args['result'][$name] = $this->patch_ldap_config($config); } } } else if ($args['name'] == 'kolab_users_directory' && !empty($args['result'])) { $args['result'] = $this->patch_ldap_config($args['result']); } return $args; } /** * Helper method to patch the given LDAP directory config with user-specific values */ protected function patch_ldap_config($config) { if (is_array($config)) { $config['base_dn'] = self::parse_ldap_vars($config['base_dn']); $config['search_base_dn'] = self::parse_ldap_vars($config['search_base_dn']); $config['bind_dn'] = str_replace('%dn', $_SESSION['kolab_dn'], $config['bind_dn']); if (!empty($config['groups'])) { $config['groups']['base_dn'] = self::parse_ldap_vars($config['groups']['base_dn']); } } return $config; } /** * Modifies list of plugins and settings according to * specified LDAP roles */ public function load_user_role_plugins_and_settings($startup = false) { if (empty($_SESSION['user_roledns'])) { return; } $rcmail = rcube::get_instance(); // Example 'kolab_auth_role_plugins' = // // Array( // '' => Array('plugin1', 'plugin2'), // ); // // NOTE that may in fact be something like: 'cn=role,%dc' $role_plugins = $rcmail->config->get('kolab_auth_role_plugins'); // Example $rcmail_config['kolab_auth_role_settings'] = // // Array( // '' => Array( // '$setting' => Array( // 'mode' => '(override|merge)', (default: override) // 'value' => <>, // 'allow_override' => (true|false) (default: false) // ), // ), // ); // // NOTE that may in fact be something like: 'cn=role,%dc' $role_settings = $rcmail->config->get('kolab_auth_role_settings'); if (!empty($role_plugins)) { foreach ($role_plugins as $role_dn => $plugins) { $role_dn = self::parse_ldap_vars($role_dn); if (!empty($role_plugins[$role_dn])) { $role_plugins[$role_dn] = array_unique(array_merge((array)$role_plugins[$role_dn], $plugins)); } else { $role_plugins[$role_dn] = $plugins; } } } if (!empty($role_settings)) { foreach ($role_settings as $role_dn => $settings) { $role_dn = self::parse_ldap_vars($role_dn); if (!empty($role_settings[$role_dn])) { $role_settings[$role_dn] = array_merge((array)$role_settings[$role_dn], $settings); } else { $role_settings[$role_dn] = $settings; } } } foreach ($_SESSION['user_roledns'] as $role_dn) { if (!empty($role_settings[$role_dn]) && is_array($role_settings[$role_dn])) { foreach ($role_settings[$role_dn] as $setting_name => $setting) { if (!isset($setting['mode'])) { $setting['mode'] = 'override'; } if ($setting['mode'] == "override") { $rcmail->config->set($setting_name, $setting['value']); } elseif ($setting['mode'] == "merge") { $orig_setting = $rcmail->config->get($setting_name); if (!empty($orig_setting)) { if (is_array($orig_setting)) { $rcmail->config->set($setting_name, array_merge($orig_setting, $setting['value'])); } } else { $rcmail->config->set($setting_name, $setting['value']); } } $dont_override = (array) $rcmail->config->get('dont_override'); if (empty($setting['allow_override'])) { $rcmail->config->set('dont_override', array_merge($dont_override, array($setting_name))); } else { if (in_array($setting_name, $dont_override)) { $_dont_override = array(); foreach ($dont_override as $_setting) { if ($_setting != $setting_name) { $_dont_override[] = $_setting; } } $rcmail->config->set('dont_override', $_dont_override); } } if ($setting_name == 'skin') { if ($rcmail->output->type == 'html') { $rcmail->output->set_skin($setting['value']); $rcmail->output->set_env('skin', $setting['value']); } } } } if (!empty($role_plugins[$role_dn])) { foreach ((array)$role_plugins[$role_dn] as $plugin) { $loaded = $this->api->load_plugin($plugin); // Some plugins e.g. kolab_2fa use 'startup' hook to // register other hooks, but when called on 'authenticate' hook // we're already after 'startup', so we'll call it directly if ($loaded && $startup && $plugin == 'kolab_2fa' && ($plugin = $this->api->get_plugin($plugin)) ) { $plugin->startup(array('task' => $rcmail->task, 'action' => $rcmail->action)); } } } } } /** * Logging method replacement to print debug/errors into * a separate (sub)folder for each user */ public function write_log($args) { $rcmail = rcube::get_instance(); if ($rcmail->config->get('log_driver') == 'syslog') { return $args; } // log_driver == 'file' is assumed here $log_dir = $rcmail->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs'); // Append original username + target username for audit-logging if ($rcmail->config->get('kolab_auth_auditlog') && !empty($_SESSION['kolab_auth_admin'])) { $args['dir'] = $log_dir . '/' . strtolower($_SESSION['kolab_auth_admin']) . '/' . strtolower($this->username); // Attempt to create the directory if (!is_dir($args['dir'])) { @mkdir($args['dir'], 0750, true); } } // Define the user log directory if a username is provided else if ($rcmail->config->get('per_user_logging') && !empty($this->username) && !stripos($log_dir, '/' . $this->username) // maybe already set by syncroton, skip ) { $user_log_dir = $log_dir . '/' . strtolower($this->username); if (is_writable($user_log_dir)) { $args['dir'] = $user_log_dir; } else if (!in_array($args['name'], array('errors', 'userlogins', 'sendmail'))) { $args['abort'] = true; // don't log if unauthenticed or no per-user log dir } } return $args; } /** * Sets defaults for new user. */ public function user_create($args) { if (!empty($this->data['user_email'])) { // addresses list is supported if (array_key_exists('email_list', $args)) { $email_list = array_unique($this->data['user_email']); // add organization to the list if (!empty($this->data['user_organization'])) { foreach ($email_list as $idx => $email) { $email_list[$idx] = array( 'organization' => $this->data['user_organization'], 'email' => $email, ); } } $args['email_list'] = $email_list; } else { $args['user_email'] = $this->data['user_email'][0]; } } if (!empty($this->data['user_name'])) { $args['user_name'] = $this->data['user_name']; } return $args; } /** * Modifies login form adding additional "Login As" field */ public function login_form($args) { $this->add_texts('localization/'); $rcmail = rcube::get_instance(); $admin_login = $rcmail->config->get('kolab_auth_admin_login'); $group = $rcmail->config->get('kolab_auth_group'); $role_attr = $rcmail->config->get('kolab_auth_role'); // Show "Login As" input if (empty($admin_login) || (empty($group) && empty($role_attr))) { return $args; } // Don't add the extra field on 2FA form if (strpos($args['content'], 'plugin.kolab-2fa-login')) { return $args; } $input = new html_inputfield(array('name' => '_loginas', 'id' => 'rcmloginas', 'type' => 'text', 'autocomplete' => 'off')); $row = html::tag('tr', null, html::tag('td', 'title', html::label('rcmloginas', rcube::Q($this->gettext('loginas')))) . html::tag('td', 'input', $input->show(trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST)))) ); // add icon style for Elastic $style = html::tag('style', [], '#login-form .input-group .icon.loginas::before { content: "\f508"; } '); $args['content'] = preg_replace('/<\/tbody>/i', $row . '' . $style, $args['content']); return $args; } /** * Find user credentials In LDAP. */ public function authenticate($args) { // get username and host $host = $args['host']; $user = $args['user']; $pass = $args['pass']; $loginas = trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST)); if (empty($user) || (empty($pass) && empty($_SERVER['REMOTE_USER']))) { $args['abort'] = true; return $args; } // temporarily set the current username to the one submitted $this->username = $user; $ldap = self::ldap(); if (!$ldap || !$ldap->ready) { self::log_login_error($user, "LDAP not ready"); $args['abort'] = true; $args['kolab_ldap_error'] = true; return $args; } // Find user record in LDAP $record = $ldap->get_user_record($user, $host); if (empty($record)) { self::log_login_error($user, "No user record found"); $args['abort'] = true; return $args; } $rcmail = rcube::get_instance(); $admin_login = $rcmail->config->get('kolab_auth_admin_login'); $admin_pass = $rcmail->config->get('kolab_auth_admin_password'); $login_attr = $rcmail->config->get('kolab_auth_login'); $name_attr = $rcmail->config->get('kolab_auth_name'); $email_attr = $rcmail->config->get('kolab_auth_email'); $org_attr = $rcmail->config->get('kolab_auth_organization'); $role_attr = $rcmail->config->get('kolab_auth_role'); $imap_attr = $rcmail->config->get('kolab_auth_mailhost'); if (!empty($role_attr) && !empty($record[$role_attr])) { $_SESSION['user_roledns'] = (array)($record[$role_attr]); } if (!empty($imap_attr) && !empty($record[$imap_attr])) { - $default_host = $rcmail->config->get('default_host'); - if (!empty($default_host)) { - rcube::write_log("errors", "Both default host and kolab_auth_mailhost set. Incompatible."); + $imap_host = $rcmail->config->get('imap_host', $rcmail->config->get('default_host')); + if (!empty($imap_host)) { + rcube::write_log("errors", "Both imap host and kolab_auth_mailhost set. Incompatible."); } else { $args['host'] = "tls://" . $record[$imap_attr]; } } // Login As... if (!empty($loginas) && $admin_login) { // Authenticate to LDAP $result = $ldap->bind($record['dn'], $pass); if (!$result) { self::log_login_error($user, "Unable to bind with '" . $record['dn'] . "'"); $args['abort'] = true; return $args; } $isadmin = false; $admin_rights = $rcmail->config->get('kolab_auth_admin_rights', array()); // @deprecated: fall-back to the old check if the original user has/belongs to administrative role/group if (empty($admin_rights)) { $group = $rcmail->config->get('kolab_auth_group'); $role_dn = $rcmail->config->get('kolab_auth_role_value'); // check role attribute if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) { $role_dn = $ldap->parse_vars($role_dn, $user, $host); if (in_array($role_dn, (array)$record[$role_attr])) { $isadmin = true; } } // check group if (!$isadmin && !empty($group)) { $groups = $ldap->get_user_groups($record['dn'], $user, $host); if (in_array($group, $groups)) { $isadmin = true; } } if ($isadmin) { // user has admin privileges privilage, get "login as" user credentials $target_entry = $ldap->get_user_record($loginas, $host); $allowed_tasks = $rcmail->config->get('kolab_auth_allowed_tasks'); } } else { // get "login as" user credentials $target_entry = $ldap->get_user_record($loginas, $host); if (!empty($target_entry)) { // get effective rights to determine login-as permissions $effective_rights = (array)$ldap->effective_rights($target_entry['dn']); if (!empty($effective_rights)) { // compat with out of date Net_LDAP3 $effective_rights = array_change_key_case($effective_rights, CASE_LOWER); $effective_rights['attrib'] = $effective_rights['attributelevelrights']; $effective_rights['entry'] = $effective_rights['entrylevelrights']; // compare the rights with the permissions mapping $allowed_tasks = array(); foreach ($admin_rights as $task => $perms) { $perms_ = explode(':', $perms); $type = array_shift($perms_); $req = array_pop($perms_); $attrib = array_pop($perms_); if (array_key_exists($type, $effective_rights)) { if ($type == 'entry' && in_array($req, $effective_rights[$type])) { $allowed_tasks[] = $task; } else if ($type == 'attrib' && array_key_exists($attrib, $effective_rights[$type]) && in_array($req, $effective_rights[$type][$attrib])) { $allowed_tasks[] = $task; } } } $isadmin = !empty($allowed_tasks); } } } // Save original user login for log (see below) if ($login_attr) { $origname = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr]; } else { $origname = $user; } if (!$isadmin || empty($target_entry)) { $this->add_texts('localization/'); $args['abort'] = true; $args['error'] = $this->gettext(array( 'name' => 'loginasnotallowed', 'vars' => array('user' => rcube::Q($loginas)), )); self::log_login_error($user, "No privileges to login as '" . $loginas . "'", $loginas); return $args; } // replace $record with target entry $record = $target_entry; $args['user'] = $this->username = $loginas; // Mark session to use SASL proxy for IMAP authentication $_SESSION['kolab_auth_admin'] = strtolower($origname); $_SESSION['kolab_auth_login'] = $rcmail->encrypt($admin_login); $_SESSION['kolab_auth_password'] = $rcmail->encrypt($admin_pass); $_SESSION['kolab_auth_allowed_tasks'] = $allowed_tasks; } // Store UID and DN of logged user in session for use by other plugins $_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid']; $_SESSION['kolab_dn'] = $record['dn']; // Store LDAP replacement variables used for current user // This improves performance of load_user_role_plugins_and_settings() // which is executed on every request (via startup hook) and where // we don't like to use LDAP (connection + bind + search) $_SESSION['kolab_auth_vars'] = $ldap->get_parse_vars(); // Store user unique identifier for freebusy_session_auth feature $_SESSION['kolab_auth_uniqueid'] = is_array($record['uniqueid']) ? $record['uniqueid'][0] : $record['uniqueid']; // Store also host as we need it for get_user_reacod() in 'ready' hook handler $_SESSION['kolab_host'] = $host; // Set user login if ($login_attr) { $this->data['user_login'] = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr]; } if ($this->data['user_login']) { $args['user'] = $this->username = $this->data['user_login']; } // User name for identity (first log in) foreach ((array)$name_attr as $field) { $name = is_array($record[$field] ?? null) ? $record[$field][0] : ($record[$field] ?? null); if (!empty($name)) { $this->data['user_name'] = $name; break; } } // User email(s) for identity (first log in) foreach ((array)$email_attr as $field) { $email = is_array($record[$field] ?? null) ? array_filter($record[$field]) : ($record[$field] ?? null); if (!empty($email)) { $this->data['user_email'] = array_merge((array)($this->data['user_email'] ?? null), (array)$email); } } // Organization name for identity (first log in) foreach ((array)$org_attr as $field) { $organization = is_array($record[$field] ?? null) ? $record[$field][0] : ($record[$field] ?? null); if (!empty($organization)) { $this->data['user_organization'] = $organization; break; } } // Log "Login As" usage if (!empty($origname)) { rcube::write_log('userlogins', sprintf('Admin login for %s by %s from %s', $args['user'], $origname, rcube_utils::remote_ip())); } // load per-user settings/plugins $this->load_user_role_plugins_and_settings(true); return $args; } /** * Set user DN for password change (password plugin with ldap_simple driver) */ public function password_ldap_bind($args) { $args['user_dn'] = $_SESSION['kolab_dn']; $rcmail = rcube::get_instance(); $rcmail->config->set('password_ldap_method', 'user'); return $args; } /** * Sets SASL Proxy login/password for IMAP and Managesieve auth */ public function imap_connect($args) { if (!empty($_SESSION['kolab_auth_admin'])) { $rcmail = rcube::get_instance(); $admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']); $admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']); $args['auth_cid'] = $admin_login; $args['auth_pw'] = $admin_pass; } return $args; } /** * Sets SASL Proxy login/password for SMTP auth */ public function smtp_connect($args) { if (!empty($_SESSION['kolab_auth_admin'])) { $rcmail = rcube::get_instance(); $admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']); $admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']); $args['smtp_auth_cid'] = $admin_login; $args['smtp_auth_pw'] = $admin_pass; } return $args; } /** * Hook to replace the plain text input field for email address by a drop-down list * with all email addresses (including aliases) from this user's LDAP record. */ public function identity_form($args) { $rcmail = rcube::get_instance(); $ident_level = intval($rcmail->config->get('identities_level', 0)); // do nothing if email address modification is disabled if ($ident_level == 1 || $ident_level == 3) { return $args; } $ldap = self::ldap(); if (!$ldap || !$ldap->ready || empty($_SESSION['kolab_dn'])) { return $args; } $emails = array(); $user_record = $ldap->get_record($_SESSION['kolab_dn']); foreach ((array)$rcmail->config->get('kolab_auth_email', array()) as $col) { $values = rcube_addressbook::get_col_values($col, $user_record, true); if (!empty($values)) $emails = array_merge($emails, array_filter($values)); } // kolab_delegation might want to modify this addresses list $plugin = $rcmail->plugins->exec_hook('kolab_auth_emails', array('emails' => $emails)); $emails = $plugin['emails']; if (!empty($emails)) { $args['form']['addressing']['content']['email'] = array( 'type' => 'select', 'options' => array_combine($emails, $emails), ); } return $args; } /** * Action executed before the page is rendered to add an onload script * that will remove all taskbar buttons for disabled tasks */ public function render_page($args) { $rcmail = rcube::get_instance(); $tasks = (array)$_SESSION['kolab_auth_allowed_tasks']; $tasks[] = 'logout'; // disable buttons in taskbar $script = " \$('a').filter(function() { var ev = \$(this).attr('onclick'); return ev && ev.match(/'switch-task','([a-z]+)'/) && \$.inArray(RegExp.\$1, " . json_encode($tasks) . ") < 0; }).remove(); "; $rcmail->output->add_script($script, 'docready'); } /** * Initializes LDAP object and connects to LDAP server */ public static function ldap() { self::$ldap = kolab_storage::ldap('kolab_auth_addressbook'); if (self::$ldap) { self::$ldap->extend_fieldmap(array('uniqueid' => 'nsuniqueid')); } return self::$ldap; } /** * Close LDAP connection */ public static function ldap_close() { if (self::$ldap) { self::$ldap->close(); self::$ldap = null; } } /** * Parses LDAP DN string with replacing supported variables. * See kolab_ldap::parse_vars() * * @param string $str LDAP DN string * * @return string Parsed DN string */ public static function parse_ldap_vars($str) { if (!empty($_SESSION['kolab_auth_vars'])) { $str = strtr($str, $_SESSION['kolab_auth_vars']); } return $str; } /** * Log failed logins * * @param string $username Username/Login * @param string $message Error message (failure reason) * @param string $login_as Username/Login of "login as" user */ public static function log_login_error($username, $message = null, $login_as = null) { $config = rcube::get_instance()->config; if ($config->get('log_logins')) { // don't fill the log with complete input, which could // have been prepared by a hacker if (strlen($username) > 256) { $username = substr($username, 0, 256) . '...'; } if (strlen($login_as) > 256) { $login_as = substr($login_as, 0, 256) . '...'; } if ($login_as) { $username = sprintf('%s (as user %s)', $username, $login_as); } // Don't log full session id for better security $session_id = session_id(); $session_id = $session_id ? substr($session_id, 0, 16) : 'no-session'; $message = sprintf( "Failed login for %s from %s in session %s %s", $username, rcube_utils::remote_ip(), $session_id, $message ? "($message)" : '' ); rcube::write_log('userlogins', $message); // disable log_logins to prevent from duplicate log entries $config->set('log_logins', false); } } } diff --git a/plugins/libkolab/bin/modcache.sh b/plugins/libkolab/bin/modcache.sh index fc87b1fa..220ae107 100755 --- a/plugins/libkolab/bin/modcache.sh +++ b/plugins/libkolab/bin/modcache.sh @@ -1,272 +1,272 @@ #!/usr/bin/env php * * Copyright (C) 2012-2014, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ define('INSTALL_PATH', __DIR__ . '/../../../'); ini_set('display_errors', 1); require_once INSTALL_PATH . 'program/include/clisetup.php'; function print_usage() { print "Usage: modcache.sh ACTION [OPTIONS] [USERNAME ARGS ...]\n"; print "Possible actions are: expunge, clear, prewarm\n"; print "-a, --all Clear/expunge all caches\n"; print "-h, --host IMAP host name\n"; print "-u, --user IMAP user name to authenticate\n"; print "-t, --type Object types to clear/expunge cache\n"; print "-l, --limit Limit the number of records to be expunged\n"; } // read arguments $opts = rcube_utils::get_opt(array( 'a' => 'all', 'h' => 'host', 'u' => 'user', 'p' => 'password', 't' => 'type', 'l' => 'limit', 'v' => 'verbose', )); $opts['username'] = !empty($opts[1]) ? $opts[1] : $opts['user']; $action = $opts[0]; $rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); // Make --host argument optional where the default_host is a simple string if (empty($opts['host'])) { $opts['host'] = imap_host(); } // connect to database $db = $rcmail->get_dbh(); $db->db_connect('w'); if (!$db->is_connected() || $db->is_error()) die("No DB connection\n"); ini_set('display_errors', 1); // All supported object types $all_types = array('contact','configuration','event','file','journal','note','task'); /* * Script controller */ switch (strtolower($action)) { /* * Clear/expunge all cache records */ case 'expunge': $folder_types = $opts['type'] ? explode(',', $opts['type']) : $all_types; $folder_types_db = array_map(array($db, 'quote'), $folder_types); $expire = strtotime(!empty($opts[2]) ? $opts[2] : 'now - 10 days'); $sql_where = "type IN (" . join(',', $folder_types_db) . ")"; if ($opts['username']) { $sql_where .= ' AND resource LIKE ?'; } $sql_query = "DELETE FROM %s WHERE folder_id IN (SELECT folder_id FROM kolab_folders WHERE $sql_where) AND created <= " . $db->quote(date('Y-m-d 00:00:00', $expire)); if ($opts['limit']) { $sql_query .= ' LIMIT ' . intval($opts['limit']); } foreach ($folder_types as $type) { $table_name = 'kolab_cache_' . $type; $db->query(sprintf($sql_query, $table_name), resource_prefix($opts).'%'); echo $db->affected_rows() . " records deleted from '$table_name'\n"; } $db->query("UPDATE kolab_folders SET ctag='' WHERE $sql_where", resource_prefix($opts).'%'); break; case 'clear': $folder_types = $opts['type'] ? explode(',', $opts['type']) : $all_types; $folder_types_db = array_map(array($db, 'quote'), $folder_types); if ($opts['all']) { $sql_query = "DELETE FROM kolab_folders WHERE 1"; } else if ($opts['username']) { $sql_query = "DELETE FROM kolab_folders WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?"; } if ($sql_query) { $db->query($sql_query, resource_prefix($opts).'%'); echo $db->affected_rows() . " records deleted from 'kolab_folders'\n"; } break; /* * Prewarm cache by synchronizing objects for the given user */ case 'prewarm': // make sure libkolab classes are loaded $rcmail->plugins->load_plugin('libkolab'); if (authenticate($opts)) { $folder_types = $opts['type'] ? explode(',', $opts['type']) : $all_types; foreach ($folder_types as $type) { // sync every folder of the given type foreach (kolab_storage::get_folders($type) as $folder) { echo "Synching " . $folder->name . " ($type) ... "; echo $folder->count($type) . "\n"; // also sync distribution lists in contact folders if ($type == 'contact') { echo "Synching " . $folder->name . " (distribution-list) ... "; echo $folder->count('distribution-list') . "\n"; } } } } else die("Authentication failed for " . $opts['user']); break; /** * Update the cache meta columns from the serialized/xml data * (might be run after a schema update) */ case 'update': // make sure libkolab classes are loaded $rcmail->plugins->load_plugin('libkolab'); $folder_types = $opts['type'] ? explode(',', $opts['type']) : $all_types; foreach ($folder_types as $type) { $class = 'kolab_storage_cache_' . $type; $sql_result = $db->query("SELECT folder_id FROM kolab_folders WHERE type=? AND synclock = 0", $type); while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) { $folder = new $class; $folder->select_by_id($sql_arr['folder_id']); echo "Updating " . $sql_arr['folder_id'] . " ($type) "; foreach ($folder->select() as $object) { $object['_formatobj']->to_array(); // load data $folder->save($object['_msguid'], $object, $object['_msguid']); echo "."; } echo "done.\n"; } } break; /* * Unknown action => show usage */ default: print_usage(); exit; } /** * Compose cache resource URI prefix for the given user credentials */ function resource_prefix($opts) { return 'imap://' . str_replace('%', '\\%', urlencode($opts['username'])) . '@' . $opts['host'] . '/'; } /** * Authenticate to the IMAP server with the given user credentials */ function authenticate(&$opts) { global $rcmail; // prompt for password if (empty($opts['password']) && ($opts['username'] || $opts['user'])) { $opts['password'] = rcube_utils::prompt_silent("Password: "); } // simulate "login as" feature if ($opts['user'] && $opts['user'] != $opts['username']) $_POST['_loginas'] = $opts['username']; else if (empty($opts['user'])) $opts['user'] = $opts['username']; // parse $host URL $url = parse_url(trim($opts['host'])); if (!empty($url['host'])) { $imap_host = $url['host']; $imap_ssl = isset($url['scheme']) && in_array($url['scheme'], array('ssl','imaps','tls')) ? $url['scheme'] : false; $imap_port = isset($url['port']) ? $url['port'] : ($imap_ssl && $imap_ssl != 'tls' ? 993 : 143); } else { $imap_host = trim($opts['host']); $imap_port = 143; $imap_ssl = false; } // let the kolab_auth plugin do its magic $auth = $rcmail->plugins->exec_hook('authenticate', array( 'host' => $imap_host, 'user' => trim($opts['user']), 'pass' => $opts['password'], 'cookiecheck' => false, 'valid' => !empty($opts['user']) && !empty($opts['host']), )); if ($auth['valid']) { $storage = $rcmail->get_storage(); if ($storage->connect($imap_host, $auth['user'], $auth['pass'], $imap_port, $imap_ssl)) { if ($opts['verbose']) echo "IMAP login succeeded.\n"; if (($user = rcube_user::query($opts['username'], $auth['host'])) && $user->ID) $rcmail->user = $user; } else die("Login to IMAP server failed!\n"); } else { die("Invalid login credentials!\n"); } return $auth['valid']; } function imap_host() { global $rcmail; - $default_host = $rcmail->config->get('default_host'); + $default_host = $rcmail->config->get('imap_host', $rcmail->config->get('default_host')); if (is_array($default_host)) { $key = key($default_host); $imap_host = is_numeric($key) ? $default_host[$key] : $key; } else { $imap_host = $default_host; } // strip protocol prefix $uri = parse_url($imap_host); if (!empty($uri['host'])) { return $uri['host']; } } diff --git a/plugins/libkolab/bin/randomcontacts.sh b/plugins/libkolab/bin/randomcontacts.sh index dc2bbb52..4fe9031b 100755 --- a/plugins/libkolab/bin/randomcontacts.sh +++ b/plugins/libkolab/bin/randomcontacts.sh @@ -1,196 +1,196 @@ #!/usr/bin/env php * * Copyright (C) 2014, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ define('INSTALL_PATH', __DIR__ . '/../../../'); ini_set('display_errors', 1); require_once INSTALL_PATH . 'program/include/clisetup.php'; function print_usage() { print "Usage: randomcontacts.sh [OPTIONS] USERNAME FOLDER\n"; print "Create random contact for a given user in a specified folder.\n"; print "-n, --num Number of contacts to be created, defaults to 50\n"; print "-h, --host IMAP host name\n"; print "-p, --password IMAP user password\n"; } // read arguments $opts = rcube_utils::get_opt(array( 'n' => 'num', 'h' => 'host', 'u' => 'user', 'p' => 'pass', 'v' => 'verbose', )); $opts['username'] = !empty($opts[0]) ? $opts[0] : $opts['user']; $opts['folder'] = $opts[1]; $rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); $rcmail->plugins->load_plugins(array('libkolab')); ini_set('display_errors', 1); if (empty($opts['host'])) { $opts['host'] = imap_host(); } if (empty($opts['username']) || empty($opts['folder']) || empty($opts['host'])) { print_usage(); exit; } // prompt for password if (empty($opts['pass'])) { $opts['pass'] = rcube_utils::prompt_silent("Password: "); } // parse $host URL $a_host = parse_url($opts['host']); if ($a_host['host']) { $host = $a_host['host']; $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? TRUE : FALSE; $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143); } else { $host = $opts['host']; $imap_port = 143; } // instantiate IMAP class $IMAP = $rcmail->get_storage(); // try to connect to IMAP server if ($IMAP->connect($host, $opts['username'], $opts['pass'], $imap_port, $imap_ssl)) { print "IMAP login successful.\n"; $user = rcube_user::query($opts['username'], $host); $rcmail->user = $user ?: new rcube_user(null, array('username' => $opts['username'], 'host' => $host)); } else { die("IMAP login failed for user " . $opts['username'] . " @ $host\n"); } // get contacts folder $folder = kolab_storage::get_folder($opts['folder']); if (!$folder || empty($folder->type)) { die("Invalid Address Book " . $opts['folder'] . "\n"); } $format = new kolab_format_contact; $num = $opts['num'] ? intval($opts['num']) : 50; echo "Creating $num contacts in " . $folder->get_resource_uri() . "\n"; for ($i=0; $i < $num; $i++) { // generate random names $contact = array( 'surname' => random_string(rand(1,2)), 'firstname' => random_string(rand(1,2)), 'organization' => random_string(rand(0,2)), 'profession' => random_string(rand(1,2)), 'email' => array(), 'phone' => array(), 'address' => array(), 'notes' => random_string(rand(10,200)), ); // randomly add email addresses $em = rand(1,3); for ($e=0; $e < $em; $e++) { $type = array_rand($format->emailtypes); $contact['email'][] = array( 'address' => strtolower(random_string(1) . '@' . random_string(1) . '.tld'), 'type' => $type, ); } // randomly add phone numbers $ph = rand(1,4); for ($p=0; $p < $ph; $p++) { $type = array_rand($format->phonetypes); $contact['phone'][] = array( 'number' => '+'.rand(2,8).rand(1,9).rand(1,9).rand(0,9).rand(0,9).rand(0,9).rand(0,9).rand(0,9).rand(0,9).rand(0,9).rand(0,9), 'type' => $type, ); } // randomly add addresses $ad = rand(0,2); for ($a=0; $a < $ad; $a++) { $type = array_rand($format->addresstypes); $contact['address'][] = array( 'street' => random_string(rand(1,3)), 'locality' => random_string(rand(1,2)), 'code' => rand(1000, 89999), 'country' => random_string(1), 'type' => $type, ); } $contact['name'] = $contact['firstname'] . ' ' . $contact['surname']; if ($folder->save($contact, 'contact')) { echo "."; } else { echo "x"; break; // abort on error } } echo " done.\n"; function random_string($len) { $words = explode(" ", "The Hough transform is named after Paul Hough who patented the method in 1962. It is a technique which can be used to isolate features of a particular shape within an image. Because it requires that the desired features be specified in some parametric form, the classical Hough transform is most commonly used for the de- tection of regular curves such as lines, circles, ellipses, etc. A generalized Hough transform can be employed in applications where a simple analytic description of a features is not possible. Due to the computational complexity of the generalized Hough algorithm, we restrict the main focus of this discussion to the classical Hough transform. Despite its domain restrictions, the classical Hough transform hereafter referred to without the classical prefix retains many applications, as most manufac- tured parts and many anatomical parts investigated in medical imagery contain feature boundaries which can be described by regular curves. The main advantage of the Hough transform technique is that it is tolerant of gaps in feature boundary descriptions and is relatively unaffected by image noise."); for ($i = 0; $i < $len; $i++) { $str .= $words[rand(0,count($words)-1)] . " "; } return rtrim($str); } function imap_host() { global $rcmail; - $default_host = $rcmail->config->get('default_host'); + $default_host = $rcmail->config->get('imap_host', $rcmail->config->get('default_host')); if (is_array($default_host)) { $key = key($default_host); $imap_host = is_numeric($key) ? $default_host[$key] : $key; } else { $imap_host = $default_host; } // strip protocol prefix $uri = parse_url($imap_host); if (!empty($uri['host'])) { return $uri['host']; } } diff --git a/plugins/libkolab/bin/readcache.sh b/plugins/libkolab/bin/readcache.sh index ca988bd5..765b1c39 100755 --- a/plugins/libkolab/bin/readcache.sh +++ b/plugins/libkolab/bin/readcache.sh @@ -1,158 +1,157 @@ #!/usr/bin/env php * * Copyright (C) 2014, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ define('INSTALL_PATH', __DIR__ . '/../../../'); ini_set('display_errors', 1); libxml_use_internal_errors(true); require_once INSTALL_PATH . 'program/include/clisetup.php'; function print_usage() { print "Usage: readcache.sh [OPTIONS] FOLDER\n"; print "-h, --host IMAP host name\n"; print "-l, --limit Limit the number of records to be listed\n"; } // read arguments $opts = rcube_utils::get_opt(array( 'h' => 'host', 'l' => 'limit', 'v' => 'verbose', )); $folder = $opts[0]; $imap_host = $opts['host']; $rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); if (empty($imap_host)) { $imap_host = imap_host(); } if (empty($folder) || empty($imap_host)) { print_usage(); exit; } // connect to database $db = $rcmail->get_dbh(); $db->db_connect('r'); if (!$db->is_connected() || $db->is_error()) { die("No DB connection\n"); } // resolve folder_id if (!is_numeric($folder)) { if (strpos($folder, '@')) { list($mailbox, $domain) = explode('@', $folder); list($username, $subpath) = explode('/', preg_replace('!^user/!', '', $mailbox), 2); $folder_uri = 'imap://' . urlencode($username.'@'.$domain) . '@' . $imap_host . '/' . $subpath; } else { die("Invalid mailbox identifier! Example: user/john.doe/Calendar@example.org\n"); } print "Resolving folder $folder_uri..."; $sql_result = $db->query('SELECT * FROM `kolab_folders` WHERE `resource`=?', $folder_uri); if ($sql_result && ($folder_data = $db->fetch_assoc($sql_result))) { $folder_id = $folder_data['folder_id']; print $folder_id; } print "\n"; } else { $folder_id = intval($folder); $sql_result = $db->query('SELECT * FROM `kolab_folders` WHERE `folder_id`=?', $folder_id); if ($sql_result) { $folder_data = $db->fetch_assoc($sql_result); } } if (empty($folder_data)) { die("Can't find cache mailbox for '$folder'\n"); } print "Querying cache for folder $folder_id ($folder_data[type])...\n"; $extra_cols = array( 'event' => array('dtstart','dtend'), 'contact' => array('type'), ); $cache_table = $db->table_name('kolab_cache_' . $folder_data['type']); $extra_cols_ = $extra_cols[$folder_data['type']] ?: array(); $sql_arr = $db->fetch_assoc($db->query("SELECT COUNT(*) as cnt FROM `$cache_table` WHERE `folder_id`=?", intval($folder_id))); print "CTag = " . $folder_data['ctag'] . "\n"; print "Lock = " . $folder_data['synclock'] . "\n"; print "Changed = " . $folder_data['changed'] . "\n"; -print "ObjCount = " . $folder_data['objectcount'] . "\n"; print "Count = " . $sql_arr['cnt'] . "\n"; print "----------------------------------------------------------------------------------\n"; print "\t\t\t\t"; print join("\t", array_map(function($c) { return '<' . strtoupper($c) . '>'; }, $extra_cols_)); print "\n----------------------------------------------------------------------------------\n"; $result = $db->limitquery("SELECT * FROM `$cache_table` WHERE `folder_id`=?", 0, $opts['limit'], intval($folder_id)); while ($result && ($sql_arr = $db->fetch_assoc($result))) { print $sql_arr['msguid'] . "\t" . $sql_arr['uid'] . "\t" . $sql_arr['changed']; // try to unserialize data block $object = json_decode($sql_arr['data']); print "\t" . ($object === false ? 'FAIL!' : 'OK'); // print extra cols array_walk($extra_cols_, function($c) use ($sql_arr) { print "\t" . $sql_arr[$c]; }); print "\n"; } print "----------------------------------------------------------------------------------\n"; echo "Done.\n"; function imap_host() { global $rcmail; - $default_host = $rcmail->config->get('default_host'); + $default_host = $rcmail->config->get('imap_host', $rcmail->config->get('default_host')); if (is_array($default_host)) { $key = key($default_host); $imap_host = is_numeric($key) ? $default_host[$key] : $key; } else { $imap_host = $default_host; } // strip protocol prefix $uri = parse_url($imap_host); if (!empty($uri['host'])) { return $uri['host']; } }