summaryrefslogtreecommitdiff
path: root/plugins/kolab_config
diff options
context:
space:
mode:
authorAleksander Machniak (Kolab Systems) <machniak@kolabsys.com>2011-10-13 07:38:10 (GMT)
committerAleksander Machniak (Kolab Systems) <machniak@kolabsys.com>2011-10-13 07:38:10 (GMT)
commit4c2b83859ec63ae8c5a9708410beb57f3aa50847 (patch)
treece9ba79e1f768a34e4382a05664d944e009a186e /plugins/kolab_config
parent7bfb9079d5a18daa3f9a26f3c15de901b10adf18 (diff)
downloadroundcubemail-plugins-kolab-4c2b83859ec63ae8c5a9708410beb57f3aa50847.tar.gz
Plugin that implements KEP 16: Storage of dictionary entries.
Created simple classes for basic XML configuration objects and configuration folder.
Diffstat (limited to 'plugins/kolab_config')
-rw-r--r--plugins/kolab_config/kolab_config.php135
-rw-r--r--plugins/kolab_config/lib/configuration.php63
-rw-r--r--plugins/kolab_config/lib/kolab_configuration.php232
3 files changed, 430 insertions, 0 deletions
diff --git a/plugins/kolab_config/kolab_config.php b/plugins/kolab_config/kolab_config.php
new file mode 100644
index 0000000..03aa386
--- /dev/null
+++ b/plugins/kolab_config/kolab_config.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * Kolab configuration storage.
+ *
+ * Plugin to use Kolab server as a configuration storage. Provides an API to handle
+ * configuration according to http://wiki.kolab.org/KEP:9.
+ *
+ * Copyright (C) 2011, Kolab Systems AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * @author Machniak Aleksander <machniak@kolabsys.com>
+ *
+ */
+class kolab_config extends rcube_plugin
+{
+ public $task = 'utils';
+
+ private $config;
+ private $enabled;
+
+ /**
+ * Required startup method of a Roundcube plugin
+ */
+ public function init()
+ {
+ $rcmail = rcmail::get_instance();
+
+ // Register spellchecker dictionary handlers
+ if (strtolower($rcmail->config->get('spellcheck_dictionary')) != 'shared') {
+ $this->add_hook('spell_dictionary_save', array($this, 'dictionary_save'));
+ $this->add_hook('spell_dictionary_get', array($this, 'dictionary_get'));
+ }
+/*
+ // Register addressbook saved searches handlers
+ $this->add_hook('saved_search_create', array($this, 'saved_search_create'));
+ $this->add_hook('saved_search_delete', array($this, 'saved_search_delete'));
+ $this->add_hook('saved_search_list', array($this, 'saved_search_list'));
+ $this->add_hook('saved_search_get', array($this, 'saved_search_get'));
+*/
+ }
+
+ /**
+ * Initializes config object and dependencies
+ */
+ private function load()
+ {
+ if ($this->config)
+ return;
+
+ $this->require_plugin('kolab_folders');
+
+ // load dependencies
+ require_once 'Horde/Util.php';
+ require_once 'Horde/Kolab/Format.php';
+ require_once 'Horde/Kolab/Format/XML.php';
+ require_once $this->home . '/lib/configuration.php';
+ require_once $this->home . '/lib/kolab_configuration.php';
+
+ String::setDefaultCharset('UTF-8');
+
+ $this->config = new kolab_configuration();
+
+ // check if configuration folder exist
+ if (strlen($this->config->dir)) {
+ $this->enabled = true;
+ }
+ }
+
+ /**
+ * Saves spellcheck dictionary.
+ *
+ * @param array $args Hook arguments
+ *
+ * @return array Hook arguments
+ */
+ public function dictionary_save($args)
+ {
+ $this->load();
+
+ if (!$this->enabled) {
+ return $args;
+ }
+
+ $lang = $args['language'];
+ $dict = $this->dict;
+
+ $dict['type'] = 'dictionary';
+ $dict['language'] = $args['language'];
+ $dict['e'] = $args['dictionary'];
+
+ if (empty($dict['e'])) {
+ // Delete the object
+ $this->config->del($dict);
+ }
+ else {
+ // Update the object
+ $this->config->set($dict);
+ }
+
+ $args['abort'] = true;
+
+ return $args;
+ }
+
+ /**
+ * Returns spellcheck dictionary.
+ *
+ * @param array $args Hook arguments
+ *
+ * @return array Hook arguments
+ */
+ public function dictionary_get($args)
+ {
+ $this->load();
+
+ if (!$this->enabled) {
+ return $args;
+ }
+
+ $lang = $args['language'];
+ $this->dict = $this->config->get('dictionary.'.$lang);
+
+ if (!empty($this->dict)) {
+ $args['dictionary'] = $this->dict['e'];
+ }
+
+ $args['abort'] = true;
+
+ return $args;
+ }
+}
diff --git a/plugins/kolab_config/lib/configuration.php b/plugins/kolab_config/lib/configuration.php
new file mode 100644
index 0000000..e8c127f
--- /dev/null
+++ b/plugins/kolab_config/lib/configuration.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Kolab XML handler for configuration (KEP:9).
+ *
+ * Copyright (C) 2011 Kolab Systems AG
+ *
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ */
+class Horde_Kolab_Format_XML_configuration extends Horde_Kolab_Format_XML {
+ /**
+ * Specific data fields for the configuration object
+ *
+ * @var Kolab
+ */
+ var $_fields_specific;
+
+ var $_root_version = 2.1;
+
+ /**
+ * Constructor
+ */
+ function Horde_Kolab_Format_XML_configuration($params = array())
+ {
+ $this->_root_name = 'configuration';
+
+ // Specific configuration fields, in kolab format specification order
+ $this->_fields_specific = array(
+ 'application' => array (
+ 'type' => HORDE_KOLAB_XML_TYPE_STRING,
+ 'value' => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING,
+ ),
+ 'type' => array(
+ 'type' => HORDE_KOLAB_XML_TYPE_STRING,
+ 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY,
+ ),
+ );
+
+ // Dictionary fields
+ if (!empty($params['subtype']) && preg_match('/^dictionary.*/', $params['subtype'])) {
+ $this->_fields_specific = array_merge($this->_fields_specific, array(
+ 'language' => array (
+ 'type' => HORDE_KOLAB_XML_TYPE_STRING,
+ 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY,
+ ),
+ 'e' => array(
+ 'type' => HORDE_KOLAB_XML_TYPE_MULTIPLE,
+ 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY,
+ 'array' => array(
+ 'type' => HORDE_KOLAB_XML_TYPE_STRING,
+ 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY,
+ ),
+ ),
+ ));
+ }
+
+ parent::Horde_Kolab_Format_XML($params);
+
+ unset($this->_fields_basic['body']);
+ unset($this->_fields_basic['categories']);
+ unset($this->_fields_basic['sensitivity']);
+ }
+}
diff --git a/plugins/kolab_config/lib/kolab_configuration.php b/plugins/kolab_config/lib/kolab_configuration.php
new file mode 100644
index 0000000..9fc4b7b
--- /dev/null
+++ b/plugins/kolab_config/lib/kolab_configuration.php
@@ -0,0 +1,232 @@
+<?php
+
+/**
+ * Kolab configuration storage handler.
+ *
+ * Copyright (C) 2011, Kolab Systems AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * @author Machniak Aleksander <machniak@kolabsys.com>
+ */
+class kolab_configuration
+{
+ public $dir;
+ private $rc;
+ private $imap_data = array();
+
+ /**
+ * Class constructor. Establishes IMAP connection.
+ */
+ public function __construct()
+ {
+ $this->rc = rcmail::get_instance();
+
+ // Connect to IMAP
+ $this->rc->imap_connect();
+
+ // Get default configuration directory
+ // @TODO: handle many config directories, according to KEP:9
+ $dirs = $this->rc->imap->list_unsubscribed('', '*', 'configuration');
+ $this->dir = array_shift($dirs);
+ }
+
+ /**
+ * Returns configuration object
+ *
+ * @param string $type Configuration object type
+ *
+ * @return array Configuration object
+ */
+ public function get($type)
+ {
+ // If config folder exists
+ if (isset($this->dir[0])) {
+ // search by object type
+ // @TODO: configuration folders shouldn't be big, consider
+ // caching of all objects, then if we extend cache with object type
+ // column we could simply search in cache.
+ // @TODO: handle many config directories, according to KEP:9
+ $ctype = 'application/x-vnd.kolab.configuration' . ($type ? '.'.$type : '');
+ $search = 'HEADER X-Kolab-Type ' . $ctype;
+
+ $this->set_imap_props();
+ $this->rc->imap->search($this->dir, $search, 'US-ASCII', 'date');
+
+ $list = $this->rc->imap->list_headers($this->dir, 1, 'date', 'DESC');
+ $this->reset_imap_props();
+
+ foreach ($list as $idx => $obj) {
+ // @TODO: maybe we could skip parsing the message structure and
+ // get second part's body directly
+ $msg = new rcube_message($obj->uid);
+ $xml = null;
+
+ // get XML part
+ foreach ((array)$msg->attachments as $part) {
+ if ($part->mimetype == 'application/xml') {
+ if (!$part->body)
+ $part->body = $msg->get_part_content($part->mime_id);
+ $xml = $part->body;
+ break;
+ }
+ }
+
+ // that shouldn't happen
+ if ($xml === null) {
+ continue;
+ }
+
+ // Load XML object parser
+ $handler = Horde_Kolab_Format::factory('XML', 'configuration', array('subtype' => $type));
+ if (is_object($handler) && is_a($handler, 'PEAR_Error')) {
+ return null;
+ }
+
+ // XML-to-array
+ $object = $handler->load($xml);
+
+ if (is_object($object) && is_a($object, 'PEAR_Error')) {
+ return null;
+ }
+
+ $object['mailbox'] = $this->dir;
+ $object['msguid'] = $obj->uid;
+
+ return $object;
+ }
+ }
+ }
+
+ /**
+ * Saves configuration object
+ *
+ * @param array $object Configuration object
+ *
+ * @return bool True on success, False on failre
+ */
+ public function set($object)
+ {
+ $object['uid'] = md5(uniqid(mt_rand(), true));
+ $raw_msg = $this->build_message($object);
+
+ if ($raw_msg && isset($this->dir[0])) {
+ $result = $this->rc->imap->save_message($this->dir, $raw_msg, '', false);
+ }
+
+ // delete old message
+ if ($result && !empty($object['msguid'])) {
+ $this->rc->imap->delete_message($object['msguid'], $object['mailbox']);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Deletes configuration object
+ *
+ * @param array $object Configuration object
+ *
+ * @return bool True on success, False on failre
+ */
+ public function del($object)
+ {
+ if (!empty($object['msguid'])) {
+ return $this->rc->imap->delete_message($object['msguid'], $object['mailbox']);
+ }
+
+ return true;
+ }
+
+ /**
+ * Sets some rcube_imap settings before searching for config objects
+ */
+ private function set_imap_props()
+ {
+ // Save old settings
+ $this->imap_data = array(
+ 'skip_deleted' => $skip_deleted,
+ 'page_size' => $page_size,
+ 'list_page' => $list_page,
+ 'threading' => $threading,
+ );
+
+ // Configure for configuration folder
+ $this->rc->imap->skip_deleted = true;
+ $this->rc->imap->threading = false;
+ $this->rc->imap->page_size = 100;
+ $this->rc->imap->list_page = 1;
+ }
+
+ /**
+ * Resets rcube_imap settings after searching for config objects
+ */
+ private function reset_imap_props()
+ {
+ // Reset to old settings
+ foreach ($this->imap_data as $key => $val) {
+ $this->rc->imap->$key = $val;
+ }
+ // reset saved search
+ $this->rc->imap->search_set = null;
+ }
+
+ /**
+ * Creates source of the configuration object message
+ */
+ private function build_message($object)
+ {
+ $MAIL_MIME = new Mail_mime("\r\n");
+
+ $name = 'configuration';
+
+ if (!empty($object['type'])) {
+ $name .= '.' . $object['type'];
+ if ($object['type'] == 'dictionary' && !empty($object['language'])) {
+ $name .= '.' . $object['language'];
+ }
+ }
+
+ $handler = Horde_Kolab_Format::factory('XML', 'configuration',
+ array('subtype' => $object['type']));
+
+ if (is_object($handler) && is_a($handler, 'PEAR_Error')) {
+ return false;
+ }
+
+ $xml = $handler->save($object);
+
+ if (is_object($xml) && is_a($xml, 'PEAR_Error')) {
+ return false;
+ }
+
+ if ($ident = $this->rc->user->get_identity()) {
+ $headers['From'] = $ident['email'];
+ $headers['To'] = $ident['email'];
+ }
+ $headers['Date'] = date('r');
+ $headers['X-Kolab-Type'] = 'application/x-vnd.kolab.'.$name;
+ $headers['Subject'] = $object['uid'];
+ $headers['Message-ID'] = rcmail_gen_message_id();
+ $headers['User-Agent'] = $this->rc->config->get('useragent');
+
+ $MAIL_MIME->headers($headers);
+ $MAIL_MIME->setTXTBody('This is a Kolab Groupware object. '
+ .'To view this object you will need an email client that understands the Kolab Groupware format. '
+ .'For a list of such email clients please visit http://www.kolab.org/kolab2-clients.html');
+
+ $MAIL_MIME->addAttachment($xml,
+ 'application/xml',
+ $name . '.xml',
+ false, '8bit', 'attachment', RCMAIL_CHARSET, '', '',
+ $this->rc->config->get('mime_param_folding') ? 'quoted-printable' : null,
+ $this->rc->config->get('mime_param_folding') == 2 ? 'quoted-printable' : null,
+ '', RCMAIL_CHARSET
+ );
+
+ return $MAIL_MIME->getMessage();
+ }
+
+}