155 lines
5.4 KiB
PHP
155 lines
5.4 KiB
PHP
![]() |
<?php
|
||
|
|
||
|
/**
|
||
|
* This class contains several utilities related to the ICalendar (rfc2445) format
|
||
|
*
|
||
|
* @package Sabre
|
||
|
* @subpackage CalDAV
|
||
|
* @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved.
|
||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||
|
*/
|
||
|
class Sabre_CalDAV_ICalendarUtil {
|
||
|
|
||
|
/**
|
||
|
* Validates an ICalendar object
|
||
|
*
|
||
|
* This method makes sure this ICalendar object is properly formatted.
|
||
|
* If we can't parse it, we'll throw exceptions.
|
||
|
*
|
||
|
* @param string $icalData
|
||
|
* @param array $allowedComponents
|
||
|
* @return bool
|
||
|
*/
|
||
|
static function validateICalendarObject($icalData, array $allowedComponents) {
|
||
|
|
||
|
$xcal = simplexml_load_string(self::toXCal($icalData));
|
||
|
if (!$xcal) throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Invalid calendarobject format');
|
||
|
|
||
|
$xcal->registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal');
|
||
|
|
||
|
// Check if there's only 1 component
|
||
|
$components = array('vevent','vtodo','vjournal','vfreebusy');
|
||
|
$componentsFound = array();
|
||
|
|
||
|
foreach($components as $component) {
|
||
|
$test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component);
|
||
|
if (is_array($test)) $componentsFound = array_merge($componentsFound, $test);
|
||
|
}
|
||
|
if (count($componentsFound)>1) {
|
||
|
throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Only 1 of VEVENT, VTODO, VJOURNAL or VFREEBUSY may be specified per calendar object');
|
||
|
}
|
||
|
if (count($componentsFound)<1) {
|
||
|
throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.');
|
||
|
}
|
||
|
$component = $componentsFound[0];
|
||
|
|
||
|
// Check if the component is allowed
|
||
|
$name = $component->getName();
|
||
|
if (!in_array(strtoupper($name),$allowedComponents)) {
|
||
|
throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.');
|
||
|
}
|
||
|
|
||
|
if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) {
|
||
|
throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects');
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts ICalendar data to XML.
|
||
|
*
|
||
|
* Properties are converted to lowercase xml elements. Parameters are;
|
||
|
* converted to attributes. BEGIN:VEVENT is converted to <vevent> and
|
||
|
* END:VEVENT </vevent> as well as other components.
|
||
|
*
|
||
|
* It's a very loose parser. If any line does not conform to the spec, it
|
||
|
* will simply be ignored. It will try to detect if \r\n or \n line endings
|
||
|
* are used.
|
||
|
*
|
||
|
* @todo Currently quoted attributes are not parsed correctly.
|
||
|
* @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03
|
||
|
* @param string $icalData
|
||
|
* @return string.
|
||
|
*/
|
||
|
static function toXCAL($icalData) {
|
||
|
|
||
|
// Detecting line endings
|
||
|
$lb="\r\n";
|
||
|
if (strpos($icalData,"\r\n")!==false) $lb = "\r\n";
|
||
|
elseif (strpos($icalData,"\n")!==false) $lb = "\n";
|
||
|
|
||
|
// Splitting up items per line
|
||
|
$lines = explode($lb,$icalData);
|
||
|
|
||
|
// Properties can be folded over 2 lines. In this case the second
|
||
|
// line will be preceeded by a space or tab.
|
||
|
$lines2 = array();
|
||
|
foreach($lines as $line) {
|
||
|
|
||
|
if (!$line) continue;
|
||
|
if ($line[0]===" " || $line[0]==="\t") {
|
||
|
$lines2[count($lines2)-1].=substr($line,1);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$lines2[]=$line;
|
||
|
|
||
|
}
|
||
|
|
||
|
$xml = '<?xml version="1.0"?>' . "\n";
|
||
|
$xml.= "<iCalendar xmlns=\"urn:ietf:params:xml:ns:xcal\">\n";
|
||
|
|
||
|
$spaces = 2;
|
||
|
foreach($lines2 as $line) {
|
||
|
|
||
|
$matches = array();
|
||
|
// This matches PROPERTYNAME;ATTRIBUTES:VALUE
|
||
|
if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches))
|
||
|
continue;
|
||
|
|
||
|
$propertyName = strtolower($matches[1]);
|
||
|
$attributes = $matches[2];
|
||
|
$value = $matches[3];
|
||
|
|
||
|
// If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it.
|
||
|
if ($propertyName === 'begin') {
|
||
|
$xml.=str_repeat(" ",$spaces);
|
||
|
$xml.='<' . strtolower($value) . ">\n";
|
||
|
$spaces+=2;
|
||
|
continue;
|
||
|
} elseif ($propertyName === 'end') {
|
||
|
$spaces-=2;
|
||
|
$xml.=str_repeat(" ",$spaces);
|
||
|
$xml.='</' . strtolower($value) . ">\n";
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$xml.=str_repeat(" ",$spaces);
|
||
|
$xml.='<' . $propertyName;
|
||
|
if ($attributes) {
|
||
|
// There can be multiple attributes
|
||
|
$attributes = explode(';',$attributes);
|
||
|
foreach($attributes as $att) {
|
||
|
|
||
|
list($attName,$attValue) = explode('=',$att,2);
|
||
|
$attName = strtolower($attName);
|
||
|
if ($attName === 'language') $attName='xml:lang';
|
||
|
$xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"';
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$xml.='>'. htmlspecialchars(trim($value)) . '</' . $propertyName . ">\n";
|
||
|
|
||
|
}
|
||
|
$xml.="</iCalendar>";
|
||
|
return $xml;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|