Changeset View
Changeset View
Standalone View
Standalone View
lib/Kolab/FreeBusy/SourceEWS.php
- This file was added.
<?php | |||||
/** | |||||
* This file is part of the Kolab Server Free/Busy Service | |||||
* | |||||
* @author Aleksander Machniak <machniak@kolabsys.com> | |||||
* | |||||
* Copyright (C) 2013-2021, Apheleia IT AG <contact@apheleia.ch> | |||||
* | |||||
* 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; | |||||
use garethp\ews\API; | |||||
use garethp\ews\API\Exception; | |||||
/** | |||||
* Implementation of a Free/Busy data source reading from an EWS server | |||||
*/ | |||||
class SourceEWS extends Source | |||||
{ | |||||
/** | |||||
* @see Source::getFreeBusyData() | |||||
*/ | |||||
public function getFreeBusyData($user, $extended) | |||||
{ | |||||
$config = $this->getUserConfig($user); | |||||
$user = is_array($user) ? $user['s'] : $user; | |||||
// Get start and end date for the request | |||||
list($start, $end) = $this->getPeriod(); | |||||
// Fetch the availability info from the EWS server | |||||
$data = $this->fetchData($user, $start, $end, $config); | |||||
if ($data === false) { | |||||
return false; | |||||
} | |||||
// Process the data (convert to ics) | |||||
foreach ($data as $idx => $event) { | |||||
// Map the busy-type property | |||||
switch ($event->getBusyType()) { | |||||
case 'Free': | |||||
$fbtype = 'FREE'; | |||||
break; | |||||
case 'Tentative': | |||||
$fbtype = 'BUSY-TENTATIVE'; | |||||
break; | |||||
case 'Busy': | |||||
// case 'OOF': | |||||
// case 'NoData': | |||||
default: | |||||
$fbtype = 'BUSY'; | |||||
break; | |||||
} | |||||
$startTime = new \DateTime($event->getStartTime(), new \DateTimeZone('UTC')); | |||||
$endTime = new \DateTime($event->getEndTime(), new \DateTimeZone('UTC')); | |||||
$data[$idx] = sprintf( | |||||
"FREEBUSY;FBTYPE=%s:%s/%s", | |||||
$fbtype, | |||||
$startTime->format('Ymd\THis\Z'), | |||||
$endTime->format('Ymd\THis\Z'), | |||||
); | |||||
} | |||||
return sprintf( | |||||
"BEGIN:VCALENDAR\n" | |||||
. "PRODID:%s\n" | |||||
. "VERSION:2.0\n" | |||||
. "METHOD:PUBLISH\n" | |||||
. "BEGIN:VFREEBUSY\n" | |||||
. "ORGANIZER:mailto:%s\n" | |||||
. "UID:%s\n" | |||||
. "DTSTAMP:%s\n" | |||||
. "DTSTART:%s\n" | |||||
. "DTEND:%s\n" | |||||
. "%s\n" | |||||
. "END:VFREEBUSY\n" | |||||
. "END:VCALENDAR\n", | |||||
Utils::PRODID, | |||||
$user, | |||||
\date('YmdHi') . '-' . \substr(\md5($user), 0, 16), | |||||
$start->format('Ymd\THis\Z'), | |||||
$start->format('Ymd\THis\Z'), | |||||
$end->format('Ymd\THis\Z'), | |||||
implode("\n", $data) | |||||
); | |||||
} | |||||
/** | |||||
* Fetches the user availability data from the EWS server | |||||
*/ | |||||
private function fetchData($user, $startdate, $enddate, $config) | |||||
{ | |||||
$logger = Logger::get('ews'); | |||||
$logger->addDebug(sprintf("Fetching data from %s for %s...", $config['host'], $user)); | |||||
try { | |||||
// Create and build the client | |||||
$api = API::withUsernameAndPassword( | |||||
$config['host'], | |||||
urldecode($config['user']), | |||||
urldecode($config['pass']), | |||||
array( | |||||
// return date-time in UTC | |||||
'timezone' => 'UTC', | |||||
// act as the requested user | |||||
'impersonation' => $user | |||||
) | |||||
); | |||||
$calendar = $api->getCalendar(); | |||||
// This is using the EWS's GetUserAvailability API | |||||
$options = array(); | |||||
/* | |||||
$options = array( | |||||
'FreeBusyViewOptions' => array( | |||||
'MergedFreeBusyIntervalInMinutes' => 15 // default: 30 | |||||
) | |||||
); | |||||
*/ | |||||
$availability = $calendar->getAvailabilityFor($startdate, $enddate, array($user), $options); | |||||
// Check response status | |||||
$responseMessage = $availability->getFreeBusyResponseArray()->FreeBusyResponse->getResponseMessage(); | |||||
if ($responseMessage->getResponseClass() === 'Error') { | |||||
throw new Exception\ExchangeException($responseMessage); | |||||
} | |||||
$items = $availability->getFreeBusyResponseArray()->FreeBusyResponse->getFreeBusyView()->getCalendarEventArray(); | |||||
if ($items && $items->CalendarEvent) { | |||||
$logger->addDebug(sprintf("Found %d events", count($items->CalendarEvent))); | |||||
return $items->CalendarEvent; | |||||
} | |||||
$logger->addDebug("Found 0 events"); | |||||
return array(); | |||||
} catch (Exception\UnauthorizedException $e) { | |||||
$logger->addError("EWS access unauthorized"); | |||||
} catch (Exception\ServiceUnavailableException $e) { | |||||
$logger->addError("EWS service unavailable"); | |||||
} catch (Exception\NoResponseReturnedException $e) { | |||||
$logger->addError("EWS no response"); | |||||
} catch (Exception\AutodiscoverFailed $e) { | |||||
$logger->addError("EWS autodiscovery failed"); | |||||
} catch (\Exception $e) { | |||||
$logger->addError($e->getMessage() ?: "EWS unknown error"); | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Get request period start and end | |||||
*/ | |||||
private function getPeriod() | |||||
{ | |||||
// use date from HTTP query | |||||
if (!empty($_GET['dtstart']) && | |||||
($dtstart = \filter_input(INPUT_GET, 'dtstart', FILTER_SANITIZE_STRING)) | |||||
) { | |||||
try { | |||||
$start = new \DateTime($dtstart, new \DateTimezone('UTC')); | |||||
} catch (Exception $e) { | |||||
// ignore | |||||
} | |||||
} | |||||
if (empty($start)) { | |||||
$start = new \DateTime('now', new \DateTimeZone('UTC')); | |||||
} | |||||
// Set the period to 7 days in the past and 42-7 days in the future | |||||
// Note: 42 days is max. supported period on EWS | |||||
$start->sub(new \DateInterval('P7D')); | |||||
$end = clone $start; | |||||
$end->add(new \DateInterval('P42D')); | |||||
return array($start, $end); | |||||
} | |||||
} |