Page MenuHomePhorge

No OneTemporary

Size
48 KB
Referenced Files
None
Subscribers
None
diff --git a/plugins/kolab_delegation/kolab_delegation.php b/plugins/kolab_delegation/kolab_delegation.php
index 8b4a5ef9..fb3e4be1 100644
--- a/plugins/kolab_delegation/kolab_delegation.php
+++ b/plugins/kolab_delegation/kolab_delegation.php
@@ -1,544 +1,544 @@
<?php
/**
* Delegation configuration utility for Kolab accounts
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
class kolab_delegation extends rcube_plugin
{
public $task = 'login|mail|settings|calendar';
private $rc;
private $engine;
/**
* Plugin initialization.
*/
public function init()
{
$this->rc = rcube::get_instance();
$this->require_plugin('libkolab');
$this->require_plugin('kolab_auth');
// on-login delegation initialization
$this->add_hook('login_after', array($this, 'login_hook'));
// on-check-recent delegation support
$this->add_hook('check_recent', array($this, 'check_recent_hook'));
// on-message-send delegation support
$this->add_hook('message_before_send', array($this, 'message_before_send'));
// delegation support in Calendar plugin
$this->add_hook('message_load', array($this, 'message_load'));
$this->add_hook('calendar_user_emails', array($this, 'calendar_user_emails'));
$this->add_hook('calendar_list_filter', array($this, 'calendar_list_filter'));
$this->add_hook('calendar_load_itip', array($this, 'calendar_load_itip'));
// delegation support in kolab_auth plugin
$this->add_hook('kolab_auth_emails', array($this, 'kolab_auth_emails'));
if ($this->rc->task == 'settings') {
// delegation management interface
$this->register_action('plugin.delegation', array($this, 'controller_ui'));
$this->register_action('plugin.delegation-delete', array($this, 'controller_action'));
$this->register_action('plugin.delegation-save', array($this, 'controller_action'));
$this->register_action('plugin.delegation-autocomplete', array($this, 'controller_action'));
$this->add_hook('settings_actions', array($this, 'settings_actions'));
if ($this->rc->action == 'plugin.delegation' || empty($_REQUEST['_framed'])) {
$this->add_texts('localization/', array('deleteconfirm', 'savingdata', 'yes', 'no'));
if ($this->rc->action == 'plugin.delegation') {
$this->include_script('kolab_delegation.js');
}
$this->skin_path = $this->local_skin_path();
$this->include_stylesheet($this->skin_path . '/style.css');
}
}
// Calendar plugin UI bindings
else if ($this->rc->task == 'calendar' && empty($_REQUEST['_framed'])) {
$this->calendar_ui();
}
}
/**
* Adds Delegation section in Settings
*/
function settings_actions($args)
{
$args['actions'][] = array(
'action' => 'plugin.delegation',
'class' => 'delegation',
'label' => 'tabtitle',
'domain' => 'kolab_delegation',
);
return $args;
}
/**
* Engine object getter
*/
private function engine()
{
if (!$this->engine) {
require_once $this->home . '/kolab_delegation_engine.php';
$this->load_config();
$this->engine = new kolab_delegation_engine();
}
return $this->engine;
}
/**
* On-login action
*/
public function login_hook($args)
{
// Manage (create) identities for delegator's email addresses
// and subscribe to delegator's folders. Also remove identities
// after delegation is removed
$engine = $this->engine();
$engine->delegation_init();
return $args;
}
/**
* Check-recent action
*/
public function check_recent_hook($args)
{
// Checking for new messages shall be extended to Inbox folders of all
// delegators if 'check_all_folders' is set to false.
if ($this->rc->task != 'mail') {
return $args;
}
if (!empty($args['all'])) {
return $args;
}
if (empty($_SESSION['delegators'])) {
return $args;
}
$storage = $this->rc->get_storage();
$other_ns = $storage->get_namespace('other');
$folders = $storage->list_folders_subscribed('', '*', 'mail');
foreach (array_keys($_SESSION['delegators']) as $uid) {
foreach ($other_ns as $ns) {
$folder = $ns[0] . $uid;
if (in_array($folder, $folders) && !in_array($folder, $args['folders'])) {
$args['folders'][] = $folder;
}
}
}
return $args;
}
/**
* Mail send action
*/
public function message_before_send($args)
{
// Checking headers of email being send, we'll add
// Sender: header if mail is send on behalf of someone else
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_delivery_filter($args);
}
return $args;
}
/**
* E-mail message loading action
*/
public function message_load($args)
{
// This is a place where we detect delegate context
// So we can handle event invitations on behalf of delegator
// @TODO: should we do this only in delegators' folders?
// skip invalid messages or Kolab objects (for better performance)
if (empty($args['object']->headers) || $args['object']->headers->get('x-kolab-type', false)) {
return $args;
}
$engine = $this->engine();
$context = $engine->delegator_context_from_message($args['object']);
if ($context) {
$this->rc->output->set_env('delegator_context', $context);
$this->include_script('kolab_delegation.js');
}
return $args;
}
/**
* calendar::get_user_emails() handler
*/
public function calendar_user_emails($args)
{
// In delegator context we'll use delegator's addresses
// instead of current user addresses
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_emails_filter($args);
}
return $args;
}
/**
* calendar_driver::list_calendars() handler
*/
public function calendar_list_filter($args)
{
// In delegator context we'll use delegator's folders
// instead of current user folders
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_folder_filter($args);
}
return $args;
}
/**
* calendar::load_itip() handler
*/
public function calendar_load_itip($args)
{
// In delegator context we'll use delegator's address/name
// for invitation responses
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_identity_filter($args);
}
return $args;
}
/**
* Delegation support in Calendar plugin UI
*/
public function calendar_ui()
{
// Initialize handling of delegators' identities in event form
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$this->rc->output->set_env('namespace', $engine->namespace_js());
$this->rc->output->set_env('delegators', $engine->list_delegators_js());
$this->include_script('kolab_delegation.js');
}
}
/**
* Delegation support in kolab_auth plugin
*/
public function kolab_auth_emails($args)
{
// Add delegators addresses to address selector in user identity form
if (!empty($_SESSION['delegators'])) {
// @TODO: Consider not adding all delegator addresses to the list.
// Instead add only address of currently edited identity
foreach ($_SESSION['delegators'] as $emails) {
$args['emails'] = array_merge($args['emails'], $emails);
}
$args['emails'] = array_unique($args['emails']);
sort($args['emails']);
}
return $args;
}
/**
* Delegation UI handler
*/
public function controller_ui()
{
// main interface (delegates list)
if (empty($_REQUEST['_framed'])) {
$this->register_handler('plugin.delegatelist', array($this, 'delegate_list'));
$this->rc->output->include_script('list.js');
$this->rc->output->send('kolab_delegation.settings');
}
// delegate frame
else {
$this->register_handler('plugin.delegateform', array($this, 'delegate_form'));
$this->register_handler('plugin.delegatefolders', array($this, 'delegate_folders'));
$this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15));
$this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length'));
$this->rc->output->add_label('autocompletechars', 'autocompletemore');
$this->rc->output->send('kolab_delegation.editform');
}
}
/**
* Delegation action handler
*/
public function controller_action()
{
- $this->add_texts('localization/', true);
+ $this->add_texts('localization/');
$engine = $this->engine();
// Delegate delete
if ($this->rc->action == 'plugin.delegation-delete') {
$id = get_input_value('id', RCUBE_INPUT_GPC);
$success = $engine->delegate_delete($id, (bool) get_input_value('acl', RCUBE_INPUT_GPC));
if ($success) {
$this->rc->output->show_message($this->gettext('deletesuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array('deleted' => $id));
}
else {
$this->rc->output->show_message($this->gettext('deleteerror'), 'error');
}
}
// Delegate add/update
else if ($this->rc->action == 'plugin.delegation-save') {
$id = get_input_value('id', RCUBE_INPUT_GPC);
$acl = get_input_value('folders', RCUBE_INPUT_GPC);
// update
if ($id) {
$delegate = $engine->delegate_get($id);
$success = $engine->delegate_acl_update($delegate['uid'], $acl);
if ($success) {
$this->rc->output->show_message($this->gettext('updatesuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array('updated' => $id));
}
else {
$this->rc->output->show_message($this->gettext('updateerror'), 'error');
}
}
// new
else {
$login = get_input_value('newid', RCUBE_INPUT_GPC);
$delegate = $engine->delegate_get_by_name($login);
$success = $engine->delegate_add($delegate, $acl);
if ($success) {
$this->rc->output->show_message($this->gettext('createsuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array(
'created' => $delegate['ID'],
'name' => $delegate['name'],
));
}
else {
$this->rc->output->show_message($this->gettext('createerror'), 'error');
}
}
}
// Delegate autocompletion
else if ($this->rc->action == 'plugin.delegation-autocomplete') {
$search = get_input_value('_search', RCUBE_INPUT_GPC, true);
- $sid = get_input_value('_id', RCUBE_INPUT_GPC);
+ $reqid = get_input_value('_reqid', RCUBE_INPUT_GPC);
$users = $engine->list_users($search);
- $this->rc->output->command('ksearch_query_results', $users, $search, $sid);
+ $this->rc->output->command('ksearch_query_results', $users, $search, $reqid);
}
$this->rc->output->send();
}
/**
* Template object of delegates list
*/
public function delegate_list($attrib = array())
{
$attrib += array('id' => 'delegate-list');
$engine = $this->engine();
$list = $engine->list_delegates();
$table = new html_table();
// sort delegates list
asort($list, SORT_LOCALE_STRING);
foreach ($list as $id => $delegate) {
$table->add_row(array('id' => 'rcmrow' . $id));
$table->add(null, Q($delegate));
}
$this->rc->output->add_gui_object('delegatelist', $attrib['id']);
$this->rc->output->set_env('delegatecount', count($list));
return $table->show($attrib);
}
/**
* Template object of delegate form
*/
public function delegate_form($attrib = array())
{
$engine = $this->engine();
$table = new html_table(array('cols' => 2));
$id = get_input_value('_id', RCUBE_INPUT_GPC);
$field_id = 'delegate';
if ($id) {
$delegate = $engine->delegate_get($id);
}
if ($delegate) {
$input = new html_hiddenfield(array('name' => $field_id, 'id' => $field_id, 'size' => 40));
$input = Q($delegate['name']) . $input->show($id);
$this->rc->output->set_env('active_delegate', $id);
$this->rc->output->command('parent.enable_command','delegate-delete', true);
}
else {
$input = new html_inputfield(array('name' => $field_id, 'id' => $field_id, 'size' => 40));
$input = $input->show();
}
$table->add('title', html::label($field_id, $this->gettext('delegate')));
$table->add(null, $input);
if ($attrib['form']) {
$this->rc->output->add_gui_object('editform', $attrib['form']);
}
return $table->show($attrib);
}
/**
* Template object of folders list
*/
public function delegate_folders($attrib = array())
{
if (!$attrib['id']) {
$attrib['id'] = 'delegatefolders';
}
$engine = $this->engine();
$id = get_input_value('_id', RCUBE_INPUT_GPC);
if ($id) {
$delegate = $engine->delegate_get($id);
}
$folder_data = $engine->list_folders($delegate['uid']);
$rights = array();
$folder_groups = array();
foreach ($folder_data as $folder_name => $folder) {
$folder_groups[$folder['type']][] = $folder_name;
$rights[$folder_name] = $folder['rights'];
}
// build block for every folder type
foreach ($folder_groups as $type => $group) {
if (empty($group)) {
continue;
}
$attrib['type'] = $type;
$html .= html::div('foldersblock',
html::tag('h3', $type, $this->gettext($type)) .
$this->delegate_folders_block($group, $attrib, $rights));
}
$this->rc->output->add_gui_object('folderslist', $attrib['id']);
return html::div($attrib, $html);
}
/**
* List of folders in specified group
*/
private function delegate_folders_block($a_folders, $attrib, $rights)
{
$path = 'plugins/kolab_delegation/' . $this->skin_path . '/';
$read_ico = $attrib['readicon'] ? html::img(array('src' => $path . $attrib['readicon'], 'title' => $this->gettext('read'))) : '';
$write_ico = $attrib['writeicon'] ? html::img(array('src' => $path . $attrib['writeicon'], 'title' => $this->gettext('write'))) : '';
$table = new html_table(array('cellspacing' => 0));
$table->add_header('read', $read_ico);
$table->add_header('write', $write_ico);
$table->add_header('foldername', $this->rc->gettext('folder'));
$checkbox_read = new html_checkbox(array('name' => 'read[]', 'class' => 'read'));
$checkbox_write = new html_checkbox(array('name' => 'write[]', 'class' => 'write'));
$names = array();
foreach ($a_folders as $folder) {
$foldername = $origname = preg_replace('/^INBOX &raquo;\s+/', '', kolab_storage::object_name($folder));
// find folder prefix to truncate (the same code as in kolab_addressbook plugin)
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($foldername, $names[$i].' &raquo; ') === 0) {
$length = strlen($names[$i].' &raquo; ');
$prefix = substr($foldername, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$foldername = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($foldername, $length);
break;
}
}
$folder_id = 'rcmf' . html_identifier($folder);
$names[] = $origname;
$classes = array('mailbox');
if ($folder_class = $this->rc->folder_classname($folder)) {
$foldername = html::quote($this->rc->gettext($folder_class));
$classes[] = $folder_class;
}
$table->add_row();
$table->add('read', $checkbox_read->show(
$rights[$folder] >= kolab_delegation_engine::ACL_READ ? $folder : null,
array('value' => $folder)));
$table->add('write', $checkbox_write->show(
$rights[$folder] >= kolab_delegation_engine::ACL_WRITE ? $folder : null,
array('value' => $folder, 'id' => $folder_id)));
$table->add(join(' ', $classes), html::label($folder_id, $foldername));
}
return $table->show();
}
}
diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php
index ef869f29..ad96831b 100644
--- a/plugins/kolab_delegation/kolab_delegation_engine.php
+++ b/plugins/kolab_delegation/kolab_delegation_engine.php
@@ -1,929 +1,934 @@
<?php
/**
* Kolab Delegation Engine
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
class kolab_delegation_engine
{
public $context;
private $rc;
private $ldap_filter;
private $ldap_delegate_field;
private $ldap_login_field;
private $ldap_name_field;
private $ldap_email_field;
private $ldap_org_field;
private $ldap_dn;
private $cache = array();
private $folder_types = array('mail', 'event', 'task');
const ACL_READ = 1;
const ACL_WRITE = 2;
/**
* Class constructor
*/
public function __construct()
{
$this->rc = rcube::get_instance();
}
/**
* Add delegate
*
* @param string|array $delegate Delegate DN (encoded) or delegate data (result of delegate_get())
* @param array $acl List of folder->right map
*/
public function delegate_add($delegate, $acl)
{
if (!is_array($delegate)) {
$delegate = $this->delegate_get($delegate);
}
$dn = $delegate['ID'];
if (empty($delegate) || empty($dn)) {
return false;
}
$list = $this->list_delegates();
$user = $this->user();
// add delegate to the list
$list = array_keys((array)$list);
$list = array_filter($list);
if (!in_array($dn, $list)) {
$list[] = $dn;
}
$list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
// update user record
$result = $this->user_update_delegates($list);
// Set ACL on folders
if ($result && !empty($acl)) {
$this->delegate_acl_update($delegate['uid'], $acl);
}
return $result;
}
/**
* Set/Update ACL on delegator's folders
*
* @param string $uid Delegate authentication identifier
* @param array $acl List of folder->right map
* @param bool $update Update (remove) old rights
*/
public function delegate_acl_update($uid, $acl, $update = false)
{
$storage = $this->rc->get_storage();
$right_types = $this->right_types();
$folders = $update ? $this->list_folders($uid) : array();
foreach ($acl as $folder_name => $rights) {
$r = $right_types[$rights];
if ($r) {
$storage->set_acl($folder_name, $uid, $r);
}
if (!empty($folders) && isset($folders[$folder_name])) {
unset($folders[$folder_name]);
}
}
foreach ($folders as $folder_name => $folder) {
if ($folder['rights']) {
$storage->delete_acl($folder_name, $uid);
}
}
return true;
}
/**
* Delete delgate
*
* @param string $dn Delegate DN (encoded)
* @param bool $acl_del Enable ACL deletion on delegator folders
*/
public function delegate_delete($dn, $acl_del = false)
{
$delegate = $this->delegate_get($dn);
$list = $this->list_delegates();
$user = $this->user();
if (empty($delegate) || !isset($list[$dn])) {
return false;
}
// remove delegate from the list
unset($list[$dn]);
$list = array_keys($list);
$list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
$user[$this->ldap_delegate_field] = $list;
// update user record
$result = $this->user_update_delegates($list);
// remove ACL
if ($result && $acl_del) {
$this->delegate_acl_update($delegate['uid'], array(), true);
}
return $result;
}
/**
* Return delegate data
*
* @param string $dn Delegate DN (encoded)
*
* @return array Delegate record (ID, name, uid, imap_uid)
*/
public function delegate_get($dn)
{
// use internal cache so we not query LDAP more than once per request
if (!isset($this->cache[$dn])) {
$ldap = $this->ldap();
if (!$ldap || empty($dn)) {
return array();
}
// Get delegate
$user = $ldap->get_record(kolab_auth_ldap::dn_decode($dn));
if (empty($user)) {
return array();
}
$delegate = $this->parse_ldap_record($user);
$delegate['ID'] = $dn;
$this->cache[$dn] = $delegate;
}
return $this->cache[$dn];
}
/**
* Return delegate data
*
* @param string $login Delegate name (the 'uid' returned in get_users())
*
* @return array Delegate record (ID, name, uid, imap_uid)
*/
public function delegate_get_by_name($login)
{
$ldap = $this->ldap();
if (!$ldap || empty($login)) {
return array();
}
$list = $ldap->search($this->ldap_login_field, $login, 1);
if (count($list) == 1) {
$dn = key($list);
$user = $list[$dn];
return $this->parse_ldap_record($user, $dn);
}
}
/**
* LDAP object getter
*/
private function ldap()
{
$ldap = kolab_auth::ldap();
if (!$ldap || !$ldap->ready) {
return null;
}
// Default filter of LDAP queries
$this->ldap_filter = $this->rc->config->get('kolab_delegation_filter', '(objectClass=kolabInetOrgPerson)');
// Name of the LDAP field for delegates list
$this->ldap_delegate_field = $this->rc->config->get('kolab_delegation_delegate_field', 'kolabDelegate');
// Encoded LDAP DN of current user, set on login by kolab_auth plugin
$this->ldap_dn = $_SESSION['kolab_dn'];
// Name of the LDAP field with authentication ID
$this->ldap_login_field = $this->rc->config->get('kolab_auth_login');
// Name of the LDAP field with user name used for identities
$this->ldap_name_field = $this->rc->config->get('kolab_auth_name');
// Name of the LDAP field with email addresses used for identities
$this->ldap_email_field = $this->rc->config->get('kolab_auth_email');
// Name of the LDAP field with organization name for identities
$this->ldap_org_field = $this->rc->config->get('kolab_auth_organization');
$ldap->set_filter($this->ldap_filter);
return $ldap;
}
/**
* List current user delegates
*/
public function list_delegates()
{
$result = array();
$ldap = $this->ldap();
$user = $this->user();
if (empty($ldap) || empty($user)) {
return array();
}
// Get delegates of current user
$delegates = $user[$this->ldap_delegate_field];
if (!empty($delegates)) {
foreach ((array)$delegates as $dn) {
$delegate = $ldap->get_record($dn);
$data = $this->parse_ldap_record($delegate, $dn);
if (!empty($data) && !empty($data['name'])) {
$result[$data['ID']] = $data['name'];
}
}
}
return $result;
}
/**
* List current user delegators
*
* @return array List of delegators
*/
public function list_delegators()
{
$result = array();
$ldap = $this->ldap();
if (empty($ldap) || empty($this->ldap_dn)) {
return array();
}
$list = $ldap->search($this->ldap_delegate_field, $this->ldap_dn, 1);
foreach ($list as $dn => $delegator) {
$delegator = $this->parse_ldap_record($delegator, $dn);
$result[$delegator['ID']] = $delegator;
}
return $result;
}
/**
* List current user delegators in format compatible with Calendar plugin
*
* @return array List of delegators
*/
public function list_delegators_js()
{
$list = $this->list_delegators();
$result = array();
foreach ($list as $delegator) {
$name = $delegator['name'];
if ($pos = strrpos($name, '(')) {
$name = trim(substr($name, 0, $pos));
}
$result[$delegator['imap_uid']] = array(
'emails' => ';' . implode(';', $delegator['email']),
'email' => $delegator['email'][0],
'name' => $name,
);
}
return $result;
}
/**
* Prepare namespace prefixes for JS environment
*
* @return array List of prefixes
*/
public function namespace_js()
{
$storage = $this->rc->get_storage();
$ns = $storage->get_namespace('other');
if ($ns) {
foreach ($ns as $idx => $nsval) {
$ns[$idx] = kolab_storage::folder_id($nsval[0]);
}
}
return $ns;
}
/**
* Get all folders to which current user has admin access
*
* @param string $delegate IMAP user identifier
*
* @return array Folder type/rights
*/
public function list_folders($delegate = null)
{
$storage = $this->rc->get_storage();
$folders = $storage->list_folders();
$metadata = kolab_storage::folders_typedata();
$result = array();
if (!is_array($metadata)) {
return $result;
}
// Definition of read and write ACL
$right_types = $this->right_types();
foreach ($folders as $folder) {
// get only folders in personal namespace
if ($storage->folder_namespace($folder) != 'personal') {
continue;
}
$rights = null;
$type = $metadata[$folder] ?: 'mail';
list($class, $subclass) = explode('.', $type);
if (!in_array($class, $this->folder_types)) {
continue;
}
// in edit mode, get folder ACL
if ($delegate) {
// @TODO: cache ACL
$acl = $storage->get_acl($folder);
if ($acl = $acl[$delegate]) {
if ($this->acl_compare($acl, $right_types[self::ACL_WRITE])) {
$rights = self::ACL_WRITE;
}
else if ($this->acl_compare($acl, $right_types[self::ACL_READ])) {
$rights = self::ACL_READ;
}
}
}
else if ($folder == 'INBOX' || $subclass == 'default' || $subclass == 'inbox') {
$rights = self::ACL_WRITE;
}
$result[$folder] = array(
'type' => $class,
'rights' => $rights,
);
}
return $result;
}
/**
* Returns list of users for autocompletion
*
* @param string $search Search string
*
* @return array Users list
*/
public function list_users($search)
{
$ldap = $this->ldap();
if (empty($ldap) || $search === '' || $search === null) {
return array();
}
$max = (int) $this->rc->config->get('autocomplete_max', 15);
$mode = (int) $this->rc->config->get('addressbook_search_mode');
$fields = array_unique(array_filter(array_merge((array)$this->ldap_name_field, (array)$this->ldap_login_field)));
$users = array();
$result = $ldap->search($fields, $search, $mode, (array)$this->ldap_login_field, $max);
foreach ($result as $record) {
+ // skip self
+ if ($record['dn'] == $_SESSION['kolab_dn']) {
+ continue;
+ }
+
$user = $this->parse_ldap_record($record);
if ($user['name']) {
$users[] = $user['name'];
}
}
sort($users, SORT_LOCALE_STRING);
return $users;
}
/**
* Extract delegate identifiers and pretty name from LDAP record
*/
private function parse_ldap_record($data, $dn = null)
{
$email = array();
$uid = $data[$this->ldap_login_field];
if (is_array($uid)) {
$uid = array_filter($uid);
$uid = $uid[0];
}
// User name for identity
foreach ((array)$this->ldap_name_field as $field) {
$name = is_array($data[$field]) ? $data[$field][0] : $data[$field];
if (!empty($name)) {
break;
}
}
// User email(s) for identity
foreach ((array)$this->ldap_email_field as $field) {
$user_email = is_array($data[$field]) ? array_filter($data[$field]) : $data[$field];
if (!empty($user_email)) {
$email = array_merge((array)$email, (array)$user_email);
}
}
// Organization for identity
foreach ((array)$this->ldap_org_field as $field) {
$organization = is_array($data[$field]) ? $data[$field][0] : $data[$field];
if (!empty($organization)) {
break;
}
}
$realname = $name;
if ($uid && $name) {
$name .= ' (' . $uid . ')';
}
else {
$name = $uid;
}
// get IMAP uid - identifier used in shared folder hierarchy
$imap_uid = $uid;
if ($pos = strpos($imap_uid, '@')) {
$imap_uid = substr($imap_uid, 0, $pos);
}
return array(
'ID' => kolab_auth_ldap::dn_encode($dn),
'uid' => $uid,
'name' => $name,
'realname' => $realname,
'imap_uid' => $imap_uid,
'email' => $email,
'organization' => $organization,
);
}
/**
* Returns LDAP record of current user
*
* @return array User data
*/
public function user($parsed = false)
{
if (!isset($this->cache['user'])) {
$ldap = $this->ldap();
if (!$ldap) {
return array();
}
// Get current user record
$this->cache['user'] = $ldap->get_record($this->ldap_dn, true);
}
return $parsed ? $this->parse_ldap_record($this->cache['user']) : $this->cache['user'];
}
/**
* Returns current user identities
*
* @return array List of identities
*/
public function user_identities()
{
// cache result in-memory, we need it more than once
if ($this->identities === null) {
$this->identities = $this->rc->user->list_identities();
}
return $this->identities;
}
/**
* Update LDAP record of current user
*
* @param array List of delegates
*/
public function user_update_delegates($list)
{
$ldap = $this->ldap();
$pass = $this->rc->decrypt($_SESSION['password']);
if (!$ldap) {
return false;
}
// need to bind as self for sufficient privilages
if (!$ldap->bind($this->ldap_dn, $pass)) {
return false;
}
$user[$this->ldap_delegate_field] = $list;
unset($this->cache['user']);
// replace delegators list in user record
return $ldap->replace($this->ldap_dn, $user);
}
/**
* Manage delegation data on user login
*/
public function delegation_init()
{
// Fetch all delegators from LDAP who assigned the
// current user as their delegate and create identities
// a) if identity with delegator's email exists, continue
// b) create identity ($delegate on behalf of $delegator
// <$delegator-email>) for new delegators
// c) remove all other identities which do not match the user's primary
// or alias email if 'kolab_delegation_purge_identities' is set.
$delegators = $this->list_delegators();
$use_subs = $this->rc->config->get('kolab_use_subscriptions');
$identities = $this->user_identities();
$emails = array();
$uids = array();
if (!empty($delegators)) {
$storage = $this->rc->get_storage();
$other_ns = $storage->get_namespace('other');
$folders = $storage->list_folders();
}
// convert identities to simpler format for faster access
foreach ($identities as $idx => $ident) {
// get user name from default identity
if (!$idx) {
$default = array(
'name' => $ident['name'],
// 'organization' => $ident['organization'],
// 'signature' => $ident['signature'],
// 'html_signature' => $ident['html_signature'],
);
}
$emails[$ident['identity_id']] = $ident['email'];
}
// for every delegator...
foreach ($delegators as $delegator) {
$uids[$delegator['imap_uid']] = $email_arr = $delegator['email'];
$diff = array_intersect($emails, $email_arr);
// identity with delegator's email already exist, do nothing
if (count($diff)) {
$emails = array_diff($emails, $email_arr);
continue;
}
// create identities for delegator emails
foreach ($email_arr as $email) {
// @TODO: "Delegatorname" or "Username on behalf of Delegatorname"?
$default['name'] = $delegator['realname'];
$default['email'] = $email;
// Database field for organization is NOT NULL
$default['organization'] = empty($delegator['organization']) ? '' : $delegator['organization'];
$this->rc->user->insert_identity($default);
}
// IMAP folders shared by new delegators shall be subscribed on login,
// as well as existing subscriptions of previously shared folders shall
// be removed. I suppose the latter one is already done in Roundcube.
// for every accessible folder...
foreach ($folders as $folder) {
// for every 'other' namespace root...
foreach ($other_ns as $ns) {
$prefix = $ns[0] . $delegator['imap_uid'];
// subscribe delegator's folder
if ($folder === $prefix || strpos($folder, $prefix . substr($ns[0], -1)) === 0) {
// Event/Task folders need client-side activation
$type = kolab_storage::folder_type($folder);
if (preg_match('/^(event|task)/i', $type)) {
kolab_storage::folder_activate($folder);
}
// Subscribe to mail folders and (if system is configured
// to display only subscribed folders) to other
if ($use_subs || preg_match('/^mail/i', $type)) {
$storage->subscribe($folder);
}
}
}
}
}
// remove identities that "do not belong" to user nor delegators
if ($this->rc->config->get('kolab_delegation_purge_identities')) {
$user = $this->user(true);
$emails = array_diff($emails, $user['email']);
foreach (array_keys($emails) as $idx) {
$this->rc->user->delete_identity($idx);
}
}
$_SESSION['delegators'] = $uids;
}
/**
* Sets delegator context according to email message recipient
*
* @param rcube_message $message Email message object
*/
public function delegator_context_from_message($message)
{
if (empty($_SESSION['delegators'])) {
return;
}
// Match delegators' addresses with message To: address
// @TODO: Is this reliable enough?
// Roundcube sends invitations to every attendee separately,
// but maybe there's a software which sends with CC header or many addresses in To:
$emails = $message->get_header('to');
$emails = rcube_mime::decode_address_list($emails, null, false);
foreach ($emails as $email) {
foreach ($_SESSION['delegators'] as $uid => $addresses) {
if (in_array($email['mailto'], $addresses)) {
return $this->context = $uid;
}
}
}
}
/**
* Return (set) current delegator context
*
* @return string Delegator UID
*/
public function delegator_context()
{
if (!$this->context && !empty($_SESSION['delegators'])) {
$context = rcube_utils::get_input_value('_context', rcube_utils::INPUT_GPC);
if ($context && isset($_SESSION['delegators'][$context])) {
$this->context = $context;
}
}
return $this->context;
}
/**
* Set user identity according to delegator delegator
*
* @param array $args Reference to plugin hook arguments
*/
public function delegator_identity_filter(&$args)
{
$context = $this->delegator_context();
if (!$context) {
return;
}
$identities = $this->user_identities();
$emails = $_SESSION['delegators'][$context];
foreach ($identities as $ident) {
if (in_array($ident['email'], $emails)) {
$args['identity'] = $ident;
return;
}
}
// fallback to default identity
$args['identity'] = array_shift($identities);
}
/**
* Filter user emails according to delegator context
*
* @param array $args Reference to plugin hook arguments
*/
public function delegator_emails_filter(&$args)
{
$context = $this->delegator_context();
// return delegator's addresses
if ($context) {
$args['emails'] = $_SESSION['delegators'][$context];
$args['abort'] = true;
}
// return only user addresses (exclude all delegators addresses)
else if (!empty($_SESSION['delegators'])) {
$identities = $this->user_identities();
$emails[] = $this->rc->user->get_username();
foreach ($identities as $identity) {
$emails[] = $identity['email'];
}
foreach ($_SESSION['delegators'] as $delegator_emails) {
$emails = array_diff($emails, $delegator_emails);
}
$args['emails'] = array_unique($emails);
$args['abort'] = true;
}
}
/**
* Filters list of calendars according to delegator context
*
* @param array $args Reference to plugin hook arguments
*/
public function delegator_folder_filter(&$args)
{
$context = $this->delegator_context();
$storage = $this->rc->get_storage();
$other_ns = $storage->get_namespace('other');
$delim = $storage->get_hierarchy_delimiter();
$calendars = array();
// code parts derived from kolab_driver::filter_calendars()
foreach ($args['list'] as $cal) {
if (!$cal->ready) {
continue;
}
if ($args['writeable'] && $cal->readonly) {
continue;
}
if ($args['active'] && !$cal->storage->is_active()) {
continue;
}
if ($args['personal']) {
$ns = $cal->get_namespace();
$name = $cal->get_realname(); // UTF-7 IMAP folder name
if (empty($context)) {
if ($ns != 'personal') {
continue;
}
}
else {
if ($ns != 'other') {
continue;
}
foreach ($other_ns as $ns) {
$folder = $ns[0] . $context . $delim;
if (strpos($name, $folder) !== 0) {
continue;
}
}
}
}
$calendars[$cal->id] = $cal;
}
$args['calendars'] = $calendars;
$args['abort'] = true;
}
/**
* Filters/updates message headers according to delegator context
*
* @param array $args Reference to plugin hook arguments
*/
public function delegator_delivery_filter(&$args)
{
// no context, but message still can be send on behalf of...
if (!empty($_SESSION['delegators'])) {
$message = $args['message'];
$headers = $message->headers();
// get email address from From: header
$from = rcube_mime::decode_address_list($headers['From']);
$from = array_shift($from);
$from = $from['mailto'];
foreach ($_SESSION['delegators'] as $uid => $addresses) {
if (in_array($from, $addresses)) {
$context = $uid;
break;
}
}
// add Sender: header with current user default identity
if ($context) {
$identity = $this->rc->user->get_identity();
$sender = format_email_recipient($identity['email'], $identity['name']);
$message->headers(array('Sender' => $sender), false, true);
}
}
}
/**
* Compares two ACLs (according to supported rights)
*
* @todo: this is stolen from acl plugin, move to rcube_storage/rcube_imap
*
* @param array $acl1 ACL rights array (or string)
* @param array $acl2 ACL rights array (or string)
*
* @param int Comparision result, 2 - full match, 1 - partial match, 0 - no match
*/
function acl_compare($acl1, $acl2)
{
if (!is_array($acl1)) $acl1 = str_split($acl1);
if (!is_array($acl2)) $acl2 = str_split($acl2);
$rights = $this->rights_supported();
$acl1 = array_intersect($acl1, $rights);
$acl2 = array_intersect($acl2, $rights);
$res = array_intersect($acl1, $acl2);
$cnt1 = count($res);
$cnt2 = count($acl2);
if ($cnt1 == $cnt2)
return 2;
else if ($cnt1)
return 1;
else
return 0;
}
/**
* Get list of supported access rights (according to RIGHTS capability)
*
* @todo: this is stolen from acl plugin, move to rcube_storage/rcube_imap
*
* @return array List of supported access rights abbreviations
*/
public function rights_supported()
{
if ($this->supported !== null) {
return $this->supported;
}
$storage = $this->rc->get_storage();
$capa = $storage->get_capability('RIGHTS');
if (is_array($capa)) {
$rights = strtolower($capa[0]);
}
else {
$rights = 'cd';
}
return $this->supported = str_split('lrswi' . $rights . 'pa');
}
private function right_types()
{
// Get supported rights and build column names
$supported = $this->rights_supported();
// depending on server capability either use 'te' or 'd' for deleting msgs
$deleteright = implode(array_intersect(str_split('ted'), $supported));
return array(
self::ACL_READ => 'lrs',
self::ACL_WRITE => 'lrswi'.$deleteright,
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 18, 1:30 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
418874
Default Alt Text
(48 KB)

Event Timeline