1<?php
2
3/**
4 * WAP Client plugin.
5 *
6 * @version @package_version@
7 * @author Aleksander Machniak <machniak@kolabsys.com>
8 *
9 * Copyright (C) 2016, Kolab Systems AG <contact@kolabsys.com>
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Affero General Public License as
13 * published by the Free Software Foundation, either version 3 of the
14 * License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Affero General Public License for more details.
20 *
21 * You should have received a copy of the GNU Affero General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25class wap_client extends rcube_plugin
26{
27    public $task   = 'settings';
28    public $noajax = true;
29
30    protected $rc;
31    protected $wap;
32    protected $userinfo;
33    protected $token;
34
35
36    /**
37     * Initializes the plugin
38     */
39    function init()
40    {
41        $this->rc = rcmail::get_instance();
42
43        $this->add_hook('preferences_list', array($this, 'prefs_table'));
44        $this->add_hook('preferences_save', array($this, 'save_prefs'));
45    }
46
47    /**
48     * Hook to inject plugin-specific user settings
49     */
50    public function prefs_table($args)
51    {
52        global $CURR_SECTION;
53
54        if ($args['section'] != 'server') {
55            return;
56        }
57
58        $this->load_config();
59
60        $accounts = (array) $this->rc->config->get('wap_client_accounts');
61
62        if (empty($accounts)) {
63            return;
64        }
65
66        $this->add_texts('localization');
67
68        if ($CURR_SECTION) {
69            $account_type = $this->get_account_type();
70            $_SESSION['wap_client_account_type'] = $account_type;
71        }
72
73        $input   = new html_radiobutton(array('name' => '_account_type', 'style' => 'display:block; float:left'));
74        $content = '';
75
76        foreach ($accounts as $idx => $def) {
77            $id   = 'account_type_' . strtolower(asciiwords($idx, true));
78            $name = $idx;
79            $name = $this->rc->text_exists('wap_client.account.' . $name) ? $this->gettext('account.' . $name) : $name;
80            $desc = $this->rc->text_exists('wap_client.accountdesc.' . $name) ? $this->gettext('accountdesc.' . $name) : $def['description'];
81
82            $name = html::span(array('style' => 'font-weight: bold'), rcube::Q($name));
83            if ($desc) {
84                $name .= html::br() . html::span(null, rcube::Q($desc));
85            }
86
87            $label_style = 'display:block; margin: 5px 0; padding-left: 30px';
88            $content .= $input->show($account_type, array('value' => $idx, 'id' => $id))
89                . html::label(array('for' => $id, 'style' => $label_style), $name);
90        }
91
92        $conf = array(
93            'account' => array(
94                'name'    => rcube::Q($this->gettext('accountoptions')),
95                'options' => array(
96                    'account_type' => array(
97                        'title'   => $this->gettext('accounttype'),
98                        'content' => $content,
99                    )
100                )
101            )
102        );
103
104        $args['blocks'] = array_merge($conf, $args['blocks']);
105
106        return $args;
107    }
108
109    /**
110     * Hook to save plugin-specific user settings
111     */
112    public function save_prefs($args)
113    {
114        if ($args['section'] != 'server') {
115            return;
116        }
117
118        $account_type = rcube_utils::get_input_value('_account_type', rcube_utils::INPUT_POST);
119
120        if (!$account_type || $account_type == $_SESSION['wap_client_account_type']) {
121            return;
122        }
123
124        $this->add_texts('localization');
125
126        $this->set_account_type($account_type);
127    }
128
129    /**
130     * Get current account type (from WAP)
131     */
132    protected function get_account_type()
133    {
134        $this->init_wap();
135
136        if (empty($this->userinfo)) {
137            $this->rc->output->show_message($this->gettext('failedtypedetection'), 'warning');
138            return;
139        }
140
141        $roles    = (array) $this->userinfo['nsroledn'];
142        $accounts = (array) $this->rc->config->get('wap_client_accounts');
143        $root_dn  = $this->rc->config->get('wap_client_root_dn');
144        $base_dn  = $this->rc->config->get('wap_client_base_dn');
145
146        foreach ($accounts as $name => $account) {
147            foreach ((array) $account['nsroledn'] as $role) {
148                $value = str_replace('$base_dn', $base_dn, $value);
149                $value = str_replace('$root_dn', $root_dn, $value);
150
151                if (!in_array($value, $roles)) {
152                    continue 2;
153                }
154            }
155
156            return $name;
157        }
158    }
159
160    /**
161     * Set account type (in WAP)
162     */
163    protected function set_account_type($type)
164    {
165        if (!$this->init_wap()) {
166            return false;
167        }
168
169        $query    = $this->userinfo;
170        $accounts = (array) $this->rc->config->get('wap_client_accounts');
171        $root_dn  = $this->rc->config->get('wap_client_root_dn');
172        $base_dn  = $this->rc->config->get('wap_client_base_dn');
173        $account  = $accounts[$type];
174
175        if (empty($account)) {
176            $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning');
177            return;
178        }
179
180        unset($account['description']);
181
182        foreach ($account as $attr => $value) {
183            switch ($attr) {
184            case 'nsroledn':
185                $value = array();
186                foreach ((array) $account['nsroledn'] as $role) {
187                    $role = str_replace('$base_dn', $base_dn, $role);
188                    $role = str_replace('$root_dn', $root_dn, $role);
189                    $value[] = $role;
190                }
191
192            default:
193                $query[$attr] = $value;
194            }
195        }
196
197        $response = $this->post('user.edit', $query);
198
199        if (!$response || $response['status'] != 'OK') {
200            $this->rc->output->show_message($this->gettext('failedtypeupdate'), 'warning');
201            return;
202        }
203
204        $this->userinfo = $query;
205    }
206
207    /**
208     * Initialize WAP connection and user session
209     */
210    protected function init_wap()
211    {
212        if ($this->wap) {
213            return $this->wap;
214        }
215
216        $this->load_config();
217        $this->require_plugin('libkolab');
218
219        $uri  = $this->rc->config->get('wap_client_uri');
220        $user = $this->rc->get_user_name();
221        $pass = $this->rc->decrypt($_SESSION['password']);
222
223        if (!$uri) {
224            rcube::raise_error("wap_client_uri is not set", true, false);
225            return;
226        }
227
228        // get HTTP_Request2 object
229        $this->uri = rcube_utils::resolve_url($uri);
230        $this->wap = libkolab::http_request($this->uri);
231
232        $query = array(
233            'username' => $user,
234            'password' => $pass,
235        //    'domain'   => $domain,
236            'info'     => true,
237        );
238
239        // authenticate the user
240        $response = $this->post('system.authenticate', $query);
241
242        if ($response) {
243            $this->userinfo = $response['result']['info'];
244            $this->token    = $response['result']['session_token'];
245        }
246
247        return $this->wap;
248    }
249
250    /**
251     * API's POST request.
252     *
253     * @param string $action Action name
254     * @param array  $post   POST arguments
255     *
256     * @return kolab_client_api_result  Response
257     */
258    protected function post($action, $post = array())
259    {
260        $url = $this->build_url($action);
261
262        if ($this->rc->config->get('wap_client_debug')) {
263            $this->rc->write_log('wap', "Calling API POST: $url\n" . @json_encode($post));
264        }
265
266        if ($this->token) {
267            $this->wap->setHeader('X-Session-Token', $this->token);
268        }
269
270        $this->wap->setMethod(HTTP_Request2::METHOD_POST);
271        $this->wap->setBody(@json_encode($post));
272
273        return $this->get_response($url);
274    }
275
276    /**
277     * Build Net_URL2 object for the request
278     *
279     * @param string $action Action GET parameter
280     * @param array  $args   GET parameters (hash array: name => value)
281     *
282     * @return Net_URL2 URL object
283     */
284    private function build_url($action, $args = array())
285    {
286        $url = rtrim($this->uri, '/');
287
288        if ($action) {
289            $url .= '/' . urlencode($action);
290        }
291
292        $url = new Net_URL2($url);
293
294        if (!empty($args)) {
295            $url->setQueryVariables($args);
296        }
297
298        return $url;
299    }
300
301    /**
302     * HTTP Response handler.
303     *
304     * @param Net_URL2 $url URL object
305     *
306     * @return array Response data
307     */
308    protected function get_response($url)
309    {
310        try {
311            $this->wap->setUrl($url);
312            $response = $this->wap->send();
313        }
314        catch (Exception $e) {
315            rcube::raise_error($e, true, false);
316            return;
317        }
318
319        try {
320            $body = $response->getBody();
321        }
322        catch (Exception $e) {
323            rcube::raise_error($e, true, false);
324            return;
325        }
326
327        if ($this->rc->config->get('wap_client_debug')) {
328            $this->rc->write_log('wap', "Response:\n$body");
329        }
330
331        $body = @json_decode($body, true);
332
333        if (!is_array($body)) {
334            rcube::raise_error("Failed to decode WAP response", true, false);
335            return;
336        }
337
338        return $body;
339    }
340}
341