diff --git a/plugins/wap_client/composer.json b/plugins/wap_client/composer.json
new file mode 100644
index 00000000..a626fbde
--- /dev/null
+++ b/plugins/wap_client/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "kolab/wap_client",
+ "type": "roundcube-plugin",
+ "description": "Kolab Web Admin Client",
+ "homepage": "https://git.kolab.org/diffusion/RPK/",
+ "license": "AGPLv3",
+ "version": "0.1.0",
+ "authors": [
+ {
+ "name": "Aleksander Machniak",
+ "email": "machniak@kolabsys.com",
+ "role": "Lead"
+ }
+ ],
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "http://plugins.roundcube.net"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "roundcube/plugin-installer": ">=0.1.3",
+ "kolab/libkolab": ">=3.2.8"
+ }
+}
diff --git a/plugins/wap_client/config.inc.php.dist b/plugins/wap_client/config.inc.php.dist
new file mode 100644
index 00000000..7ce30c1e
--- /dev/null
+++ b/plugins/wap_client/config.inc.php.dist
@@ -0,0 +1,49 @@
+
+ *
+ * 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 .
+ */
+
+// Kolab WAP API URL
+$config['wap_client_uri'] = '/kolab-webadmin/api';
+
+// Log conversation with WAP
+$config['wap_client_debug'] = false;
+
+// Domain root DN
+$config['wap_client_root_dn'] = 'dc=example,dc=org';
+
+// Domain base DN
+$config['wap_client_base_dn'] = 'dc=example,dc=org';
+
+// Accounts definition
+$config['wap_client_accounts'] = array();
+/*
+$config['wap_client_accounts'] = array(
+ 'Lite' => array(
+ 'description' => 'Mail account with 2GB quota',
+ 'nsroledn' => array('cn=imap-user,$base_dn', 'cn=active-user,$root_dn'),
+ 'mailquota' => 2097152,
+ ),
+ 'Professional' => array(
+ 'description' => 'Professional groupware account with 10GB quota',
+ 'nsroledn' => array('cn=activesync-user,$base_dn', 'cn=kolab-user,$base_dn', 'cn=active-user,$root_dn'),
+ 'mailquota' => 10485760,
+ ),
+);
+*/
diff --git a/plugins/wap_client/localization/en_US.inc b/plugins/wap_client/localization/en_US.inc
new file mode 100644
index 00000000..5400e2ae
--- /dev/null
+++ b/plugins/wap_client/localization/en_US.inc
@@ -0,0 +1,14 @@
+
+ *
+ * Copyright (C) 2016, Kolab Systems AG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+class wap_client extends rcube_plugin
+{
+ public $task = 'settings';
+ public $noajax = true;
+
+ protected $rc;
+ protected $wap;
+ protected $userinfo;
+ protected $token;
+
+
+ /**
+ * Initializes the plugin
+ */
+ function init()
+ {
+ $this->rc = rcmail::get_instance();
+
+ $this->add_hook('preferences_list', array($this, 'prefs_table'));
+ $this->add_hook('preferences_save', array($this, 'save_prefs'));
+ }
+
+ /**
+ * Hook to inject plugin-specific user settings
+ */
+ public function prefs_table($args)
+ {
+ global $CURR_SECTION;
+
+ if ($args['section'] != 'server') {
+ return;
+ }
+
+ $this->load_config();
+
+ $accounts = (array) $this->rc->config->get('wap_client_accounts');
+
+ if (empty($accounts)) {
+ return;
+ }
+
+ $this->add_texts('localization');
+
+ if ($CURR_SECTION) {
+ $account_type = $this->get_account_type();
+ $_SESSION['wap_client_account_type'] = $account_type;
+ }
+
+ $input = new html_radiobutton(array('name' => '_account_type', 'style' => 'display:block; float:left'));
+ $content = '';
+
+ foreach ($accounts as $idx => $def) {
+ $id = 'account_type_' . strtolower(asciiwords($idx, true));
+ $name = $idx;
+ $name = $this->rc->text_exists('wap_client.account.' . $name) ? $this->gettext('account.' . $name) : $name;
+ $desc = $this->rc->text_exists('wap_client.accountdesc.' . $name) ? $this->gettext('accountdesc.' . $name) : $def['description'];
+
+ $name = html::span(array('style' => 'font-weight: bold'), rcube::Q($name));
+ if ($desc) {
+ $name .= html::br() . html::span(null, rcube::Q($desc));
+ }
+
+ $label_style = 'display:block; margin: 5px 0; padding-left: 30px';
+ $content .= $input->show($account_type, array('value' => $idx, 'id' => $id))
+ . html::label(array('for' => $id, 'style' => $label_style), $name);
+ }
+
+ $conf = array(
+ 'account' => array(
+ 'name' => rcube::Q($this->gettext('accountoptions')),
+ 'options' => array(
+ 'account_type' => array(
+ 'title' => $this->gettext('accounttype'),
+ 'content' => $content,
+ )
+ )
+ )
+ );
+
+ $args['blocks'] = array_merge($conf, $args['blocks']);
+
+ return $args;
+ }
+
+ /**
+ * Hook to save plugin-specific user settings
+ */
+ public function save_prefs($args)
+ {
+ if ($args['section'] != 'server') {
+ return;
+ }
+
+ $account_type = rcube_utils::get_input_value('_account_type', rcube_utils::INPUT_POST);
+
+ if (!$account_type || $account_type == $_SESSION['wap_client_account_type']) {
+ return;
+ }
+
+ $this->add_texts('localization');
+
+ $this->set_account_type($account_type);
+ }
+
+ /**
+ * Get current account type (from WAP)
+ */
+ protected function get_account_type()
+ {
+ $this->init_wap();
+
+ if (empty($this->userinfo)) {
+ $this->rc->output->show_message($this->gettext('failedtypedetection'), 'warning');
+ return;
+ }
+
+ $roles = (array) $this->userinfo['nsroledn'];
+ $accounts = (array) $this->rc->config->get('wap_client_accounts');
+ $root_dn = $this->rc->config->get('wap_client_root_dn');
+ $base_dn = $this->rc->config->get('wap_client_base_dn');
+
+ foreach ($accounts as $name => $account) {
+ foreach ((array) $account['nsroledn'] as $role) {
+ $value = str_replace('$base_dn', $base_dn, $value);
+ $value = str_replace('$root_dn', $root_dn, $value);
+
+ if (!in_array($value, $roles)) {
+ continue 2;
+ }
+ }
+
+ return $name;
+ }
+ }
+
+ /**
+ * Set account type (in WAP)
+ */
+ protected function set_account_type($type)
+ {
+ if (!$this->init_wap()) {
+ return false;
+ }
+
+ $query = $this->userinfo;
+ $accounts = (array) $this->rc->config->get('wap_client_accounts');
+ $root_dn = $this->rc->config->get('wap_client_root_dn');
+ $base_dn = $this->rc->config->get('wap_client_base_dn');
+ $account = $accounts[$type];
+
+ if (empty($account)) {
+ $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning');
+ return;
+ }
+
+ unset($account['description']);
+
+ foreach ($account as $attr => $value) {
+ switch ($attr) {
+ case 'nsroledn':
+ $value = array();
+ foreach ((array) $account['nsroledn'] as $role) {
+ $role = str_replace('$base_dn', $base_dn, $role);
+ $role = str_replace('$root_dn', $root_dn, $role);
+ $value[] = $role;
+ }
+
+ default:
+ $query[$attr] = $value;
+ }
+ }
+
+ $response = $this->post('user.edit', $query);
+
+ if (!$response || $response['status'] != 'OK') {
+ $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning');
+ return;
+ }
+
+ $this->userinfo = $query;
+ }
+
+ /**
+ * Initialize WAP connection and user session
+ */
+ protected function init_wap()
+ {
+ if ($this->wap) {
+ return $this->wap;
+ }
+
+ $this->load_config();
+ $this->require_plugin('libkolab');
+
+ $uri = $this->rc->config->get('wap_client_uri');
+ $user = $this->rc->get_user_name();
+ $pass = $this->rc->decrypt($_SESSION['password']);
+
+ if (!$uri) {
+ rcube::raise_error("wap_client_uri is not set", true, false);
+ return;
+ }
+
+ // get HTTP_Request2 object
+ $this->uri = rcube_utils::resolve_url($uri);
+ $this->wap = libkolab::http_request($this->uri);
+
+ $query = array(
+ 'username' => $user,
+ 'password' => $pass,
+ // 'domain' => $domain,
+ 'info' => true,
+ );
+
+ // authenticate the user
+ $response = $this->post('system.authenticate', $query);
+
+ if ($response) {
+ $this->userinfo = $response['result']['info'];
+ $this->token = $response['result']['session_token'];
+ }
+
+ return $this->wap;
+ }
+
+ /**
+ * API's POST request.
+ *
+ * @param string $action Action name
+ * @param array $post POST arguments
+ *
+ * @return kolab_client_api_result Response
+ */
+ protected function post($action, $post = array())
+ {
+ $url = $this->build_url($action);
+
+ if ($this->rc->config->get('wap_client_debug')) {
+ $this->rc->write_log('wap', "Calling API POST: $url\n" . @json_encode($post));
+ }
+
+ if ($this->token) {
+ $this->wap->setHeader('X-Session-Token', $this->token);
+ }
+
+ $this->wap->setMethod(HTTP_Request2::METHOD_POST);
+ $this->wap->setBody(@json_encode($post));
+
+ return $this->get_response($url);
+ }
+
+ /**
+ * Build Net_URL2 object for the request
+ *
+ * @param string $action Action GET parameter
+ * @param array $args GET parameters (hash array: name => value)
+ *
+ * @return Net_URL2 URL object
+ */
+ private function build_url($action, $args = array())
+ {
+ $url = rtrim($this->uri, '/');
+
+ if ($action) {
+ $url .= '/' . urlencode($action);
+ }
+
+ $url = new Net_URL2($url);
+
+ if (!empty($args)) {
+ $url->setQueryVariables($args);
+ }
+
+ return $url;
+ }
+
+ /**
+ * HTTP Response handler.
+ *
+ * @param Net_URL2 $url URL object
+ *
+ * @return array Response data
+ */
+ protected function get_response($url)
+ {
+ try {
+ $this->wap->setUrl($url);
+ $response = $this->wap->send();
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return;
+ }
+
+ try {
+ $body = $response->getBody();
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return;
+ }
+
+ if ($this->rc->config->get('wap_client_debug')) {
+ $this->rc->write_log('wap', "Response:\n$body");
+ }
+
+ $body = @json_decode($body, true);
+
+ if (!is_array($body)) {
+ rcube::raise_error("Failed to decode WAP response", true, false);
+ return;
+ }
+
+ return $body;
+ }
+}