summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Bruederli <bruederli@kolabsys.com>2014-07-04 12:39:12 (GMT)
committerThomas Bruederli <bruederli@kolabsys.com>2014-07-04 12:39:12 (GMT)
commit5831b1ddf686d4536ef2059f1a90cd6ff4ff99d6 (patch)
treeabd17a18bfd5f97b0766d8b0eeec64b6420a3bca
parent92f8c8a6bb883d7778af2a9670ca1fc5853991eb (diff)
downloadkolab-freebusy-5831b1ddf686d4536ef2059f1a90cd6ff4ff99d6.tar.gz
Add new source to get free/busy data from a running kolab-freebusyd daemon (#3164)
-rw-r--r--config/config.ini.sample15
-rw-r--r--lib/Kolab/FreeBusy/Source.php7
-rw-r--r--lib/Kolab/FreeBusy/SourceFBDaemon.php139
-rw-r--r--lib/Kolab/FreeBusy/Utils.php34
4 files changed, 181 insertions, 14 deletions
diff --git a/config/config.ini.sample b/config/config.ini.sample
index 4439cb7..79b31eb 100644
--- a/config/config.ini.sample
+++ b/config/config.ini.sample
@@ -68,7 +68,8 @@ bind_pw = "<service-bind-pw>"
base_dn = "ou=Resources,dc=yourdomain,dc=com"
filter = "(&(objectClass=kolabsharedfolder)(mail=%s))"
attributes = mail, kolabtargetfolder
-fbsource = "imap://cyrus-admin:<admin-pass>@localhost/%kolabtargetfolder?acl=lrs"
+fbsource = "fbdaemon://localhost:<port>?folder=%kolabtargetfolder"
+timeout = 10 ; abort after 10 seconds
cacheto = /var/cache/kolab-freebusy/%mail.ifb
expires = 10m
loglevel = 100 ; Debug
@@ -81,7 +82,15 @@ fbsource = https://externalhost/free-busy/%s.ics
format = Exchange2010
;; further examples of fbsource URIs
-; - fetch data from another server by HTTP(s)
+;; - fetch data from another server by HTTP(s)
; fbsource = "https://fb-service-user:imap-password@kolab-server/freebusy/%mail.ifb"
-; - read directoy from a users calendars (all) using IMAP proxy authentication
+
+;; - read data from a users calendars (all) using IMAP proxy authentication
; fbsource = "imap://%mail:<admin-pass>@localhost/?proxy_auth=cyrus-admin"
+
+;; - read data from a shared IMAP folder with cyrus-admin privileges
+; fbsource = "imap://cyrus-admin:<admin-pass>@localhost/%kolabtargetfolder?acl=lrs"
+
+;; - trigger kolab-freebusyd daemon (folder= for shared folders, user= for user mailboxes)
+; fbsource = "fbdaemon://localhost:<port>?folder=%kolabtargetfolder&user=%mail"
+
diff --git a/lib/Kolab/FreeBusy/Source.php b/lib/Kolab/FreeBusy/Source.php
index a0a915d..def290f 100644
--- a/lib/Kolab/FreeBusy/Source.php
+++ b/lib/Kolab/FreeBusy/Source.php
@@ -47,6 +47,8 @@ abstract class Source
case 'imaps': return new SourceIMAP($config + $conf);
case 'http':
case 'https': return new SourceURL($config + $conf);
+ case 'fbd':
+ case 'fbdaemon': return new SourceFBDaemon($config + $conf);
}
Logger::get('source')->addError("Invalid source configuration: " . $url);
@@ -78,7 +80,10 @@ abstract class Source
if (is_string($val) && strpos($val, '%') !== false) {
$val = preg_replace_callback(
'/%\{?([a-z0-9]+)\}?/',
- function($m) use ($user) { return $user[$m[1]]; },
+ function($m) use ($k, $user) {
+ $enc = $k == 'url' || $k == 'query' || $k == 'fbsource';
+ return $enc ? urlencode($user[$m[1]]) : $user[$m[1]];
+ },
$val);
}
diff --git a/lib/Kolab/FreeBusy/SourceFBDaemon.php b/lib/Kolab/FreeBusy/SourceFBDaemon.php
new file mode 100644
index 0000000..13a0fb5
--- /dev/null
+++ b/lib/Kolab/FreeBusy/SourceFBDaemon.php
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * This file is part of the Kolab Server Free/Busy Service
+ *
+ * @author Thomas Bruederli <bruederli@kolabsys.com>
+ *
+ * Copyright (C) 2014, 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/>.
+ */
+
+namespace Kolab\FreeBusy;
+
+/**
+ * Implementation of a Free/Busy data source reading from kolab-freebusyd service
+ */
+class SourceFBDaemon extends Source
+{
+ /**
+ * @see Source::getFreeBusyData()
+ */
+ public function getFreeBusyData($user, $extended)
+ {
+ $log = Logger::get('fbdaemon', intval($this->config['loglevel']));
+
+ $config = $this->getUserConfig($user);
+ parse_str(strval($config['query']), $param);
+ $config += $param;
+
+ // log this...
+ $log->addInfo("Fetching data for ", $config);
+
+ // caching is enabled
+ if (!empty($config['cacheto'])) {
+ // check for cached data
+ if ($cached = $this->getCached($config)) {
+ $log->addInfo("Deliver cached data from " . $config['cacheto']);
+ return $cached;
+ }
+ // touch cache file to avoid multiple requests generating the same data
+ if (file_exists($config['cacheto'])) {
+ touch($config['cacheto']);
+ }
+ else {
+ file_put_contents($config['cacheto'], Utils::dummyVFreebusy($user['mail']));
+ $tempfile = $config['cacheto'];
+ }
+ }
+
+ // compose command for freebusyd
+ if (!empty($config['folder'])) {
+ $cmd = 'FOLDER';
+ $target = $config['folder'];
+ }
+ else if (!empty($config['user'])) {
+ $cmd = 'USER';
+ $target = $config['user'];
+ }
+ else {
+ $log->addWarning("No valid target user/name specified", $config);
+ }
+
+ // open connection to fbdaemon and execute IFB command
+ if (!empty($cmd) && ($fp = fsockopen($config['host'], $config['port'], $errno, $errstr, 5))) {
+ $timeout = $config['timeout'] ? intval($config['timeout']) : max(10, ini_get('max_execution_time')) - 5;
+ stream_set_timeout($fp , $timeout);
+
+ $start = Utils::periodStart();
+ $end = Utils::periodEnd();
+
+ $send = sprintf('IFB %s "%s" slot:%d-%d'."\n", $cmd, $target, $start, $end);
+ $log->debug('C: ' . $send, array('start' => gmdate('Y-m-d\TH:i:s\Z', $start), 'end' => gmdate('Y-m-d\TH:i:s\Z', $end), 'timeout' => $timeout));
+ $fbdata = false;
+
+ fwrite($fp, $send);
+ while (!feof($fp)) {
+ $line = fgets($fp, 128);
+ $log->debug('S: ' . $line);
+ $len = 0;
+
+ // detect response header
+ if (preg_match('/^\*\s+\(\{(\d+)\}/', $line, $m)) {
+ $len = intval($m[1]);
+ if ($len > 0) {
+ $fbdata = fread($fp, $len);
+ $log->debug("S: " . $fbdata);
+ }
+ }
+
+ // exit loop if result complete
+ if (preg_match('/^(OK|BAD)\s+/', $line, $m)) {
+ if ($m[1] == 'BAD') {
+ $log->addWarning("BAD response from kolab-freebusyd", array('request' => $send, 'response' => $line));
+ }
+ break;
+ }
+ }
+
+ fclose($fp);
+ }
+
+ // log daemon connection errors
+ if ($errno || $errstr) {
+ $log->addError("Cannot connect to kolab-freebusyd", array(
+ 'errno' => $errno,
+ 'error' => $errstr,
+ 'host' => $config['host'],
+ 'port' => $config['port'],
+ ));
+ }
+
+ if (!empty($fbdata)) {
+ // post-process fbdata (replace ORGANIZER: property)
+ if (!empty($user['mail'])) {
+ $fbdata = preg_replace('/(ORGANIZER:mailto:)(.+)/i', '\1' . $user['mail'], $fbdata);
+ }
+ return $fbdata;
+ }
+ // remove (temporary) cache file again
+ else if ($tempfile) {
+ unlink($tempfile);
+ }
+
+ // not found
+ return false;
+ }
+}
diff --git a/lib/Kolab/FreeBusy/Utils.php b/lib/Kolab/FreeBusy/Utils.php
index 0d76ab6..942341e 100644
--- a/lib/Kolab/FreeBusy/Utils.php
+++ b/lib/Kolab/FreeBusy/Utils.php
@@ -170,6 +170,28 @@ class Utils
}
/**
+ * Getter for the free/busy period start time
+ *
+ * @return int Unix timestamp
+ */
+ public static function periodStart()
+ {
+ // Should probably be a setting. For now, do 8 weeks in the past
+ return time() - (60 * 60 * 24 * 7 * 8);
+ }
+
+ /**
+ * Getter for the free/busy period end time
+ *
+ * @return int Unix timestamp
+ */
+ public static function periodEnd()
+ {
+ // Should probably be a setting. For now, do 16 weeks into the future
+ return time() + (60 * 60 * 24 * 7 * 16);
+ }
+
+ /**
* Returns an apparent empty Free/Busy list for the given user
*/
public static function dummyVFreebusy($user)
@@ -177,14 +199,6 @@ class Utils
$now = time();
$dtformat = 'Ymd\THis\Z';
- // NOTE: The following settings should probably correspond with
- // whatever period of time kolab-freebusyd thinks it should use.
-
- // Should probably be a setting. For now, do 8 weeks in the past
- $start = $now - (60 * 60 * 24 * 7 * 8);
- // Should probably be a setting. For now, do 16 weeks into the future
- $end = $now + (60 * 60 * 24 * 7 * 16);
-
$dummy = "BEGIN:VCALENDAR\n";
$dummy .= "VERSION:2.0\n";
$dummy .= "PRODID:" . self::PRODID . "\n";
@@ -192,8 +206,8 @@ class Utils
$dummy .= "BEGIN:VFREEBUSY\n";
$dummy .= "ORGANIZER:MAILTO:" . $user . "\n";
$dummy .= "DTSTAMP:" . gmdate($dtformat) . "\n";
- $dummy .= "DTSTART:" . gmdate($dtformat, $start) . "\n";
- $dummy .= "DTEND:" . gmdate($dtformat, $end) . "\n";
+ $dummy .= "DTSTART:" . gmdate($dtformat, self::periodStart()) . "\n";
+ $dummy .= "DTEND:" . gmdate($dtformat, self::periodEnd()) . "\n";
$dummy .= "COMMENT:This is a dummy vfreebusy that indicates an empty calendar\n";
$dummy .= "FREEBUSY:19700101T000000Z/19700101T000000Z\n";
$dummy .= "END:VFREEBUSY\n";