Page MenuHomePhorge

No OneTemporary

Size
192 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/init.php b/lib/init.php
index 8a96b68..b58b7e7 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -1,121 +1,120 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| 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/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
// Roundcube Framework constants
define('RCMAIL_START', microtime(true));
define('RCMAIL_VERSION', '0.9-git');
define('RCMAIL_CHARSET', 'UTF-8');
define('INSTALL_PATH', realpath(dirname(__FILE__) . '/../') . '/');
define('RCMAIL_CONFIG_DIR', INSTALL_PATH . 'config');
-define('RCMAIL_PLUGINS_DIR', INSTALL_PATH . 'plugins');
+define('RCMAIL_PLUGINS_DIR', INSTALL_PATH . 'lib/plugins');
// PHP configuration
$config = array(
'error_reporting' => E_ALL &~ (E_NOTICE | E_STRICT),
'mbstring.func_overload' => 0,
// 'suhosin.session.encrypt' => 0,
// 'session.auto_start' => 0,
// 'file_uploads' => 1,
'magic_quotes_runtime' => 0,
'magic_quotes_sybase' => 0,
);
foreach ($config as $optname => $optval) {
if ($optval != ini_get($optname) && @ini_set($optname, $optval) === false) {
die("ERROR: Wrong '$optname' option value!");
}
}
// Define include path
$include_path = INSTALL_PATH . 'lib' . PATH_SEPARATOR;
$include_path .= INSTALL_PATH . 'lib/ext' . PATH_SEPARATOR;
-$include_path .= INSTALL_PATH . 'lib/kolab' . PATH_SEPARATOR;
$include_path .= INSTALL_PATH . 'lib/ext/Roundcube' . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
// @TODO: what is a reasonable value for ActiveSync?
@set_time_limit(600);
// set internal encoding for mbstring extension
if (extension_loaded('mbstring')) {
mb_internal_encoding(RCMAIL_CHARSET);
@mb_regex_encoding(RCMAIL_CHARSET);
}
// include global functions from Roundcube Framework
require_once 'rcube_shared.inc';
// Register main autoloader
spl_autoload_register('kolab_sync_autoload');
// set PEAR error handling (will also load the PEAR main class)
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error');
// Autoloader for Syncroton
//require_once 'Zend/Loader/Autoloader.php';
//$autoloader = Zend_Loader_Autoloader::getInstance();
//$autoloader->setFallbackAutoloader(true);
/**
* Use PHP5 autoload for dynamic class loading
*/
function kolab_sync_autoload($classname)
{
// Roundcube Framework
$filename = preg_replace(
array(
'/Mail_(.+)/',
'/Net_(.+)/',
'/Auth_(.+)/',
'/^html_.+/',
'/^utf8$/',
),
array(
'Mail/\\1',
'Net/\\1',
'Auth/\\1',
'html',
'utf8.class',
),
$classname
);
if ($fp = @fopen("$filename.php", 'r', true)) {
fclose($fp);
include_once "$filename.php";
return true;
}
// Syncroton, replacement for Zend autoloader
$filename = str_replace('_', DIRECTORY_SEPARATOR, $classname);
if ($fp = @fopen("$filename.php", 'r', true)) {
fclose($fp);
include_once "$filename.php";
return true;
}
return false;
}
diff --git a/lib/kolab_sync.php b/lib/kolab_sync.php
index 21c3f03..27ad3d9 100644
--- a/lib/kolab_sync.php
+++ b/lib/kolab_sync.php
@@ -1,333 +1,338 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| 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/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
* Main application class (based on Roundcube Framework)
*/
class kolab_sync extends rcube
{
/**
* Application name
*
* @var string
*/
public $app_name = 'ActiveSync for Kolab'; // no double quotes inside
/**
* Current user
*
* @var rcube_user
*/
public $user;
const CHARSET = 'UTF-8';
const VERSION = "2.0";
/**
* This implements the 'singleton' design pattern
*
* @return kolab_sync The one and only instance
*/
static function get_instance()
{
if (!self::$instance || !is_a(self::$instance, 'kolab_sync')) {
self::$instance = new kolab_sync();
self::$instance->startup(); // init AFTER object was linked with self::$instance
}
return self::$instance;
}
+
+ /**
+ * Initialization of class instance
+ */
public function startup()
{
// Initialize Syncroton Logger
$debug_mode = $this->config->get('activesync_debug') ? kolab_sync_logger::DEBUG : kolab_sync_logger::WARN;
$this->logger = new kolab_sync_logger($debug_mode);
// Get list of plugins
// WARNING: We can use only plugins that are prepared for this
// e.g. are not using output or rcmail objects or
// doesn't throw errors when using them
$plugins = (array)$this->config->get('activesync_plugins', array('kolab_auth', 'kolab_folders'));
+ $required = array('libkolab', 'kolab_folders');
// Initialize/load plugins
$this->plugins = kolab_sync_plugin_api::get_instance();
$this->plugins->init($this, $this->task);
- $this->plugins->load_plugins($plugins);
+ $this->plugins->load_plugins($plugins, $required);
}
/**
* Application execution (authentication and ActiveSync)
*/
public function run()
{
$this->plugins->exec_hook('startup', array('task' => 'login'));
// when used with (f)cgi no PHP_AUTH* variables are available without defining a special rewrite rule
if (!isset($_SERVER['PHP_AUTH_USER'])) {
// "Basic didhfiefdhfu4fjfjdsa34drsdfterrde..."
if (isset($_SERVER["REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REMOTE_USER"], 6));
} elseif (isset($_SERVER["REDIRECT_REMOTE_USER"])) {
$basicAuthData = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6));
} elseif (isset($_SERVER["Authorization"])) {
$basicAuthData = base64_decode(substr($_SERVER["Authorization"], 6));
} elseif (isset($_SERVER["HTTP_AUTHORIZATION"])) {
$basicAuthData = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6));
}
if (isset($basicAuthData) && !empty($basicAuthData)) {
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(":", $basicAuthData);
}
}
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
// Convert domain.tld\username into username@domain (?)
$username = explode("\\", $_SERVER['PHP_AUTH_USER']);
if (count($username) == 2) {
$_SERVER['PHP_AUTH_USER'] = $username[1];
if (!strpos($_SERVER['PHP_AUTH_USER'], '@') && !empty($username[0])) {
$_SERVER['PHP_AUTH_USER'] .= '@' . $username[0];
}
}
$userid = $this->authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
}
if (empty($userid)) {
header('WWW-Authenticate: Basic realm="' . $this->app_name .'"');
header('HTTP/1.1 401 Unauthorized');
exit;
}
// Register Syncroton backends
Syncroton_Registry::set('loggerBackend', $this->logger);
Syncroton_Registry::set(Syncroton_Registry::DATABASE, new kolab_sync_db);
Syncroton_Registry::set(Syncroton_Registry::TRANSACTIONMANAGER, kolab_sync_transaction_manager::getInstance());
Syncroton_Registry::set(Syncroton_Registry::DEVICEBACKEND, new kolab_sync_backend_device);
Syncroton_Registry::set(Syncroton_Registry::FOLDERBACKEND, new kolab_sync_backend_folder);
Syncroton_Registry::set(Syncroton_Registry::SYNCSTATEBACKEND, new kolab_sync_backend_state);
Syncroton_Registry::set(Syncroton_Registry::CONTENTSTATEBACKEND, new kolab_sync_backend_content);
Syncroton_Registry::set(Syncroton_Registry::POLICYBACKEND, new kolab_sync_backend_policy);
Syncroton_Registry::setContactsDataClass('kolab_sync_data_contacts');
Syncroton_Registry::setCalendarDataClass('kolab_sync_data_calendar');
Syncroton_Registry::setEmailDataClass('kolab_sync_data_email');
Syncroton_Registry::setTasksDataClass('kolab_sync_data_tasks');
Syncroton_Registry::setGALDataClass('kolab_sync_data_gal');
// Run Syncroton
$syncroton = new Syncroton_Server($userid);
$syncroton->handle();
}
/**
* Authenticates a user
*
* @param string $username User name
* @param string $password User password
*
* @param int User ID
*/
public function authenticate($username, $password)
{
$auth = $this->plugins->exec_hook('authenticate', array(
'host' => $this->select_host($username),
'user' => $username,
'pass' => $password,
'valid' => true,
));
// Authenticate - get Roundcube user ID
if ($auth['valid'] && !$auth['abort']
&& ($userid = $this->login($auth['user'], $auth['pass'], $auth['host']))) {
return $userid;
}
$this->plugins->exec_hook('login_failed', array(
'host' => $auth['host'],
'user' => $auth['user'],
));
}
/**
* Storage host selection
*/
private function select_host($username)
{
// Get IMAP host
$host = $this->config->get('default_host');
if (is_array($host)) {
list($user, $domain) = explode('@', $username);
// try to select host by mail domain
if (!empty($domain)) {
foreach ($host as $storage_host => $mail_domains) {
if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
$host = $storage_host;
break;
}
else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
$host = is_numeric($storage_host) ? $mail_domains : $storage_host;
break;
}
}
}
// take the first entry if $host is not found
if (is_array($host)) {
list($key, $val) = each($default_host);
$host = is_numeric($key) ? $val : $key;
}
}
return rcube_utils::parse_host($host);
}
/**
* Authenticates a user in IMAP and returns Roundcube user ID.
*/
private function login($username, $password, $host)
{
if (empty($username)) {
return null;
}
$login_lc = $this->config->get('login_lc');
$default_port = $this->config->get('default_port', 143);
// parse $host
$a_host = parse_url($host);
if ($a_host['host']) {
$host = $a_host['host'];
$ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
if (!empty($a_host['port'])) {
$port = $a_host['port'];
}
else if ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143)) {
$port = 993;
}
}
if (!$port) {
$port = $default_port;
}
// Convert username to lowercase. If storage backend
// is case-insensitive we need to store always the same username
if ($login_lc) {
if ($login_lc == 2 || $login_lc === true) {
$username = mb_strtolower($username);
}
else if (strpos($username, '@')) {
// lowercase domain name
list($local, $domain) = explode('@', $username);
$username = $local . '@' . mb_strtolower($domain);
}
}
// Here we need IDNA ASCII
// Only rcube_contacts class is using domain names in Unicode
$host = rcube_utils::idn_to_ascii($host);
$username = rcube_utils::idn_to_ascii($username);
// user already registered?
if ($user = rcube_user::query($username, $host)) {
$username = $user->data['username'];
}
// authenticate user in IMAP
$storage = $this->get_storage();
if (!$storage->connect($host, $username, $password, $port, $ssl)) {
return null;
}
// No user in database, but IMAP auth works
if (!is_object($user)) {
if ($this->config->get('auto_create_user')) {
// create a new user record
$user = rcube_user::create($username, $host);
if (!$user) {
self::raise_error(array(
'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to create a user record",
), true, false);
return null;
}
}
else {
self::raise_error(array(
'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
'message' => "Access denied for new user $username. 'auto_create_user' is disabled",
), true, false);
return null;
}
}
// overwrite config with user preferences
$this->user = $user;
$this->config->set_user_prefs((array)$this->user->get_prefs());
$this->set_storage_prop();
setlocale(LC_ALL, 'en_US.utf8', 'en_US.UTF-8');
// force reloading of mailboxes list/data
//$storage->clear_cache('mailboxes', true);
return $user->ID;
}
/**
* Function to be executed in script shutdown
*/
public function shutdown()
{
parent::shutdown();
// write performance stats to logs/console
if ($this->config->get('devel_mode')) {
if (function_exists('memory_get_usage'))
$mem = sprintf('%.1f', memory_get_usage() / 1048576);
if (function_exists('memory_get_peak_usage'))
$mem .= '/' . sprintf('%.1f', memory_get_peak_usage() / 1048576);
$query = $_SERVER['QUERY_STRING'];
$log = $query . ($mem ? ($query ? ' ' : '') . "[$mem]" : '');
if (defined('RCMAIL_START'))
self::print_timer(RCMAIL_START, $log);
else
self::console($log);
}
}
}
diff --git a/lib/plugins/kolab_auth/LICENSE b/lib/plugins/kolab_auth/LICENSE
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/lib/plugins/kolab_auth/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/lib/plugins/kolab_auth/config.inc.php.dist b/lib/plugins/kolab_auth/config.inc.php.dist
new file mode 100644
index 0000000..6ddfc63
--- /dev/null
+++ b/lib/plugins/kolab_auth/config.inc.php.dist
@@ -0,0 +1,64 @@
+<?php
+
+// The id of the LDAP address book (which refers to the $rcmail_config['ldap_public'])
+// or complete addressbook definition array.
+$rcmail_config['kolab_auth_addressbook'] = '';
+
+// This will overwrite defined filter
+$rcmail_config['kolab_auth_filter'] = '(&(objectClass=kolabInetOrgPerson)(|(uid=%u)(mail=%fu)(alias=%fu)))';
+
+// Use this fields (from fieldmap configuration) to get authentication ID
+$rcmail_config['kolab_auth_login'] = 'email';
+
+// Use this fields (from fieldmap configuration) for default identity.
+// If the value array contains more than one field, first non-empty will be used
+// Note: These aren't LDAP attributes, but field names in config
+// Note: If there's more than one email address, as many identities will be created
+$rcmail_config['kolab_auth_name'] = array('name', 'cn');
+$rcmail_config['kolab_auth_email'] = array('email');
+
+// Login and password of the admin user. Enables "Login As" feature.
+$rcmail_config['kolab_auth_admin_login'] = '';
+$rcmail_config['kolab_auth_admin_password'] = '';
+
+// Enable audit logging for abuse of administrative privileges.
+$rcmail_config['kolab_auth_auditlog'] = true;
+
+// Role field (from fieldmap configuration)
+$rcmail_config['kolab_auth_role'] = 'role';
+// The required value for the role attribute to contain should the user be allowed
+// to login as another user.
+$rcmail_config['kolab_auth_role_value'] = '';
+
+// Administrative group name to which user must be assigned to
+// which adds privilege to login as another user.
+$rcmail_config['kolab_auth_group'] = '';
+
+// Enable plugins on a role-by-role basis. In this example, the 'acl' plugin
+// is enabled for people with a 'cn=professional-user,dc=mykolab,dc=ch' role.
+//
+// Note that this does NOT mean the 'acl' plugin is disabled for other people.
+$rcmail_config['kolab_auth_role_plugins'] = Array(
+ 'cn=professional-user,dc=mykolab,dc=ch' => Array(
+ 'acl',
+ ),
+ );
+
+// Settings on a role-by-role basis. In this example, the 'htmleditor' setting
+// is enabled(1) for people with a 'cn=professional-user,dc=mykolab,dc=ch' role,
+// and it cannot be overridden. Sample use-case: disable htmleditor for normal people,
+// do not allow the setting to be controlled through the preferences, enable the
+// html editor for professional users and allow them to override the setting in
+// the preferences.
+$rcmail_config['kolab_auth_role_settings'] = Array(
+ 'cn=professional-user,dc=mykolab,dc=ch' => Array(
+ 'htmleditor' => Array(
+ 'mode' => 'override',
+ 'value' => 1,
+ 'allow_override' => true
+ ),
+ ),
+ );
+
+
+?>
diff --git a/lib/plugins/kolab_auth/kolab_auth.php b/lib/plugins/kolab_auth/kolab_auth.php
new file mode 100644
index 0000000..620def5
--- /dev/null
+++ b/lib/plugins/kolab_auth/kolab_auth.php
@@ -0,0 +1,531 @@
+<?php
+
+/**
+ * Kolab Authentication (based on ldap_authentication plugin)
+ *
+ * Authenticates on LDAP server, finds canonized authentication ID for IMAP
+ * and for new users creates identity based on LDAP information.
+ *
+ * Supports impersonate feature (login as another user). To use this feature
+ * imap_auth_type/smtp_auth_type must be set to DIGEST-MD5 or PLAIN.
+ *
+ * @version @package_version@
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ *
+ * Copyright (C) 2011, 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_auth extends rcube_plugin
+{
+ private $ldap;
+ private $data = array();
+
+ public function init()
+ {
+ $rcmail = rcube::get_instance();
+
+ $this->add_hook('authenticate', array($this, 'authenticate'));
+ $this->add_hook('startup', array($this, 'startup'));
+ $this->add_hook('user_create', array($this, 'user_create'));
+
+ // 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('write_log', array($this, 'write_log'));
+
+ // TODO: This section does not actually seem to work
+ if ($rcmail->config->get('kolab_auth_auditlog', false)) {
+ $rcmail->config->set('debug_level', 1);
+ $rcmail->config->set('devel_mode', true);
+ $rcmail->config->set('smtp_log', true);
+ $rcmail->config->set('log_logins', true);
+ $rcmail->config->set('log_session', true);
+ $rcmail->config->set('sql_debug', 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);
+
+ }
+
+ }
+
+ public function startup($args) {
+ // Arguments are task / action, not interested
+ if (!empty($_SESSION['user_roledns'])) {
+ $this->load_user_role_plugins_and_settings($_SESSION['user_roledns']);
+ }
+
+ return $args;
+ }
+
+ public function load_user_role_plugins_and_settings($role_dns) {
+ $rcmail = rcube::get_instance();
+ $this->load_config();
+
+ // Check role dependent plugins to enable and settings to modify
+
+ // Example 'kolab_auth_role_plugins' =
+ //
+ // Array(
+ // '<role_dn>' => Array('plugin1', 'plugin2'),
+ // );
+
+ $role_plugins = $rcmail->config->get('kolab_auth_role_plugins');
+
+ // Example $rcmail_config['kolab_auth_role_settings'] =
+ //
+ // Array(
+ // '<role_dn>' => Array(
+ // '$setting' => Array(
+ // 'mode' => '(override|merge)', (default: override)
+ // 'value' => <>,
+ // 'allow_override' => (true|false) (default: false)
+ // ),
+ // ),
+ // );
+
+ $role_settings = $rcmail->config->get('kolab_auth_role_settings');
+
+ foreach ($role_dns as $role_dn) {
+ if (isset($role_plugins[$role_dn]) && is_array($role_plugins[$role_dn])) {
+ foreach ($role_plugins[$role_dn] as $plugin) {
+ $this->require_plugin($plugin);
+ }
+ }
+
+ if (isset($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 (!isset($setting['allow_override']) || !$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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public function write_log($args) {
+ $rcmail = rcube::get_instance();
+
+ if (!$rcmail->config->get('kolab_auth_auditlog', false)) {
+ return $args;
+ }
+
+ $args['abort'] = true;
+
+ if ($rcmail->config->get('log_driver') == 'syslog') {
+ $prio = $args['name'] == 'errors' ? LOG_ERR : LOG_INFO;
+ syslog($prio, $args['line']);
+ return $args;
+ }
+ else {
+ $line = sprintf("[%s]: %s\n", $args['date'], $args['line']);
+
+ // log_driver == 'file' is assumed here
+ $log_dir = $rcmail->config->get('log_dir', INSTALL_PATH . 'logs');
+ $log_path = $log_dir.'/'.strtolower($_SESSION['kolab_auth_admin']).'/'.strtolower($_SESSION['username']);
+
+ // Append original username + target username
+ if (!is_dir($log_path)) {
+ // Attempt to create the directory
+ if (@mkdir($log_path, 0750, true)) {
+ $log_dir = $log_path;
+ }
+ }
+ else {
+ $log_dir = $log_path;
+ }
+
+ // try to open specific log file for writing
+ $logfile = $log_dir.'/'.$args['name'];
+
+ if ($fp = fopen($logfile, 'a')) {
+ fwrite($fp, $line);
+ fflush($fp);
+ fclose($fp);
+ return $args;
+ }
+ else {
+ trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);
+ }
+ }
+
+ 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)) {
+ $args['email_list'] = array_unique($this->data['user_email']);
+ }
+ 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->load_config();
+ $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;
+ }
+
+ $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', Q($this->gettext('loginas'))))
+ . html::tag('td', 'input', $input->show(trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST))))
+ );
+ $args['content'] = preg_replace('/<\/tbody>/i', $row . '</tbody>', $args['content']);
+
+ return $args;
+ }
+
+ /**
+ * Find user credentials In LDAP.
+ */
+ public function authenticate($args)
+ {
+ $this->load_config();
+
+ if (!$this->init_ldap()) {
+ $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');
+
+ // get username and host
+ $host = rcube_utils::parse_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)) {
+ $args['abort'] = true;
+ return $args;
+ }
+
+ // Find user record in LDAP
+ $record = $this->get_user_record($user, $host);
+
+ if (empty($record)) {
+ $args['abort'] = true;
+ return $args;
+ }
+
+ $role_attr = $rcmail->config->get('kolab_auth_role');
+
+ if (!empty($role_attr) && !empty($record[$role_attr])) {
+ $_SESSION['user_roledns'] = (array)($record[$role_attr]);
+ }
+
+ // Login As...
+ if (!empty($loginas) && $admin_login) {
+ // Authenticate to LDAP
+ $dn = $this->ldap->dn_decode($record['ID']);
+ $result = $this->ldap->bind($dn, $pass);
+
+ if (!$result) {
+ return $args;
+ }
+
+ // check if the original user has/belongs to administrative role/group
+ $isadmin = false;
+ $group = $rcmail->config->get('kolab_auth_group');
+ $role_attr = $rcmail->config->get('kolab_auth_role');
+ $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 = $this->parse_vars($role_dn, $user, $host);
+ foreach ((array)$record[$role_attr] as $role) {
+ if ($role == $role_dn) {
+ $isadmin = true;
+ break;
+ }
+ }
+ }
+
+ // check group
+ if (!$isadmin && !empty($group)) {
+ $groups = $this->ldap->get_record_groups($record['ID']);
+ foreach ($groups as $g) {
+ if ($group == $this->ldap->dn_decode($g)) {
+ $isadmin = true;
+ break;
+ }
+ }
+
+ }
+
+ // 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;
+ }
+
+ $record = null;
+
+ // user has the privilage, get "login as" user credentials
+ if ($isadmin) {
+ $record = $this->get_user_record($loginas, $host);
+ }
+
+ if (empty($record)) {
+ $args['abort'] = true;
+ return $args;
+ }
+
+ $args['user'] = $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);
+ }
+
+ // Store UID in session for use by other plugins
+ $_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid'];
+
+ // 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->data['user_login'];
+ }
+
+ // User name for identity (first log in)
+ foreach ((array)$name_attr as $field) {
+ $name = is_array($record[$field]) ? $record[$field][0] : $record[$field];
+ 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]) ? array_filter($record[$field]) : $record[$field];
+ if (!empty($email)) {
+ $this->data['user_email'] = array_merge((array)$this->data['user_email'], (array)$email);
+ }
+ }
+
+ // 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()));
+ }
+
+ 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['options']['smtp_auth_cid'] = $admin_login;
+ $args['options']['smtp_auth_pw'] = $admin_pass;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Initializes LDAP object and connects to LDAP server
+ */
+ private function init_ldap()
+ {
+ if ($this->ldap) {
+ return $this->ldap->ready;
+ }
+
+ $rcmail = rcube::get_instance();
+
+ $addressbook = $rcmail->config->get('kolab_auth_addressbook');
+
+ if (!is_array($addressbook)) {
+ $ldap_config = (array)$rcmail->config->get('ldap_public');
+ $addressbook = $ldap_config[$addressbook];
+ }
+
+ if (empty($addressbook)) {
+ return false;
+ }
+
+ $this->ldap = new kolab_auth_ldap_backend(
+ $addressbook,
+ $rcmail->config->get('ldap_debug'),
+ $rcmail->config->mail_domain($_SESSION['imap_host'])
+ );
+
+ return $this->ldap->ready;
+ }
+
+ /**
+ * Fetches user data from LDAP addressbook
+ */
+ private function get_user_record($user, $host)
+ {
+ $rcmail = rcube::get_instance();
+ $filter = $rcmail->config->get('kolab_auth_filter');
+
+ $filter = $this->parse_vars($filter, $user, $host);
+
+ // reset old result
+ $this->ldap->reset();
+
+ // get record
+ $this->ldap->set_filter($filter);
+ $results = $this->ldap->list_records();
+
+ if (count($results->records) == 1) {
+ return $results->records[0];
+ }
+ }
+
+ /**
+ * Prepares filter query for LDAP search
+ */
+ private function parse_vars($str, $user, $host)
+ {
+ $rcmail = rcube::get_instance();
+ $domain = $rcmail->config->get('username_domain');
+
+ if (!empty($domain) && strpos($user, '@') === false) {
+ if (is_array($domain) && isset($domain[$host])) {
+ $user .= '@'.rcube_utils::parse_host($domain[$host], $host);
+ }
+ else if (is_string($domain)) {
+ $user .= '@'.rcube_utils::parse_host($domain, $host);
+ }
+ }
+
+ // replace variables in filter
+ list($u, $d) = explode('@', $user);
+ $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string
+ $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u);
+
+ return strtr($str, $replaces);
+ }
+}
+
+/**
+ * Wrapper class for rcube_ldap addressbook
+ */
+class kolab_auth_ldap_backend extends rcube_ldap
+{
+ function __construct($p, $debug=false, $mail_domain=null)
+ {
+ parent::__construct($p, $debug, $mail_domain);
+ $this->fieldmap['uid'] = 'uid';
+ }
+
+ function set_filter($filter)
+ {
+ if ($filter) {
+ $this->prop['filter'] = $filter;
+ }
+ }
+}
diff --git a/lib/plugins/kolab_auth/localization/de_CH.inc b/lib/plugins/kolab_auth/localization/de_CH.inc
new file mode 100644
index 0000000..9cdad33
--- /dev/null
+++ b/lib/plugins/kolab_auth/localization/de_CH.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Anmelden als';
+
+?>
diff --git a/lib/plugins/kolab_auth/localization/de_DE.inc b/lib/plugins/kolab_auth/localization/de_DE.inc
new file mode 100644
index 0000000..9cdad33
--- /dev/null
+++ b/lib/plugins/kolab_auth/localization/de_DE.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Anmelden als';
+
+?>
diff --git a/lib/plugins/kolab_auth/localization/en_US.inc b/lib/plugins/kolab_auth/localization/en_US.inc
new file mode 100644
index 0000000..e1adb3f
--- /dev/null
+++ b/lib/plugins/kolab_auth/localization/en_US.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Login As';
+
+?>
diff --git a/lib/plugins/kolab_auth/localization/pl_PL.inc b/lib/plugins/kolab_auth/localization/pl_PL.inc
new file mode 100644
index 0000000..785f200
--- /dev/null
+++ b/lib/plugins/kolab_auth/localization/pl_PL.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Zaloguj jako';
+
+?>
diff --git a/lib/plugins/kolab_auth/package.xml b/lib/plugins/kolab_auth/package.xml
new file mode 100644
index 0000000..6200c4c
--- /dev/null
+++ b/lib/plugins/kolab_auth/package.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+ http://pear.php.net/dtd/tasks-1.0.xsd
+ http://pear.php.net/dtd/package-2.0
+ http://pear.php.net/dtd/package-2.0.xsd">
+ <name>kolab_auth</name>
+ <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
+ <summary>Kolab Authentication</summary>
+ <description>
+ Authenticates on LDAP server, finds canonized authentication ID for IMAP
+ and for new users creates identity based on LDAP information.
+ Supports impersonate feature (login as another user). To use this feature
+ imap_auth_type/smtp_auth_type must be set to DIGEST-MD5 or PLAIN.
+ </description>
+ <lead>
+ <name>Aleksander Machniak</name>
+ <user>machniak</user>
+ <email>machniak@kolabsys.com</email>
+ <active>yes</active>
+ </lead>
+ <date>2012-10-08</date>
+ <version>
+ <release>0.4</release>
+ <api>0.1</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
+ <notes>-</notes>
+ <contents>
+ <dir baseinstalldir="/" name="/">
+ <file name="kolab_auth.php" role="php">
+ <tasks:replace from="@name@" to="name" type="package-info"/>
+ <tasks:replace from="@package_version@" to="version" type="package-info"/>
+ </file>
+ <file name="config.inc.php.dist" role="data"></file>
+ <file name="LICENSE" role="data"></file>
+
+ <file name="localization/de_CH.inc" role="data"></file>
+ <file name="localization/de_DE.inc" role="data"></file>
+ <file name="localization/en_US.inc" role="data"></file>
+ <file name="localization/pl_PL.inc" role="data"></file>
+ </dir>
+ <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.1</min>
+ </php>
+ <pearinstaller>
+ <min>1.7.0</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <phprelease/>
+</package>
diff --git a/lib/plugins/kolab_folders/LICENSE b/lib/plugins/kolab_folders/LICENSE
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/lib/plugins/kolab_folders/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/lib/plugins/kolab_folders/config.inc.php.dist b/lib/plugins/kolab_folders/config.inc.php.dist
new file mode 100644
index 0000000..e393684
--- /dev/null
+++ b/lib/plugins/kolab_folders/config.inc.php.dist
@@ -0,0 +1,34 @@
+<?php
+
+// Default kolab-specific folders. Set values to non-empty
+// strings to create default folders of apropriate type.
+// If there is no default folder with specified type in user mailbox,
+// it will be created.
+// Note: Mail folders will be also subscribed.
+
+// Default Configuration folder
+$rcmail_config['kolab_folders_configuration_default'] = '';
+// Default Calendar folder
+$rcmail_config['kolab_folders_event_default'] = '';
+// Default Contacts (Addressbook) folder
+$rcmail_config['kolab_folders_contact_default'] = '';
+// Default Tasks folder
+$rcmail_config['kolab_folders_task_default'] = '';
+// Default Notes folder
+$rcmail_config['kolab_folders_note_default'] = '';
+// Default Journal folder
+$rcmail_config['kolab_folders_journal_default'] = '';
+
+// INBOX folder
+$rcmail_config['kolab_folders_mail_inbox'] = '';
+// Drafts folder
+$rcmail_config['kolab_folders_mail_drafts'] = '';
+// Sent folder
+$rcmail_config['kolab_folders_mail_sentitems'] = '';
+// Trash folder
+$rcmail_config['kolab_folders_mail_wastebasket'] = '';
+// Others folders
+$rcmail_config['kolab_folders_mail_outbox'] = '';
+$rcmail_config['kolab_folders_mail_junkemail'] = '';
+
+?>
diff --git a/lib/plugins/kolab_folders/kolab_folders.js b/lib/plugins/kolab_folders/kolab_folders.js
new file mode 100644
index 0000000..7cabfdd
--- /dev/null
+++ b/lib/plugins/kolab_folders/kolab_folders.js
@@ -0,0 +1,65 @@
+/**
+ * Client script for the Kolab folder management/listing extension
+ *
+ * @version @package_version@
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ *
+ * Copyright (C) 2011, 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/>.
+ */
+
+$(document).ready(function() {
+ // IE doesn't allow setting OPTION's display/visibility
+ // We'll need to remove SELECT's options, see below
+ if (bw.ie) {
+ rcmail.env.subtype_html = $('#_subtype').html();
+ }
+
+ // Add onchange handler for folder type SELECT, and call it on form init
+ $('#_ctype').change(function() {
+ var type = $(this).val(),
+ sub = $('#_subtype'),
+ subtype = sub.val();
+
+ // For IE we need to revert the whole SELECT to the original state
+ if (bw.ie) {
+ sub.html(rcmail.env.subtype_html).val(subtype);
+ }
+
+ // For non-mail folders we must hide mail-specific subtypes
+ $('option', sub).each(function() {
+ var opt = $(this), val = opt.val();
+ if (val == '')
+ return;
+ // there's no mail.default
+ if (val == 'default' && type != 'mail') {
+ opt.show();
+ return;
+ };
+
+ if (type == 'mail' && val != 'default')
+ opt.show();
+ else if (bw.ie)
+ opt.remove();
+ else
+ opt.hide();
+ });
+
+ // And re-set subtype
+ if (type != 'mail' && subtype != '' && subtype != 'default') {
+ sub.val('');
+ }
+ }).change();
+});
diff --git a/lib/plugins/kolab_folders/kolab_folders.php b/lib/plugins/kolab_folders/kolab_folders.php
new file mode 100644
index 0000000..ed05122
--- /dev/null
+++ b/lib/plugins/kolab_folders/kolab_folders.php
@@ -0,0 +1,543 @@
+<?php
+
+/**
+ * Type-aware folder management/listing for Kolab
+ *
+ * @version @package_version@
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ *
+ * Copyright (C) 2011, 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_folders extends rcube_plugin
+{
+ public $task = '?(?!login).*';
+
+ public $types = array('mail', 'event', 'journal', 'task', 'note', 'contact', 'configuration');
+ public $mail_types = array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail');
+
+ private $rc;
+ private static $instance;
+
+
+ /**
+ * Plugin initialization.
+ */
+ function init()
+ {
+ self::$instance = $this;
+ $this->rc = rcube::get_instance();
+
+ // load required plugin
+ $this->require_plugin('libkolab');
+
+ // Folder listing hooks
+ $this->add_hook('storage_folders', array($this, 'mailboxes_list'));
+
+ // Folder manager hooks
+ $this->add_hook('folder_form', array($this, 'folder_form'));
+ $this->add_hook('folder_update', array($this, 'folder_save'));
+ $this->add_hook('folder_create', array($this, 'folder_save'));
+ $this->add_hook('folder_delete', array($this, 'folder_save'));
+ $this->add_hook('folder_rename', array($this, 'folder_save'));
+ $this->add_hook('folders_list', array($this, 'folders_list'));
+ }
+
+ /**
+ * Handler for mailboxes_list hook. Enables type-aware lists filtering.
+ */
+ function mailboxes_list($args)
+ {
+ // infinite loop prevention
+ if ($this->is_processing) {
+ return $args;
+ }
+
+ if (!$this->metadata_support()) {
+ return $args;
+ }
+
+ $this->is_processing = true;
+
+ // get folders
+ $folders = kolab_storage::list_folders($args['root'], $args['name'], $args['filter'], $args['mode'] == 'LSUB', $folderdata);
+
+ $this->is_processing = false;
+
+ if (!is_array($folders)) {
+ return $args;
+ }
+
+ // Create default folders
+ if ($args['root'] == '' && $args['name'] = '*') {
+ $this->create_default_folders($folders, $args['filter'], $folderdata);
+ }
+
+ $args['folders'] = $folders;
+
+ return $args;
+ }
+
+ /**
+ * Handler for folders_list hook. Add css classes to folder rows.
+ */
+ function folders_list($args)
+ {
+ if (!$this->metadata_support()) {
+ return $args;
+ }
+
+ $table = $args['table'];
+ $storage = $this->rc->get_storage();
+
+ // get folders types
+ $folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY);
+
+ if (!is_array($folderdata)) {
+ return $args;
+ }
+
+ // Add type-based style for table rows
+ // See kolab_folders::folder_class_name()
+ for ($i=1, $cnt=$table->size(); $i<=$cnt; $i++) {
+ $attrib = $table->get_row_attribs($i);
+ $folder = $attrib['foldername']; // UTF7-IMAP
+ $type = !empty($folderdata[$folder]) ? $folderdata[$folder][kolab_storage::CTYPE_KEY] : null;
+
+ if (!$type)
+ $type = 'mail';
+
+ $class_name = self::folder_class_name($type);
+
+ $attrib['class'] = trim($attrib['class'] . ' ' . $class_name);
+ $table->set_row_attribs($attrib, $i);
+ }
+
+ return $args;
+ }
+
+ /**
+ * Handler for folder info/edit form (folder_form hook).
+ * Adds folder type selector.
+ */
+ function folder_form($args)
+ {
+ if (!$this->metadata_support()) {
+ return $args;
+ }
+ // load translations
+ $this->add_texts('localization/', false);
+
+ // INBOX folder is of type mail.inbox and this cannot be changed
+ if ($args['name'] == 'INBOX') {
+ $args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
+ 'label' => $this->gettext('folderctype'),
+ 'value' => sprintf('%s (%s)', $this->gettext('foldertypemail'), $this->gettext('inbox')),
+ );
+
+ return $args;
+ }
+
+ if ($args['options']['is_root']) {
+ return $args;
+ }
+
+ $mbox = strlen($args['name']) ? $args['name'] : $args['parent_name'];
+
+ if (isset($_POST['_ctype'])) {
+ $new_ctype = trim(get_input_value('_ctype', RCUBE_INPUT_POST));
+ $new_subtype = trim(get_input_value('_subtype', RCUBE_INPUT_POST));
+ }
+
+ // Get type of the folder or the parent
+ if (strlen($mbox)) {
+ list($ctype, $subtype) = $this->get_folder_type($mbox);
+ if (strlen($args['parent_name']) && $subtype == 'default')
+ $subtype = ''; // there can be only one
+ }
+
+ if (!$ctype) {
+ $ctype = 'mail';
+ }
+
+ $storage = $this->rc->get_storage();
+
+ // Don't allow changing type of shared folder, according to ACL
+ if (strlen($mbox)) {
+ $options = $storage->folder_info($mbox);
+ if ($options['namespace'] != 'personal' && !in_array('a', $options['rights'])) {
+ if (in_array($ctype, $this->types)) {
+ $value = $this->gettext('foldertype'.$ctype);
+ }
+ else {
+ $value = $ctype;
+ }
+ if ($subtype) {
+ $value .= ' ('. ($subtype == 'default' ? $this->gettext('default') : $subtype) .')';
+ }
+
+ $args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
+ 'label' => $this->gettext('folderctype'),
+ 'value' => $value,
+ );
+
+ return $args;
+ }
+ }
+
+ // Add javascript script to the client
+ $this->include_script('kolab_folders.js');
+
+ // build type SELECT fields
+ $type_select = new html_select(array('name' => '_ctype', 'id' => '_ctype'));
+ $sub_select = new html_select(array('name' => '_subtype', 'id' => '_subtype'));
+
+ foreach ($this->types as $type) {
+ $type_select->add($this->gettext('foldertype'.$type), $type);
+ }
+ // add non-supported type
+ if (!in_array($ctype, $this->types)) {
+ $type_select->add($ctype, $ctype);
+ }
+
+ $sub_select->add('', '');
+ $sub_select->add($this->gettext('default'), 'default');
+ foreach ($this->mail_types as $type) {
+ $sub_select->add($this->gettext($type), $type);
+ }
+
+ $args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
+ 'label' => $this->gettext('folderctype'),
+ 'value' => $type_select->show(isset($new_ctype) ? $new_ctype : $ctype)
+ . $sub_select->show(isset($new_subtype) ? $new_subtype : $subtype),
+ );
+
+ return $args;
+ }
+
+ /**
+ * Handler for folder update/create action (folder_update/folder_create hook).
+ */
+ function folder_save($args)
+ {
+ // Folder actions from folders list
+ if (empty($args['record'])) {
+ return $args;
+ }
+
+ // Folder create/update with form
+ $ctype = trim(get_input_value('_ctype', RCUBE_INPUT_POST));
+ $subtype = trim(get_input_value('_subtype', RCUBE_INPUT_POST));
+ $mbox = $args['record']['name'];
+ $old_mbox = $args['record']['oldname'];
+ $subscribe = $args['record']['subscribe'];
+
+ if (empty($ctype)) {
+ return $args;
+ }
+
+ // load translations
+ $this->add_texts('localization/', false);
+
+ // Skip folder creation/rename in core
+ // @TODO: Maybe we should provide folder_create_after and folder_update_after hooks?
+ // Using create_mailbox/rename_mailbox here looks bad
+ $args['abort'] = true;
+
+ // There can be only one default folder of specified type
+ if ($subtype == 'default') {
+ $default = $this->get_default_folder($ctype);
+
+ if ($default !== null && $old_mbox != $default) {
+ $args['result'] = false;
+ $args['message'] = $this->gettext('defaultfolderexists');
+ return $args;
+ }
+ }
+ // Subtype sanity-checks
+ else if ($subtype && ($ctype != 'mail' || !in_array($subtype, $this->mail_types))) {
+ $subtype = '';
+ }
+
+ $ctype .= $subtype ? '.'.$subtype : '';
+
+ $storage = $this->rc->get_storage();
+
+ // Create folder
+ if (!strlen($old_mbox)) {
+ // By default don't subscribe to non-mail folders
+ if ($subscribe)
+ $subscribe = (bool) preg_match('/^mail/', $ctype);
+
+ $result = $storage->create_folder($mbox, $subscribe);
+ // Set folder type
+ if ($result) {
+ $this->set_folder_type($mbox, $ctype);
+ }
+ }
+ // Rename folder
+ else {
+ if ($old_mbox != $mbox) {
+ $result = $storage->rename_folder($old_mbox, $mbox);
+ }
+ else {
+ $result = true;
+ }
+
+ if ($result) {
+ list($oldtype, $oldsubtype) = $this->get_folder_type($mbox);
+ $oldtype .= $oldsubtype ? '.'.$oldsubtype : '';
+
+ if ($ctype != $oldtype) {
+ $this->set_folder_type($mbox, $ctype);
+ }
+ }
+ }
+
+ $args['record']['class'] = self::folder_class_name($ctype);
+ $args['record']['subscribe'] = $subscribe;
+ $args['result'] = $result;
+
+ return $args;
+ }
+
+ /**
+ * Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
+ *
+ * @return boolean
+ */
+ function metadata_support()
+ {
+ $storage = $this->rc->get_storage();
+
+ return $storage->get_capability('METADATA') ||
+ $storage->get_capability('ANNOTATEMORE') ||
+ $storage->get_capability('ANNOTATEMORE2');
+ }
+
+ /**
+ * Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
+ *
+ * @param string $folder Folder name
+ *
+ * @return array Folder content-type
+ */
+ function get_folder_type($folder)
+ {
+ $storage = $this->rc->get_storage();
+ $folderdata = $storage->get_metadata($folder, array(kolab_storage::CTYPE_KEY_PRIVATE, kolab_storage::CTYPE_KEY));
+
+ if (!($ctype = $folderdata[$folder][kolab_storage::CTYPE_KEY_PRIVATE])) {
+ $ctype = $folderdata[$folder][kolab_storage::CTYPE_KEY];
+ }
+
+ return explode('.', $ctype);
+ }
+
+ /**
+ * Sets folder content-type.
+ *
+ * @param string $folder Folder name
+ * @param string $type Content type
+ *
+ * @return boolean True on success
+ */
+ function set_folder_type($folder, $type='mail')
+ {
+ return kolab_storage::set_folder_type($folder, $type);
+ }
+
+ /**
+ * Returns the name of default folder
+ *
+ * @param string $type Folder type
+ *
+ * @return string Folder name
+ */
+ function get_default_folder($type)
+ {
+ $storage = $this->rc->get_storage();
+ $folderdata = $storage->get_metadata('*', array(kolab_storage::CTYPE_KEY_PRIVATE, kolab_storage::CTYPE_KEY));
+
+ if (!is_array($folderdata)) {
+ return null;
+ }
+
+ $type .= '.default';
+ $namespace = $storage->get_namespace();
+
+ // get all folders of specified type
+ $folderdata = array_map(array($this, 'folder_select_metadata'), $folderdata);
+ $folderdata = array_intersect($folderdata, array($type));
+
+ foreach ($folderdata as $folder => $data) {
+ // check if folder is in personal namespace
+ foreach (array('shared', 'other') as $nskey) {
+ if (!empty($namespace[$nskey])) {
+ foreach ($namespace[$nskey] as $ns) {
+ if ($ns[0] && substr($folder, 0, strlen($ns[0])) == $ns[0]) {
+ continue 3;
+ }
+ }
+ }
+ }
+
+ // There can be only one default folder of specified type
+ return $folder;
+ }
+
+ return null;
+ }
+
+ /**
+ * Callback for array_map to select the correct annotation value
+ */
+ private function folder_select_metadata($types)
+ {
+ return $types[kolab_storage::CTYPE_KEY_PRIVATE] ?: $types[kolab_storage::CTYPE_KEY];
+ }
+
+ /**
+ * Returns CSS class name for specified folder type
+ *
+ * @param string $type Folder type
+ *
+ * @return string Class name
+ */
+ static function folder_class_name($type)
+ {
+ list($ctype, $subtype) = explode('.', $type);
+
+ $class[] = 'type-' . ($ctype ? $ctype : 'mail');
+
+ if ($subtype)
+ $class[] = 'subtype-' . $subtype;
+
+ return implode(' ', $class);
+ }
+
+ /**
+ * Creates default folders if they doesn't exist
+ */
+ private function create_default_folders(&$folders, $filter, $folderdata = null)
+ {
+ $storage = $this->rc->get_storage();
+ $namespace = $storage->get_namespace();
+ $defaults = array();
+ $need_update = false;
+
+ if (!is_array($folderdata)) {
+ $folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY);
+
+ if (!is_array($folderdata)) {
+ return;
+ }
+
+ // "Flattenize" metadata array to become a name->type hash
+ $folderdata = array_map('implode', $folderdata);
+ }
+
+ // Find personal namespace prefix
+ if (is_array($namespace['personal']) && count($namespace['personal']) == 1) {
+ $prefix = $namespace['personal'][0][0];
+ }
+ else {
+ $prefix = '';
+ }
+
+ $this->load_config();
+
+ // get configured defaults
+ foreach ($this->types as $type) {
+ $subtypes = $type == 'mail' ? $this->mail_types : array('default');
+ foreach ($subtypes as $subtype) {
+ $opt_name = 'kolab_folders_' . $type . '_' . $subtype;
+ if ($folder = $this->rc->config->get($opt_name)) {
+ // convert configuration value to UTF7-IMAP charset
+ $folder = rcube_charset::convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
+ // and namespace prefix if needed
+ if ($prefix && strpos($folder, $prefix) === false && $folder != 'INBOX') {
+ $folder = $prefix . $folder;
+ }
+ $defaults[$type . '.' . $subtype] = $folder;
+ }
+ }
+ }
+
+ // find default folders
+ foreach ($defaults as $type => $foldername) {
+ // folder exists, do nothing
+ if (!empty($folderdata[$foldername])) {
+ continue;
+ }
+
+ // special case, need to set type only
+ if ($foldername == 'INBOX' || $type == 'mail.inbox') {
+ $this->set_folder_type($foldername, 'mail.inbox');
+ continue;
+ }
+
+ // get all folders of specified type
+ $folders = array_intersect($folderdata, array($type));
+ unset($folders[0]);
+
+ // find folders in personal namespace
+ foreach ($folders as $folder) {
+ if ($folder) {
+ foreach (array('shared', 'other') as $nskey) {
+ if (!empty($namespace[$nskey])) {
+ foreach ($namespace[$nskey] as $ns) {
+ if ($ns[0] && substr($folder, 0, strlen($ns[0])) == $ns[0]) {
+ continue 3;
+ }
+ }
+ }
+ }
+ }
+
+ // got folder in personal namespace
+ continue 2;
+ }
+
+ list($type1, $type2) = explode('.', $type);
+
+ // create folder
+ if ($type1 != 'mail' || !$storage->folder_exists($foldername)) {
+ $storage->create_folder($foldername, $type1 == 'mail');
+ }
+
+ // set type
+ $result = $this->set_folder_type($foldername, $type);
+
+ // add new folder to the result
+ if ($result && (!$filter || $filter == $type1)) {
+ $folders[] = $foldername;
+ }
+ }
+ }
+
+
+ /**
+ * Static getter for default folder of the given type
+ *
+ * @param string $type Folder type
+ * @return string Folder name
+ */
+ public static function default_folder($type)
+ {
+ return self::$instance->get_default_folder($type);
+ }
+}
diff --git a/lib/plugins/kolab_folders/localization/de_CH.inc b/lib/plugins/kolab_folders/localization/de_CH.inc
new file mode 100644
index 0000000..f9f6e16
--- /dev/null
+++ b/lib/plugins/kolab_folders/localization/de_CH.inc
@@ -0,0 +1,24 @@
+<?php
+
+$labels = array();
+
+$labels['folderctype'] = 'Ordnerinhalt';
+$labels['foldertypemail'] = 'E-Mail';
+$labels['foldertypeevent'] = 'Kalender'; // Termine?
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Aufgaben';
+$labels['foldertypenote'] = 'Notizen';
+$labels['foldertypecontact'] = 'Kontakte';
+$labels['foldertypeconfiguration'] = 'Konfiguration';
+
+$labels['default'] = 'Standard';
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sentitems'] = 'Gesendet';
+$labels['outbox'] = 'Postausgang';
+$labels['wastebasket'] = 'Gelöscht';
+$labels['junkemail'] = 'Spam';
+
+$messages['defaultfolderexists'] = 'Es existiert bereits ein Standardordner für den angegebenen Typ';
+
+?>
diff --git a/lib/plugins/kolab_folders/localization/de_DE.inc b/lib/plugins/kolab_folders/localization/de_DE.inc
new file mode 100644
index 0000000..f9f6e16
--- /dev/null
+++ b/lib/plugins/kolab_folders/localization/de_DE.inc
@@ -0,0 +1,24 @@
+<?php
+
+$labels = array();
+
+$labels['folderctype'] = 'Ordnerinhalt';
+$labels['foldertypemail'] = 'E-Mail';
+$labels['foldertypeevent'] = 'Kalender'; // Termine?
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Aufgaben';
+$labels['foldertypenote'] = 'Notizen';
+$labels['foldertypecontact'] = 'Kontakte';
+$labels['foldertypeconfiguration'] = 'Konfiguration';
+
+$labels['default'] = 'Standard';
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sentitems'] = 'Gesendet';
+$labels['outbox'] = 'Postausgang';
+$labels['wastebasket'] = 'Gelöscht';
+$labels['junkemail'] = 'Spam';
+
+$messages['defaultfolderexists'] = 'Es existiert bereits ein Standardordner für den angegebenen Typ';
+
+?>
diff --git a/lib/plugins/kolab_folders/localization/en_US.inc b/lib/plugins/kolab_folders/localization/en_US.inc
new file mode 100644
index 0000000..70867bc
--- /dev/null
+++ b/lib/plugins/kolab_folders/localization/en_US.inc
@@ -0,0 +1,24 @@
+<?php
+
+$labels = array();
+
+$labels['folderctype'] = 'Content type';
+$labels['foldertypemail'] = 'Mail';
+$labels['foldertypeevent'] = 'Calendar'; // Events?
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Tasks';
+$labels['foldertypenote'] = 'Notes';
+$labels['foldertypecontact'] = 'Contacts';
+$labels['foldertypeconfiguration'] = 'Configuration';
+
+$labels['default'] = 'Default';
+$labels['inbox'] = 'Inbox';
+$labels['drafts'] = 'Drafts';
+$labels['sentitems'] = 'Sent';
+$labels['outbox'] = 'Outbox';
+$labels['wastebasket'] = 'Trash';
+$labels['junkemail'] = 'Junk';
+
+$messages['defaultfolderexists'] = 'There is already default folder of specified type';
+
+?>
diff --git a/lib/plugins/kolab_folders/localization/pl_PL.inc b/lib/plugins/kolab_folders/localization/pl_PL.inc
new file mode 100644
index 0000000..95177cd
--- /dev/null
+++ b/lib/plugins/kolab_folders/localization/pl_PL.inc
@@ -0,0 +1,21 @@
+<?php
+
+$labels = array();
+$labels['folderctype'] = 'Zawartość';
+$labels['foldertypemail'] = 'Poczta';
+$labels['foldertypeevent'] = 'Kalendarz';
+$labels['foldertypejournal'] = 'Dziennik';
+$labels['foldertypetask'] = 'Zadania';
+$labels['foldertypenote'] = 'Notatki';
+$labels['foldertypecontact'] = 'Kontakty';
+$labels['foldertypeconfiguration'] = 'Konfiguracja';
+$labels['default'] = 'Domyślny';
+$labels['inbox'] = 'Odebrane';
+$labels['drafts'] = 'Szkice';
+$labels['sentitems'] = 'Wysłane';
+$labels['outbox'] = 'Wychodzące';
+$labels['wastebasket'] = 'Kosz';
+$labels['junkemail'] = 'Spam';
+$messages['defaultfolderexists'] = 'Folder domyślny dla podanego typu już istnieje';
+
+?>
diff --git a/lib/plugins/kolab_folders/package.xml b/lib/plugins/kolab_folders/package.xml
new file mode 100644
index 0000000..875d614
--- /dev/null
+++ b/lib/plugins/kolab_folders/package.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+ http://pear.php.net/dtd/tasks-1.0.xsd
+ http://pear.php.net/dtd/package-2.0
+ http://pear.php.net/dtd/package-2.0.xsd">
+ <name>kolab_folders</name>
+ <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri>
+ <summary>Type-aware folder management/listing for Kolab</summary>
+ <description>
+ The plugin extends folders handling with features of the Kolab Suite
+ according to specified format (http://www.kolab.org/doc/kolabformat-2.0-html).
+ With this plugin enabled it is possible to:
+ - set/get/change folder's type,
+ - filter folders list by folder type,
+ - style folders list rows (in folder manager),
+ - create default folders with specified type.
+ </description>
+ <lead>
+ <name>Aleksander Machniak</name>
+ <user>machniak</user>
+ <email>machniak@kolabsys.com</email>
+ <active>yes</active>
+ </lead>
+ <date>2012-05-14</date>
+ <version>
+ <release>2.0</release>
+ <api>2.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license>
+ <notes>-</notes>
+ <contents>
+ <dir baseinstalldir="/" name="/">
+ <file name="kolab_folders.php" role="php">
+ <tasks:replace from="@name@" to="name" type="package-info"/>
+ <tasks:replace from="@package_version@" to="version" type="package-info"/>
+ </file>
+ <file name="kolab_folders.js" role="data">
+ <tasks:replace from="@name@" to="name" type="package-info"/>
+ <tasks:replace from="@package_version@" to="version" type="package-info"/>
+ </file>
+ <file name="config.inc.php.dist" role="data"></file>
+ <file name="localization/en_US.inc" role="data"></file>
+ <file name="localization/pl_PL.inc" role="data"></file>
+ <file name="LICENSE" role="data"></file>
+ </dir>
+ <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.1</min>
+ </php>
+ <pearinstaller>
+ <min>1.7.0</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <phprelease />
+</package>
diff --git a/lib/plugins/libkolab/README b/lib/plugins/libkolab/README
new file mode 100644
index 0000000..0a3c0ce
--- /dev/null
+++ b/lib/plugins/libkolab/README
@@ -0,0 +1,43 @@
+libkolab plugin to access to Kolab groupware data
+=================================================
+
+The contained library classes establish a connection to the Kolab server
+and manage the access to the Kolab groupware objects stored in various
+IMAP folders. For reading and writing these objects, the PHP bindings of
+the libkolabxml library are used.
+
+
+REQUIREMENTS
+------------
+* libkolabxml PHP bindings
+ - kolabformat.so loaded into PHP
+ - kolabformat.php placed somewhere in the include_path
+* PEAR: HTTP/Request2
+* PEAR: Net/URL2
+
+* Optional for old format support:
+ Horde Kolab_Format package and all of its dependencies
+ which are at least Horde_(Browser,DOM,NLS,String,Utils)
+
+
+INSTALLATION
+------------
+To use local cache you need to create a dedicated table in Roundcube's database.
+To do so, execute the SQL commands in SQL/<yourdatabase>.sql
+
+
+CONFIGURATION
+-------------
+The following options can be configured in Roundcube's main config file
+or a local config file (config.inc.php) located in the plugin folder.
+
+// Enable caching of Kolab objects in local database
+$rcmail_config['kolab_cache'] = true;
+
+// Optional override of the URL to read and trigger Free/Busy information of Kolab users
+// Defaults to https://<imap-server->/freebusy
+$rcmail_config['kolab_freebusy_server'] = 'https://<some-host>/<freebusy-path>';
+
+// Set this option to disable SSL certificate checks when triggering Free/Busy (enabled by default)
+$rcmail_config['kolab_ssl_verify_peer'] = false;
+
diff --git a/lib/plugins/libkolab/SQL/mysql.sql b/lib/plugins/libkolab/SQL/mysql.sql
new file mode 100644
index 0000000..244ab3d
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/mysql.sql
@@ -0,0 +1,25 @@
+/**
+ * libkolab database schema
+ *
+ * @version @package_version@
+ * @author Thomas Bruederli
+ * @licence GNU AGPL
+ **/
+
+DROP TABLE IF EXISTS `kolab_cache`;
+
+CREATE TABLE `kolab_cache` (
+ `resource` VARCHAR(255) CHARACTER SET ascii NOT NULL,
+ `type` VARCHAR(32) CHARACTER SET ascii NOT NULL,
+ `msguid` BIGINT UNSIGNED NOT NULL,
+ `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL,
+ `created` DATETIME DEFAULT NULL,
+ `changed` DATETIME DEFAULT NULL,
+ `data` TEXT NOT NULL,
+ `xml` TEXT NOT NULL,
+ `dtstart` DATETIME,
+ `dtend` DATETIME,
+ `tags` VARCHAR(255) NOT NULL,
+ `words` TEXT NOT NULL,
+ PRIMARY KEY(`resource`,`type`,`msguid`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
diff --git a/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff b/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff
new file mode 100644
index 0000000..e8b767d
--- /dev/null
+++ b/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff
@@ -0,0 +1,325 @@
+--- Date/Recurrence.php.orig 2012-07-10 19:54:48.000000000 +0200
++++ Date/Recurrence.php 2012-07-10 19:55:38.000000000 +0200
+@@ -95,6 +95,20 @@
+ public $recurData = null;
+
+ /**
++ * BYDAY recurrence number
++ *
++ * @var integer
++ */
++ public $recurNthDay = null;
++
++ /**
++ * BYMONTH recurrence data
++ *
++ * @var array
++ */
++ public $recurMonths = array();
++
++ /**
+ * All the exceptions from recurrence for this event.
+ *
+ * @var array
+@@ -157,6 +171,44 @@
+ }
+
+ /**
++ *
++ * @param integer $nthDay The nth weekday of month to repeat events on
++ */
++ public function setRecurNthWeekday($nth)
++ {
++ $this->recurNthDay = (int)$nth;
++ }
++
++ /**
++ *
++ * @return integer The nth weekday of month to repeat events.
++ */
++ public function getRecurNthWeekday()
++ {
++ return isset($this->recurNthDay) ? $this->recurNthDay : ceil($this->start->mday / 7);
++ }
++
++ /**
++ * Specifies the months for yearly (weekday) recurrence
++ *
++ * @param array $months List of months (integers) this event recurs on.
++ */
++ function setRecurByMonth($months)
++ {
++ $this->recurMonths = (array)$months;
++ }
++
++ /**
++ * Returns a list of months this yearly event recurs on
++ *
++ * @return array List of months (integers) this event recurs on.
++ */
++ function getRecurByMonth()
++ {
++ return $this->recurMonths;
++ }
++
++ /**
+ * Returns the days this event recurs on.
+ *
+ * @return integer A mask consisting of Horde_Date::MASK_* constants
+@@ -546,8 +598,13 @@
+ $estart = clone $this->start;
+
+ // What day of the week, and week of the month, do we recur on?
+- $nth = ceil($this->start->mday / 7);
+- $weekday = $estart->dayOfWeek();
++ if (isset($this->recurNthDay)) {
++ $nth = $this->recurNthDay;
++ $weekday = log($this->recurData, 2);
++ } else {
++ $nth = ceil($this->start->mday / 7);
++ $weekday = $estart->dayOfWeek();
++ }
+
+ // Adjust $estart to be the first candidate.
+ $offset = ($after->month - $estart->month) + ($after->year - $estart->year) * 12;
+@@ -660,8 +717,13 @@
+ $estart = clone $this->start;
+
+ // What day of the week, and week of the month, do we recur on?
+- $nth = ceil($this->start->mday / 7);
+- $weekday = $estart->dayOfWeek();
++ if (isset($this->recurNthDay)) {
++ $nth = $this->recurNthDay;
++ $weekday = log($this->recurData, 2);
++ } else {
++ $nth = ceil($this->start->mday / 7);
++ $weekday = $estart->dayOfWeek();
++ }
+
+ // Adjust $estart to be the first candidate.
+ $offset = floor(($after->year - $estart->year + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval;
+@@ -894,15 +956,6 @@
+ case 'W':
+ $this->setRecurType(self::RECUR_WEEKLY);
+ if (!empty($remainder)) {
+- $maskdays = array(
+- 'SU' => Horde_Date::MASK_SUNDAY,
+- 'MO' => Horde_Date::MASK_MONDAY,
+- 'TU' => Horde_Date::MASK_TUESDAY,
+- 'WE' => Horde_Date::MASK_WEDNESDAY,
+- 'TH' => Horde_Date::MASK_THURSDAY,
+- 'FR' => Horde_Date::MASK_FRIDAY,
+- 'SA' => Horde_Date::MASK_SATURDAY,
+- );
+ $mask = 0;
+ while (preg_match('/^ ?[A-Z]{2} ?/', $remainder, $matches)) {
+ $day = trim($matches[0]);
+@@ -953,7 +1006,10 @@
+ list($year, $month, $mday) = sscanf($remainder, '%04d%02d%02d');
+ $this->setRecurEnd(new Horde_Date(array('year' => $year,
+ 'month' => $month,
+- 'mday' => $mday)));
++ 'mday' => $mday,
++ 'hour' => 23,
++ 'min' => 59,
++ 'sec' => 59)));
+ }
+ }
+ }
+@@ -1049,6 +1105,16 @@
+ // Always default the recurInterval to 1.
+ $this->setRecurInterval(isset($rdata['INTERVAL']) ? $rdata['INTERVAL'] : 1);
+
++ $maskdays = array(
++ 'SU' => Horde_Date::MASK_SUNDAY,
++ 'MO' => Horde_Date::MASK_MONDAY,
++ 'TU' => Horde_Date::MASK_TUESDAY,
++ 'WE' => Horde_Date::MASK_WEDNESDAY,
++ 'TH' => Horde_Date::MASK_THURSDAY,
++ 'FR' => Horde_Date::MASK_FRIDAY,
++ 'SA' => Horde_Date::MASK_SATURDAY,
++ );
++
+ switch (Horde_String::upper($rdata['FREQ'])) {
+ case 'DAILY':
+ $this->setRecurType(self::RECUR_DAILY);
+@@ -1057,15 +1123,6 @@
+ case 'WEEKLY':
+ $this->setRecurType(self::RECUR_WEEKLY);
+ if (isset($rdata['BYDAY'])) {
+- $maskdays = array(
+- 'SU' => Horde_Date::MASK_SUNDAY,
+- 'MO' => Horde_Date::MASK_MONDAY,
+- 'TU' => Horde_Date::MASK_TUESDAY,
+- 'WE' => Horde_Date::MASK_WEDNESDAY,
+- 'TH' => Horde_Date::MASK_THURSDAY,
+- 'FR' => Horde_Date::MASK_FRIDAY,
+- 'SA' => Horde_Date::MASK_SATURDAY,
+- );
+ $days = explode(',', $rdata['BYDAY']);
+ $mask = 0;
+ foreach ($days as $day) {
+@@ -1090,6 +1147,10 @@
+ case 'MONTHLY':
+ if (isset($rdata['BYDAY'])) {
+ $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY);
++ if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) {
++ $this->setRecurOnDay($maskdays[$m[2]]);
++ $this->setRecurNthWeekday($m[1]);
++ }
+ } else {
+ $this->setRecurType(self::RECUR_MONTHLY_DATE);
+ }
+@@ -1100,6 +1161,14 @@
+ $this->setRecurType(self::RECUR_YEARLY_DAY);
+ } elseif (isset($rdata['BYDAY'])) {
+ $this->setRecurType(self::RECUR_YEARLY_WEEKDAY);
++ if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) {
++ $this->setRecurOnDay($maskdays[$m[2]]);
++ $this->setRecurNthWeekday($m[1]);
++ }
++ if ($rdata['BYMONTH']) {
++ $months = explode(',', $rdata['BYMONTH']);
++ $this->setRecurByMonth($months);
++ }
+ } else {
+ $this->setRecurType(self::RECUR_YEARLY_DATE);
+ }
+@@ -1163,13 +1232,19 @@
+ break;
+
+ case self::RECUR_MONTHLY_WEEKDAY:
+- $nth_weekday = (int)($this->start->mday / 7);
+- if (($this->start->mday % 7) > 0) {
+- $nth_weekday++;
++ if (isset($this->recurNthDay)) {
++ $nth_weekday = $this->recurNthDay;
++ $day_of_week = log($this->recurData, 2);
++ } else {
++ $day_of_week = $this->start->dayOfWeek();
++ $nth_weekday = (int)($this->start->mday / 7);
++ if (($this->start->mday % 7) > 0) {
++ $nth_weekday++;
++ }
+ }
+ $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
+ $rrule = 'FREQ=MONTHLY;INTERVAL=' . $this->recurInterval
+- . ';BYDAY=' . $nth_weekday . $vcaldays[$this->start->dayOfWeek()];
++ . ';BYDAY=' . $nth_weekday . $vcaldays[$day_of_week];
+ break;
+
+ case self::RECUR_YEARLY_DATE:
+@@ -1182,15 +1257,22 @@
+ break;
+
+ case self::RECUR_YEARLY_WEEKDAY:
+- $nth_weekday = (int)($this->start->mday / 7);
+- if (($this->start->mday % 7) > 0) {
+- $nth_weekday++;
+- }
++ if (isset($this->recurNthDay)) {
++ $nth_weekday = $this->recurNthDay;
++ $day_of_week = log($this->recurData, 2);
++ } else {
++ $day_of_week = $this->start->dayOfWeek();
++ $nth_weekday = (int)($this->start->mday / 7);
++ if (($this->start->mday % 7) > 0) {
++ $nth_weekday++;
++ }
++ }
++ $months = !empty($this->recurMonths) ? join(',', $this->recurMonths) : $this->start->month;
+ $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
+ $rrule = 'FREQ=YEARLY;INTERVAL=' . $this->recurInterval
+ . ';BYDAY='
+ . $nth_weekday
+- . $vcaldays[$this->start->dayOfWeek()]
++ . $vcaldays[$day_of_week]
+ . ';BYMONTH=' . $this->start->month;
+ break;
+ }
+@@ -1223,6 +1305,21 @@
+
+ $this->setRecurInterval((int)$hash['interval']);
+
++ $month2number = array(
++ 'january' => 1,
++ 'february' => 2,
++ 'march' => 3,
++ 'april' => 4,
++ 'may' => 5,
++ 'june' => 6,
++ 'july' => 7,
++ 'august' => 8,
++ 'september' => 9,
++ 'october' => 10,
++ 'november' => 11,
++ 'december' => 12,
++ );
++
+ $parse_day = false;
+ $set_daymask = false;
+ $update_month = false;
+@@ -1255,11 +1352,9 @@
+
+ case 'weekday':
+ $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY);
+- $nth_weekday = (int)$hash['daynumber'];
+- $hash['daynumber'] = 1;
++ $this->setRecurNthWeekday($hash['daynumber']);
+ $parse_day = true;
+- $update_daynumber = true;
+- $update_weekday = true;
++ $set_daymask = true;
+ break;
+ }
+ break;
+@@ -1297,12 +1392,13 @@
+ }
+
+ $this->setRecurType(self::RECUR_YEARLY_WEEKDAY);
+- $nth_weekday = (int)$hash['daynumber'];
+- $hash['daynumber'] = 1;
++ $this->setRecurNthWeekday($hash['daynumber']);
+ $parse_day = true;
+- $update_month = true;
+- $update_daynumber = true;
+- $update_weekday = true;
++ $set_daymask = true;
++
++ if ($hash['month'] && isset($month2number[$hash['month']])) {
++ $this->setRecurByMonth($month2number[$hash['month']]);
++ }
+ break;
+ }
+ }
+@@ -1368,21 +1464,6 @@
+
+ if ($update_month || $update_daynumber || $update_weekday) {
+ if ($update_month) {
+- $month2number = array(
+- 'january' => 1,
+- 'february' => 2,
+- 'march' => 3,
+- 'april' => 4,
+- 'may' => 5,
+- 'june' => 6,
+- 'july' => 7,
+- 'august' => 8,
+- 'september' => 9,
+- 'october' => 10,
+- 'november' => 11,
+- 'december' => 12,
+- );
+-
+ if (isset($month2number[$hash['month']])) {
+ $this->start->month = $month2number[$hash['month']];
+ }
+@@ -1398,7 +1479,7 @@
+ }
+
+ if ($update_weekday) {
+- $this->start->setNthWeekday($last_found_day, $nth_weekday);
++ $this->setNthWeekday($nth_weekday);
+ }
+ }
+
diff --git a/lib/plugins/libkolab/bin/Date_last_weekday.diff b/lib/plugins/libkolab/bin/Date_last_weekday.diff
new file mode 100644
index 0000000..d260360
--- /dev/null
+++ b/lib/plugins/libkolab/bin/Date_last_weekday.diff
@@ -0,0 +1,37 @@
+--- Date.php.orig 2012-07-10 19:14:26.000000000 +0200
++++ Date.php 2012-07-10 19:16:22.000000000 +0200
+@@ -627,16 +627,25 @@
+ return;
+ }
+
+- $this->_mday = 1;
+- $first = $this->dayOfWeek();
+- if ($weekday < $first) {
+- $this->_mday = 8 + $weekday - $first;
+- } else {
+- $this->_mday = $weekday - $first + 1;
++ if ($nth < 0) { // last $weekday of month
++ $this->_mday = $lastday = Horde_Date_Utils::daysInMonth($this->_month, $this->_year);
++ $last = $this->dayOfWeek();
++ $this->_mday += ($weekday - $last);
++ if ($this->_mday > $lastday)
++ $this->_mday -= 7;
++ }
++ else {
++ $this->_mday = 1;
++ $first = $this->dayOfWeek();
++ if ($weekday < $first) {
++ $this->_mday = 8 + $weekday - $first;
++ } else {
++ $this->_mday = $weekday - $first + 1;
++ }
++ $diff = 7 * $nth - 7;
++ $this->_mday += $diff;
++ $this->_correct(self::MASK_DAY, $diff < 0);
+ }
+- $diff = 7 * $nth - 7;
+- $this->_mday += $diff;
+- $this->_correct(self::MASK_DAY, $diff < 0);
+ }
+
+ /**
diff --git a/lib/plugins/libkolab/bin/get_horde_date.sh b/lib/plugins/libkolab/bin/get_horde_date.sh
new file mode 100755
index 0000000..b8e663d
--- /dev/null
+++ b/lib/plugins/libkolab/bin/get_horde_date.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+# Copy Horde_Date_Recurrence classes and dependencies to the given target directory.
+# This will create a standalone copy of the classes requried for date recurrence computation.
+
+SRCDIR=$1
+DESTDIR=$2
+BINDIR=`dirname $0`
+
+if [ ! -d "$SRCDIR" -o ! -d "$DESTDIR" ]; then
+ echo "Usage: get_horde_date.sh SRCDIR DESTDIR"
+ echo "Please enter valid source and destination directories for the Horde libs"
+ exit 1
+fi
+
+
+# concat Date.php and Date/Utils.php
+HORDE_DATE="$DESTDIR/Horde_Date.php"
+
+echo "<?php
+
+/**
+ * This is a concatenated copy of the following files:
+ * Horde/Date.php, Horde/Date/Utils.php
+ * Pull the latest version of these files from the PEAR channel of the Horde
+ * project at http://pear.horde.org by installing the Horde_Date package.
+ */
+" > $HORDE_DATE
+
+patch $SRCDIR/Date.php $BINDIR/Date_last_weekday.diff --output=$HORDE_DATE.patched
+sed 's/<?php//; s/?>//' $HORDE_DATE.patched >> $HORDE_DATE
+sed 's/<?php//; s/?>//' $SRCDIR/Date/Utils.php >> $HORDE_DATE
+
+# copy and patch Date/Recurrence.php
+HORDE_DATE_RECURRENCE="$DESTDIR/Horde_Date_Recurrence.php"
+
+echo "<?php
+
+/**
+ * This is a modified copy of Horde/Date/Recurrence.php
+ * Pull the latest version of this file from the PEAR channel of the Horde
+ * project at http://pear.horde.org by installing the Horde_Date package.
+ */
+
+if (!class_exists('Horde_Date'))
+ require_once(dirname(__FILE__) . '/Horde_Date.php');
+
+// minimal required implementation of Horde_Date_Translation to avoid a huge dependency nightmare
+class Horde_Date_Translation
+{
+ function t(\$arg) { return \$arg; }
+ function ngettext(\$sing, \$plur, \$num) { return (\$num > 1 ? \$plur : \$sing); }
+}
+" > $HORDE_DATE_RECURRENCE
+
+patch $SRCDIR/Date/Recurrence.php $BINDIR/Date_Recurrence_weekday.diff --output=$HORDE_DATE_RECURRENCE.patched
+sed 's/<?php//; s/?>//' $HORDE_DATE_RECURRENCE.patched >> $HORDE_DATE_RECURRENCE
+
+# remove dependency to Horde_String
+sed -i '' "s/Horde_String::/strto/" $HORDE_DATE_RECURRENCE
+
+rm $DESTDIR/Horde_Date*.patched
+
+
diff --git a/lib/plugins/libkolab/bin/modcache.sh b/lib/plugins/libkolab/bin/modcache.sh
new file mode 100755
index 0000000..04d36a5
--- /dev/null
+++ b/lib/plugins/libkolab/bin/modcache.sh
@@ -0,0 +1,191 @@
+#!/usr/bin/env php -d enable_dl=On
+<?php
+
+/**
+ * Kolab storage cache modification script
+ *
+ * @version 3.0
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 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/>.
+ */
+
+define('INSTALL_PATH', realpath('.') . '/' );
+ini_set('display_errors', 1);
+
+if (!file_exists(INSTALL_PATH . 'program/include/clisetup.php'))
+ die("Execute this from the Roundcube installation dir!\n\n");
+
+require_once INSTALL_PATH . 'program/include/clisetup.php';
+
+function print_usage()
+{
+ print "Usage: modcache.sh [OPTIONS] ACTION [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 = 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();
+
+
+/*
+ * Script controller
+ */
+switch (strtolower($action)) {
+
+/*
+ * Clear/expunge all cache records
+ */
+case 'expunge':
+ $expire = strtotime(!empty($opts[2]) ? $opts[2] : 'now - 10 days');
+ $sql_add = " AND created <= '" . date('Y-m-d 00:00:00', $expire) . "'";
+ if ($opts['limit']) {
+ $sql_add .= ' LIMIT ' . intval($opts['limit']);
+ }
+
+case 'clear':
+ // connect to database
+ $db = $rcmail->get_dbh();
+ $db->db_connect('w');
+ if (!$db->is_connected() || $db->is_error())
+ die("No DB connection\n");
+
+ $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','distribution-list','event','task','configuration');
+ $folder_types_db = array_map(array($db, 'quote'), $folder_types);
+
+ if ($opts['all']) {
+ $sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ")";
+ }
+ else if ($opts['username']) {
+ $sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?";
+ }
+
+ if ($sql_query) {
+ $db->query($sql_query . $sql_add, resource_prefix($opts).'%');
+ echo $db->affected_rows() . " records deleted from 'kolab_cache'\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']) : array('contact','event','task','configuration');
+ 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;
+
+
+/*
+ * Unknown action => show usage
+ */
+default:
+ print_usage();
+ exit;
+}
+
+
+/**
+ * Compose cache resource URI prefix for the given user credentials
+ */
+function resource_prefix($opts)
+{
+ return 'imap://' . 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'] = 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'];
+
+ // let the kolab_auth plugin do its magic
+ $auth = $rcmail->plugins->exec_hook('authenticate', array(
+ 'host' => trim($opts['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($auth['host'], $auth['user'], $auth['pass'], 143, false)) {
+ if ($opts['verbose'])
+ echo "IMAP login succeeded.\n";
+ if (($user = rcube_user::query($opts['username'], $auth['host'])) && $user->ID)
+ $rcmail->set_user($user);
+ }
+ else
+ die("Login to IMAP server failed!\n");
+ }
+ else {
+ die("Invalid login credentials!\n");
+ }
+
+ return $auth['valid'];
+}
+
diff --git a/lib/plugins/libkolab/config.inc.php.dist b/lib/plugins/libkolab/config.inc.php.dist
new file mode 100644
index 0000000..fedf793
--- /dev/null
+++ b/lib/plugins/libkolab/config.inc.php.dist
@@ -0,0 +1,9 @@
+<?php
+ /* Configuration for libkolab */
+
+ $rcmail_config['kolab_cache'] = true;
+
+ $rcmail_config['kolab_freebusy_server'] = 'https://' . $_SESSION['imap_host'] . '/freebusy';
+ $rcmail_config['kolab_ssl_verify_peer'] = true;
+
+?>
diff --git a/lib/kolab/Horde_Date.php b/lib/plugins/libkolab/lib/Horde_Date.php
similarity index 100%
rename from lib/kolab/Horde_Date.php
rename to lib/plugins/libkolab/lib/Horde_Date.php
diff --git a/lib/kolab/Horde_Date_Recurrence.php b/lib/plugins/libkolab/lib/Horde_Date_Recurrence.php
similarity index 100%
rename from lib/kolab/Horde_Date_Recurrence.php
rename to lib/plugins/libkolab/lib/Horde_Date_Recurrence.php
diff --git a/lib/kolab/Horde_Kolab_Format_XML_configuration.php b/lib/plugins/libkolab/lib/Horde_Kolab_Format_XML_configuration.php
similarity index 100%
rename from lib/kolab/Horde_Kolab_Format_XML_configuration.php
rename to lib/plugins/libkolab/lib/Horde_Kolab_Format_XML_configuration.php
diff --git a/lib/kolab/kolab_date_recurrence.php b/lib/plugins/libkolab/lib/kolab_date_recurrence.php
similarity index 100%
rename from lib/kolab/kolab_date_recurrence.php
rename to lib/plugins/libkolab/lib/kolab_date_recurrence.php
diff --git a/lib/kolab/kolab_format.php b/lib/plugins/libkolab/lib/kolab_format.php
similarity index 100%
rename from lib/kolab/kolab_format.php
rename to lib/plugins/libkolab/lib/kolab_format.php
diff --git a/lib/kolab/kolab_format_configuration.php b/lib/plugins/libkolab/lib/kolab_format_configuration.php
similarity index 100%
rename from lib/kolab/kolab_format_configuration.php
rename to lib/plugins/libkolab/lib/kolab_format_configuration.php
diff --git a/lib/kolab/kolab_format_contact.php b/lib/plugins/libkolab/lib/kolab_format_contact.php
similarity index 100%
rename from lib/kolab/kolab_format_contact.php
rename to lib/plugins/libkolab/lib/kolab_format_contact.php
diff --git a/lib/kolab/kolab_format_distributionlist.php b/lib/plugins/libkolab/lib/kolab_format_distributionlist.php
similarity index 100%
rename from lib/kolab/kolab_format_distributionlist.php
rename to lib/plugins/libkolab/lib/kolab_format_distributionlist.php
diff --git a/lib/kolab/kolab_format_event.php b/lib/plugins/libkolab/lib/kolab_format_event.php
similarity index 100%
rename from lib/kolab/kolab_format_event.php
rename to lib/plugins/libkolab/lib/kolab_format_event.php
diff --git a/lib/kolab/kolab_format_journal.php b/lib/plugins/libkolab/lib/kolab_format_journal.php
similarity index 100%
rename from lib/kolab/kolab_format_journal.php
rename to lib/plugins/libkolab/lib/kolab_format_journal.php
diff --git a/lib/kolab/kolab_format_note.php b/lib/plugins/libkolab/lib/kolab_format_note.php
similarity index 100%
rename from lib/kolab/kolab_format_note.php
rename to lib/plugins/libkolab/lib/kolab_format_note.php
diff --git a/lib/kolab/kolab_format_task.php b/lib/plugins/libkolab/lib/kolab_format_task.php
similarity index 100%
rename from lib/kolab/kolab_format_task.php
rename to lib/plugins/libkolab/lib/kolab_format_task.php
diff --git a/lib/kolab/kolab_format_xcal.php b/lib/plugins/libkolab/lib/kolab_format_xcal.php
similarity index 100%
rename from lib/kolab/kolab_format_xcal.php
rename to lib/plugins/libkolab/lib/kolab_format_xcal.php
diff --git a/lib/kolab/kolab_storage.php b/lib/plugins/libkolab/lib/kolab_storage.php
similarity index 98%
rename from lib/kolab/kolab_storage.php
rename to lib/plugins/libkolab/lib/kolab_storage.php
index b29b416..57e5491 100644
--- a/lib/kolab/kolab_storage.php
+++ b/lib/plugins/libkolab/lib/kolab_storage.php
@@ -1,652 +1,645 @@
<?php
/**
* Kolab storage class providing static methods to access groupware objects on a Kolab server.
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 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_storage
{
const CTYPE_KEY = '/shared/vendor/kolab/folder-type';
const CTYPE_KEY_PRIVATE = '/private/vendor/kolab/folder-type';
const COLOR_KEY_SHARED = '/shared/vendor/kolab/color';
const COLOR_KEY_PRIVATE = '/private/vendor/kolab/color';
const SERVERSIDE_SUBSCRIPTION = 0;
const CLIENTSIDE_SUBSCRIPTION = 1;
public static $last_error;
private static $ready = false;
private static $config;
private static $cache;
private static $imap;
/**
* Setup the environment needed by the libs
*/
public static function setup()
{
if (self::$ready)
return true;
$rcmail = rcube::get_instance();
self::$config = $rcmail->config;
self::$imap = $rcmail->get_storage();
self::$ready = class_exists('kolabformat') &&
(self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
if (self::$ready) {
// set imap options
self::$imap->set_options(array(
'skip_deleted' => true,
'threading' => false,
));
self::$imap->set_pagesize(9999);
}
return self::$ready;
}
/**
* Get a list of storage folders for the given data type
*
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
*
* @return array List of Kolab_Folder objects (folder names in UTF7-IMAP)
*/
public static function get_folders($type)
{
$folders = $folderdata = array();
if (self::setup()) {
foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) {
$folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]);
}
}
return $folders;
}
/**
* Getter for a specific storage folder
*
* @param string IMAP folder to access (UTF7-IMAP)
* @return object kolab_storage_folder The folder object
*/
public static function get_folder($folder)
{
return self::setup() ? new kolab_storage_folder($folder) : null;
}
/**
* Getter for a single Kolab object, identified by its UID.
* This will search all folders storing objects of the given type.
*
* @param string Object UID
* @param string Object type (contact,distribution-list,event,task,note)
* @return array The Kolab object represented as hash array or false if not found
*/
public static function get_object($uid, $type)
{
self::setup();
$folder = null;
foreach ((array)self::list_folders('', '*', $type) as $foldername) {
if (!$folder)
$folder = new kolab_storage_folder($foldername);
else
$folder->set_folder($foldername);
if ($object = $folder->get_object($uid))
return $object;
}
return false;
}
/**
*
*/
public static function get_freebusy_server()
{
return unslashify(self::$config->get('kolab_freebusy_server', 'https://' . $_SESSION['imap_host'] . '/freebusy'));
}
/**
* Compose an URL to query the free/busy status for the given user
*/
public static function get_freebusy_url($email)
{
return self::get_freebusy_server() . '/' . $email . '.ifb';
}
/**
* Creates folder ID from folder name
*
* @param string $folder Folder name (UTF7-IMAP)
*
* @return string Folder ID string
*/
public static function folder_id($folder)
{
return asciiwords(strtr($folder, '/.-', '___'));
}
/**
* Deletes IMAP folder
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public static function folder_delete($name)
{
// clear cached entries first
if ($folder = self::get_folder($name))
$folder->cache->purge();
$success = self::$imap->delete_folder($name);
self::$last_error = self::$imap->get_error_str();
return $success;
}
/**
* Creates IMAP folder
*
* @param string $name Folder name (UTF7-IMAP)
* @param string $type Folder type
* @param bool $subscribed Sets folder subscription
*
* @return bool True on success, false on failure
*/
public static function folder_create($name, $type = null, $subscribed = false)
{
self::setup();
if ($saved = self::$imap->create_folder($name, $subscribed)) {
// set metadata for folder type
if ($type) {
$saved = self::set_folder_type($name, $type);
// revert if metadata could not be set
if (!$saved) {
self::$imap->delete_folder($name);
}
}
}
if ($saved) {
return true;
}
self::$last_error = self::$imap->get_error_str();
return false;
}
/**
* Renames IMAP folder
*
* @param string $oldname Old folder name (UTF7-IMAP)
* @param string $newname New folder name (UTF7-IMAP)
*
* @return bool True on success, false on failure
*/
public static function folder_rename($oldname, $newname)
{
self::setup();
$success = self::$imap->rename_folder($oldname, $newname);
self::$last_error = self::$imap->get_error_str();
return $success;
}
/**
* Rename or Create a new IMAP folder.
*
* Does additional checks for permissions and folder name restrictions
*
* @param array Hash array with folder properties and metadata
* - name: Folder name
* - oldname: Old folder name when changed
* - parent: Parent folder to create the new one in
* - type: Folder type to create
* @return mixed New folder name or False on failure
*/
public static function folder_update(&$prop)
{
self::setup();
$folder = rcube_charset::convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP');
$oldfolder = $prop['oldname']; // UTF7
$parent = $prop['parent']; // UTF7
$delimiter = self::$imap->get_hierarchy_delimiter();
if (strlen($oldfolder)) {
$options = self::$imap->folder_info($oldfolder);
}
if (!empty($options) && ($options['norename'] || $options['protected'])) {
}
// sanity checks (from steps/settings/save_folder.inc)
else if (!strlen($folder)) {
self::$last_error = 'cannotbeempty';
return false;
}
else if (strlen($folder) > 128) {
self::$last_error = 'nametoolong';
return false;
}
else {
// these characters are problematic e.g. when used in LIST/LSUB
foreach (array($delimiter, '%', '*') as $char) {
if (strpos($folder, $delimiter) !== false) {
self::$last_error = 'forbiddencharacter';
return false;
}
}
}
if (!empty($options) && ($options['protected'] || $options['norename'])) {
$folder = $oldfolder;
}
else if (strlen($parent)) {
$folder = $parent . $delimiter . $folder;
}
else {
// add namespace prefix (when needed)
$folder = self::$imap->mod_folder($folder, 'in');
}
// Check access rights to the parent folder
if (strlen($parent) && (!strlen($oldfolder) || $oldfolder != $folder)) {
$parent_opts = self::$imap->folder_info($parent);
if ($parent_opts['namespace'] != 'personal'
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
) {
self::$last_error = 'No permission to create folder';
return false;
}
}
// update the folder name
if (strlen($oldfolder)) {
if ($oldfolder != $folder) {
$result = self::folder_rename($oldfolder, $folder);
}
else
$result = true;
}
// create new folder
else {
$result = self::folder_create($folder, $prop['type'], $prop['subscribed'] === self::SERVERSIDE_SUBSCRIPTION);
}
// save color in METADATA
// TODO: also save 'showalarams' and other properties here
if ($result && $prop['color']) {
$meta_saved = false;
$ns = self::$imap->folder_namespace($folder);
if ($ns == 'personal') // save in shared namespace for personal folders
$meta_saved = self::$imap->set_metadata($folder, array(self::COLOR_KEY_SHARED => $prop['color']));
if (!$meta_saved) // try in private namespace
$meta_saved = self::$imap->set_metadata($folder, array(self::COLOR_KEY_PRIVATE => $prop['color']));
if ($meta_saved)
unset($prop['color']); // unsetting will prevent fallback to local user prefs
}
return $result ? $folder : false;
}
/**
* Getter for human-readable name of Kolab object (folder)
* See http://wiki.kolab.org/UI-Concepts/Folder-Listing for reference
*
* @param string $folder IMAP folder name (UTF7-IMAP)
* @param string $folder_ns Will be set to namespace name of the folder
*
* @return string Name of the folder-object
*/
public static function object_name($folder, &$folder_ns=null)
{
self::setup();
$found = false;
$namespace = self::$imap->get_namespace();
if (!empty($namespace['shared'])) {
foreach ($namespace['shared'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
$prefix = '';
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
$found = true;
$folder_ns = 'shared';
break;
}
}
}
if (!$found && !empty($namespace['other'])) {
foreach ($namespace['other'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$delim = $ns[1];
// get username
$pos = strpos($folder, $delim);
if ($pos) {
$prefix = '('.substr($folder, 0, $pos).') ';
$folder = substr($folder, $pos+1);
}
else {
$prefix = '('.$folder.')';
$folder = '';
}
$found = true;
$folder_ns = 'other';
break;
}
}
}
if (!$found && !empty($namespace['personal'])) {
foreach ($namespace['personal'] as $ns) {
if (strlen($ns[0]) && strpos($folder, $ns[0]) === 0) {
// remove namespace prefix
$folder = substr($folder, strlen($ns[0]));
$prefix = '';
$delim = $ns[1];
$found = true;
break;
}
}
}
if (empty($delim))
$delim = self::$imap->get_hierarchy_delimiter();
$folder = rcube_charset::convert($folder, 'UTF7-IMAP');
$folder = html::quote($folder);
$folder = str_replace(html::quote($delim), ' &raquo; ', $folder);
if ($prefix)
$folder = html::quote($prefix) . ' ' . $folder;
if (!$folder_ns)
$folder_ns = 'personal';
return $folder;
}
/**
* Helper method to generate a truncated folder name to display
*/
public static function folder_displayname($origname, &$names)
{
$name = $origname;
// find folder prefix to truncate
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($name, $names[$i] . ' &raquo; ') === 0) {
$length = strlen($names[$i] . ' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($name, $length);
break;
}
}
$names[] = $origname;
return $name;
}
/**
* Creates a SELECT field with folders list
*
* @param string $type Folder type
* @param array $attrs SELECT field attributes (e.g. name)
* @param string $current The name of current folder (to skip it)
*
* @return html_select SELECT object
*/
public static function folder_selector($type, $attrs, $current = '')
{
// get all folders of specified type
$folders = self::get_folders($type);
$delim = self::$imap->get_hierarchy_delimiter();
$names = array();
$len = strlen($current);
if ($len && ($rpos = strrpos($current, $delim))) {
$parent = substr($current, 0, $rpos);
$p_len = strlen($parent);
}
// Filter folders list
foreach ($folders as $c_folder) {
$name = $c_folder->name;
// skip current folder and it's subfolders
if ($len && ($name == $current || strpos($name, $current.$delim) === 0)) {
continue;
}
// always show the parent of current folder
if ($p_len && $name == $parent) { }
// skip folders where user have no rights to create subfolders
else if ($c_folder->get_owner() != $_SESSION['username']) {
$rights = $c_folder->get_myrights();
if (!preg_match('/[ck]/', $rights)) {
continue;
}
}
$names[$name] = rcube_charset::convert($name, 'UTF7-IMAP');
}
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
if ($p_len && !isset($names[$parent])) {
$names[$parent] = rcube_charset::convert($parent, 'UTF7-IMAP');
}
// Sort folders list
asort($names, SORT_LOCALE_STRING);
$folders = array_keys($names);
$names = array();
// Build SELECT field of parent folder
$attrs['is_escaped'] = true;
$select = new html_select($attrs);
$select->add('---', '');
foreach ($folders as $name) {
$imap_name = $name;
$name = $origname = self::object_name($name);
// find folder prefix to truncate
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($name, $names[$i].' &raquo; ') === 0) {
$length = strlen($names[$i].' &raquo; ');
$prefix = substr($name, 0, $length);
$count = count(explode(' &raquo; ', $prefix));
$name = str_repeat('&nbsp;&nbsp;', $count-1) . '&raquo; ' . substr($name, $length);
break;
}
}
$names[] = $origname;
$select->add($name, $imap_name);
}
return $select;
}
/**
* Returns a list of folder names
*
* @param string Optional root folder
* @param string Optional name pattern
* @param string Data type to list folders for (contact,distribution-list,event,task,note,mail)
* @param string Enable to return subscribed folders only
* @param array Will be filled with folder-types data
*
* @return array List of folders
*/
public static function list_folders($root = '', $mbox = '*', $filter = null, $subscribed = false, &$folderdata = array())
{
if (!self::setup()) {
return null;
}
if (!$filter) {
// Get ALL folders list, standard way
if ($subscribed) {
return self::$imap->list_folders_subscribed($root, $mbox);
}
else {
return self::$imap->list_folders($root, $mbox);
}
}
$prefix = $root . $mbox;
// get folders types
$folderdata = self::$imap->get_metadata($prefix, array(self::CTYPE_KEY, self::CTYPE_KEY_PRIVATE));
if (!is_array($folderdata)) {
return array();
}
$folderdata = array_map(array('kolab_storage', 'folder_select_metadata'), $folderdata);
$regexp = '/^' . preg_quote($filter, '/') . '(\..+)?$/';
// In some conditions we can skip LIST command (?)
if ($subscribed == false && $filter != 'mail' && $prefix == '*') {
foreach ($folderdata as $folder => $type) {
if (!preg_match($regexp, $type)) {
unset($folderdata[$folder]);
}
}
return array_keys($folderdata);
}
// Get folders list
if ($subscribed) {
$folders = self::$imap->list_folders_subscribed($root, $mbox);
}
else {
$folders = self::$imap->list_folders($root, $mbox);
}
// In case of an error, return empty list (?)
if (!is_array($folders)) {
return array();
}
// Filter folders list
foreach ($folders as $idx => $folder) {
$type = $folderdata[$folder];
if ($filter == 'mail' && empty($type)) {
continue;
}
if (empty($type) || !preg_match($regexp, $type)) {
unset($folders[$idx]);
}
}
return $folders;
}
/**
* Callback for array_map to select the correct annotation value
*/
static function folder_select_metadata($types)
{
- if (!empty($types[self::CTYPE_KEY_PRIVATE])) {
- return $types[self::CTYPE_KEY_PRIVATE];
- }
- else if (!empty($types[self::CTYPE_KEY])) {
- list($ctype, $suffix) = explode('.', $types[self::CTYPE_KEY]);
- return $ctype;
- }
- return null;
+ return $types[self::CTYPE_KEY_PRIVATE] ?: $types[self::CTYPE_KEY];
}
/**
* Returns type of IMAP folder
*
* @param string $folder Folder name (UTF7-IMAP)
*
* @return string Folder type
*/
static function folder_type($folder)
{
self::setup();
$metadata = self::$imap->get_metadata($folder, array(self::CTYPE_KEY, self::CTYPE_KEY_PRIVATE));
if (!is_array($metadata)) {
return null;
}
if (!empty($metadata[$folder])) {
return self::folder_select_metadata($metadata[$folder]);
}
return 'mail';
}
/**
* Sets folder content-type.
*
* @param string $folder Folder name
* @param string $type Content type
*
* @return boolean True on success
*/
static function set_folder_type($folder, $type='mail')
{
list($ctype, $subtype) = explode('.', $type);
$success = self::$imap->set_metadata($folder, array(self::CTYPE_KEY => $ctype, self::CTYPE_KEY_PRIVATE => $subtype ? $type : null));
if (!$success) // fallback: only set private annotation
$success |= self::$imap->set_metadata($folder, array(self::CTYPE_KEY_PRIVATE => $type));
return $success;
}
}
diff --git a/lib/kolab/kolab_storage_cache.php b/lib/plugins/libkolab/lib/kolab_storage_cache.php
similarity index 100%
rename from lib/kolab/kolab_storage_cache.php
rename to lib/plugins/libkolab/lib/kolab_storage_cache.php
diff --git a/lib/kolab/kolab_storage_folder.php b/lib/plugins/libkolab/lib/kolab_storage_folder.php
similarity index 100%
rename from lib/kolab/kolab_storage_folder.php
rename to lib/plugins/libkolab/lib/kolab_storage_folder.php
diff --git a/lib/plugins/libkolab/libkolab.php b/lib/plugins/libkolab/libkolab.php
new file mode 100644
index 0000000..3709ee0
--- /dev/null
+++ b/lib/plugins/libkolab/libkolab.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Kolab core library
+ *
+ * Plugin to setup a basic environment for the interaction with a Kolab server.
+ * Other Kolab-related plugins will depend on it and can use the library classes
+ *
+ * @version @package_version@
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 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 libkolab extends rcube_plugin
+{
+ /**
+ * Required startup method of a Roundcube plugin
+ */
+ public function init()
+ {
+ // load local config
+ $this->load_config();
+
+ $this->add_hook('storage_init', array($this, 'storage_init'));
+
+ // extend include path to load bundled lib classes
+ $include_path = $this->home . '/lib' . PATH_SEPARATOR . ini_get('include_path');
+ set_include_path($include_path);
+
+ $rcmail = rcube::get_instance();
+ try {
+ kolab_format::$timezone = new DateTimeZone($rcmail->config->get('timezone', 'GMT'));
+ }
+ catch (Exception $e) {
+ raise_error($e, true);
+ kolab_format::$timezone = new DateTimeZone('GMT');
+ }
+
+ // load (old) dependencies if available
+ if (@include_once('Horde/Util.php')) {
+ include_once 'Horde/Kolab/Format.php';
+ include_once 'Horde/Kolab/Format/XML.php';
+ include_once 'Horde/Kolab/Format/XML/contact.php';
+ include_once 'Horde/Kolab/Format/XML/event.php';
+ include_once 'Horde_Kolab_Format_XML_configuration.php';
+
+ String::setDefaultCharset('UTF-8');
+ }
+ }
+
+ /**
+ * Hook into IMAP FETCH HEADER.FIELDS command and request Kolab-specific headers
+ */
+ function storage_init($p)
+ {
+ $p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE X-KOLAB-MIME-VERSION');
+ return $p;
+ }
+
+
+}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Mar 18, 11:56 PM (9 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
458432
Default Alt Text
(192 KB)

Event Timeline