config/config.ini.sample lib/Kolab

Thomas Brüderli bruederli at kolabsys.com
Fri Jul 4 20:36:40 CEST 2014


 config/config.ini.sample              |   15 ++-
 lib/Kolab/FreeBusy/Source.php         |    7 +
 lib/Kolab/FreeBusy/SourceFBDaemon.php |  139 ++++++++++++++++++++++++++++++++++
 lib/Kolab/FreeBusy/Utils.php          |   34 +++++---
 4 files changed, 181 insertions(+), 14 deletions(-)

New commits:
commit 5831b1ddf686d4536ef2059f1a90cd6ff4ff99d6
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Jul 4 08:39:12 2014 -0400

    Add new source to get free/busy data from a running kolab-freebusyd daemon (#3164)

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 at kolabsys.com>
+ *
+ * Copyright (C) 2014, Kolab Systems AG <contact at 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";




More information about the commits mailing list