gunnar: server/php-kolab/Kolab_Freebusy/Freebusy domxml-php4-to-php5.php, NONE, 1.1 freebusy.class.php, NONE, 1.1 freebusycache.class.php, NONE, 1.1 freebusycollector.class.php, NONE, 1.1 freebusyimapcache.class.php, NONE, 1.1 freebusyldap.class.php, NONE, 1.1 freebusyldap_dummy.class.php, NONE, 1.1 misc.php, NONE, 1.1 recurrence.class.php, NONE, 1.1
cvs at kolab.org
cvs at kolab.org
Tue Aug 7 17:45:58 CEST 2007
Author: gunnar
Update of /kolabrepository/server/php-kolab/Kolab_Freebusy/Freebusy
In directory doto:/tmp/cvs-serv31660/Kolab_Freebusy/Freebusy
Added Files:
domxml-php4-to-php5.php freebusy.class.php
freebusycache.class.php freebusycollector.class.php
freebusyimapcache.class.php freebusyldap.class.php
freebusyldap_dummy.class.php misc.php recurrence.class.php
Log Message:
A first PEAR package derived from kolab-resource-handlers/freebusy. Not yet completed.
--- NEW FILE: domxml-php4-to-php5.php ---
<?php
/*
Requires PHP5, uses built-in DOM extension.
To be used in PHP4 scripts using DOMXML extension.
Allows PHP4/DOMXML scripts to run on PHP5/DOM.
(Optional: requires PHP5/XSL extension for domxml_xslt functions, and PHP>=5.1/libxml for error reports)
Typical use:
{
if (version_compare(PHP_VERSION,'5','>='))
require_once('domxml-php4-to-php5.php');
}
Version 1.17, 2007-06-05, http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
------------------------------------------------------------------
Written by Alexandre Alapetite, http://alexandre.alapetite.net/cv/
Copyright 2004-2007, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
http://creativecommons.org/licenses/by-sa/2.0/fr/
http://alexandre.alapetite.net/divers/apropos/#by-sa
- Attribution. You must give the original author credit
- Share Alike. If you alter, transform, or build upon this work,
you may distribute the resulting work only under a license identical to this one
(Can be included in GPL/LGPL projects)
- The French law is authoritative
- Any of these conditions can be waived if you get permission from Alexandre Alapetite
- Please send to Alexandre Alapetite the modifications you make,
in order to improve this file for the benefit of everybody
If you want to distribute this code, please do it as a link to:
http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
*/
define('DOMXML_LOAD_PARSING',0);
define('DOMXML_LOAD_VALIDATING',1);
define('DOMXML_LOAD_RECOVERING',2);
define('DOMXML_LOAD_SUBSTITUTE_ENTITIES',4);
//define('DOMXML_LOAD_COMPLETE_ATTRS',8);
define('DOMXML_LOAD_DONT_KEEP_BLANKS',16);
function domxml_new_doc($version) {return new php4DOMDocument();}
function domxml_new_xmldoc($version) {return new php4DOMDocument();}
function domxml_open_file($filename,$mode=DOMXML_LOAD_PARSING,&$error=null)
{
$dom=new php4DOMDocument($mode);
$errorMode=(func_num_args()>2);
if ($errorMode) libxml_use_internal_errors(true);
if (!$dom->myDOMNode->load($filename)) $dom=null;
if ($errorMode)
{
$error=array_map('_error_report',libxml_get_errors());
libxml_clear_errors();
}
return $dom;
}
function domxml_open_mem($str,$mode=DOMXML_LOAD_PARSING,&$error=null)
{
$dom=new php4DOMDocument($mode);
$errorMode=(func_num_args()>2);
if ($errorMode) libxml_use_internal_errors(true);
if (!$dom->myDOMNode->loadXML($str)) $dom=null;
if ($errorMode)
{
$error=array_map('_error_report',libxml_get_errors());
libxml_clear_errors();
}
return $dom;
}
function html_doc($html_doc,$from_file=false)
{
$dom=new php4DOMDocument();
if ($from_file) $result=$dom->myDOMNode->loadHTMLFile($html_doc);
else $result=$dom->myDOMNode->loadHTML($html_doc);
return $result ? $dom : null;
}
function html_doc_file($filename) {return html_doc($filename,true);}
function xmldoc($str) {return domxml_open_mem($str);}
function xmldocfile($filename) {return domxml_open_file($filename);}
function xpath_eval($xpath_context,$eval_str,$contextnode=null) {return $xpath_context->xpath_eval($eval_str,$contextnode);}
function xpath_new_context($dom_document) {return new php4DOMXPath($dom_document);}
function xpath_register_ns($xpath_context,$prefix,$namespaceURI) {return $xpath_context->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
function _error_report($error) {return array('errormessage'=>$error->message,'nodename'=>'','line'=>$error->line,'col'=>$error->column)+($error->file==''?array():array('directory'=>dirname($error->file),'file'=>basename($error->file)));}
class php4DOMAttr extends php4DOMNode
{
function name() {return $this->myDOMNode->name;}
function set_value($content) {return $this->myDOMNode->value=$content;}
function specified() {return $this->myDOMNode->specified;}
function value() {return $this->myDOMNode->value;}
}
class php4DOMDocument extends php4DOMNode
{
function php4DOMDocument($mode=DOMXML_LOAD_PARSING)
{
$this->myDOMNode=new DOMDocument();
$this->myOwnerDocument=$this;
if ($mode & DOMXML_LOAD_VALIDATING) $dom->myDOMNode->validateOnParse=true;
if ($mode & DOMXML_LOAD_RECOVERING) $dom->myDOMNode->recover=true;
if ($mode & DOMXML_LOAD_SUBSTITUTE_ENTITIES) $dom->myDOMNode->substituteEntities=true;
if ($mode & DOMXML_LOAD_DONT_KEEP_BLANKS) $dom->myDOMNode->preserveWhiteSpace=false;
}
function add_root($name)
{
if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
return new php4DOMElement($this->myDOMNode->appendChild($this->myDOMNode->createElement($name)),$this->myOwnerDocument);
}
function create_attribute($name,$value)
{
$myAttr=$this->myDOMNode->createAttribute($name);
$myAttr->value=$value;
return new php4DOMAttr($myAttr,$this);
}
function create_cdata_section($content) {return new php4DOMNode($this->myDOMNode->createCDATASection($content),$this);}
function create_comment($data) {return new php4DOMNode($this->myDOMNode->createComment($data),$this);}
function create_element($name) {return new php4DOMElement($this->myDOMNode->createElement($name),$this);}
function create_element_ns($uri,$name,$prefix=null)
{
if ($prefix==null) $prefix=$this->myDOMNode->lookupPrefix($uri);
if (($prefix==null)&&($this->myDOMNode->hasChildNodes())&&(!$this->myDOMNode->firstChild->isDefaultNamespace($uri))) $prefix='a'.sprintf('%u',crc32($uri));
return new php4DOMElement($this->myDOMNode->createElementNS($uri,$prefix==null ? $name : $prefix.':'.$name),$this);
}
function create_entity_reference($content) {return new php4DOMNode($this->myDOMNode->createEntityReference($content),$this);} //By Walter Ebert 2007-01-22
function create_text_node($content) {return new php4DOMText($this->myDOMNode->createTextNode($content),$this);}
function document_element() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
function dump_file($filename,$compressionmode=false,$format=false)
{
$format0=$this->myDOMNode->formatOutput;
$this->myDOMNode->formatOutput=$format;
$res=$this->myDOMNode->save($filename);
$this->myDOMNode->formatOutput=$format0;
return $res;
}
function dump_mem($format=false,$encoding=false)
{
$format0=$this->myDOMNode->formatOutput;
$this->myDOMNode->formatOutput=$format;
$encoding0=$this->myDOMNode->encoding;
if ($encoding) $this->myDOMNode->encoding=$encoding;
$dump=$this->myDOMNode->saveXML();
$this->myDOMNode->formatOutput=$format0;
if ($encoding) $this->myDOMNode->encoding= $encoding0=='' ? 'UTF-8' : $encoding0; //UTF-8 is XML default encoding
return $dump;
}
function free()
{
if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
$this->myDOMNode=null;
$this->myOwnerDocument=null;
}
function get_element_by_id($id) {return parent::_newDOMElement($this->myDOMNode->getElementById($id),$this);}
function get_elements_by_tagname($name)
{
$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
$nodeSet=array();
$i=0;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this);
return $nodeSet;
}
function html_dump_mem() {return $this->myDOMNode->saveHTML();}
function root() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
function xpath_new_context() {return new php4DOMXPath($this);}
}
class php4DOMElement extends php4DOMNode
{
function add_namespace($uri,$prefix)
{
if ($this->myDOMNode->hasAttributeNS('http://www.w3.org/2000/xmlns/',$prefix)) return false;
else
{
$this->myDOMNode->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$prefix,$uri); //By Daniel Walker 2006-09-08
return true;
}
}
function get_attribute($name) {return $this->myDOMNode->getAttribute($name);}
function get_attribute_node($name) {return parent::_newDOMElement($this->myDOMNode->getAttributeNode($name),$this->myOwnerDocument);}
function get_elements_by_tagname($name)
{
$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
$nodeSet=array();
$i=0;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this->myOwnerDocument);
return $nodeSet;
}
function has_attribute($name) {return $this->myDOMNode->hasAttribute($name);}
function remove_attribute($name) {return $this->myDOMNode->removeAttribute($name);}
function set_attribute($name,$value)
{
//return $this->myDOMNode->setAttribute($name,$value); //Does not return a DomAttr
$myAttr=$this->myDOMNode->ownerDocument->createAttribute($name);
$myAttr->value=$value;
$this->myDOMNode->setAttributeNode($myAttr);
return new php4DOMAttr($myAttr,$this->myOwnerDocument);
}
function set_attribute_node($attr)
{
$this->myDOMNode->setAttributeNode($this->_importNode($attr));
return $attr;
}
function set_name($name)
{
if ($this->myDOMNode->prefix=='') $newNode=$this->myDOMNode->ownerDocument->createElement($name);
else $newNode=$this->myDOMNode->ownerDocument->createElementNS($this->myDOMNode->namespaceURI,$this->myDOMNode->prefix.':'.$name);
$myDOMNodeList=$this->myDOMNode->attributes;
$i=0;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item($i++))
if ($node->namespaceURI=='') $newNode->setAttribute($node->name,$node->value);
else $newNode->setAttributeNS($node->namespaceURI,$node->nodeName,$node->value);
$myDOMNodeList=$this->myDOMNode->childNodes;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item(0)) $newNode->appendChild($node);
$this->myDOMNode->parentNode->replaceChild($newNode,$this->myDOMNode);
$this->myDOMNode=$newNode;
return true;
}
function tagname() {return $this->myDOMNode->tagName;}
}
class php4DOMNode
{
public $myDOMNode;
public $myOwnerDocument;
function php4DOMNode($aDomNode,$aOwnerDocument)
{
$this->myDOMNode=$aDomNode;
$this->myOwnerDocument=$aOwnerDocument;
}
function __get($name)
{
switch ($name)
{
case 'type': return $this->myDOMNode->nodeType;
case 'tagname': return $this->myDOMNode->tagName;
case 'content': return $this->myDOMNode->textContent;
case 'name': return $this->myDOMNode->name;
case 'value': return $this->myDOMNode->value;
default:
$myErrors=debug_backtrace();
trigger_error('Undefined property: '.get_class($this).'::$'.$name.' ['.$myErrors[0]['file'].':'.$myErrors[0]['line'].']',E_USER_NOTICE);
return false;
}
}
function add_child($newnode) {return append_child($newnode);}
function add_namespace($uri,$prefix) {return false;}
function append_child($newnode) {return self::_newDOMElement($this->myDOMNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
function append_sibling($newnode) {return self::_newDOMElement($this->myDOMNode->parentNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
function attributes()
{
$myDOMNodeList=$this->myDOMNode->attributes;
$nodeSet=array();
$i=0;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMAttr($node,$this->myOwnerDocument);
return $nodeSet;
}
function child_nodes()
{
$myDOMNodeList=$this->myDOMNode->childNodes;
$nodeSet=array();
$i=0;
if (isset($myDOMNodeList))
while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=self::_newDOMElement($node,$this->myOwnerDocument);
return $nodeSet;
}
function children() {return $this->child_nodes();}
function clone_node($deep=false) {return self::_newDOMElement($this->myDOMNode->cloneNode($deep),$this->myOwnerDocument);}
//dump_node($node) should only be called on php4DOMDocument
function dump_node($node=null) {return $node==null ? $this->myOwnerDocument->myDOMNode->saveXML($this->myDOMNode) : $this->myOwnerDocument->myDOMNode->saveXML($node->myDOMNode);}
function first_child() {return self::_newDOMElement($this->myDOMNode->firstChild,$this->myOwnerDocument);}
function get_content() {return $this->myDOMNode->textContent;}
function has_attributes() {return $this->myDOMNode->hasAttributes();}
function has_child_nodes() {return $this->myDOMNode->hasChildNodes();}
function insert_before($newnode,$refnode) {return self::_newDOMElement($this->myDOMNode->insertBefore($this->_importNode($newnode),$refnode==null?null:$refnode->myDOMNode),$this->myOwnerDocument);}
function is_blank_node() {return ($this->myDOMNode->nodeType===XML_TEXT_NODE)&&preg_match('%^\s*$%',$this->myDOMNode->nodeValue);}
function last_child() {return self::_newDOMElement($this->myDOMNode->lastChild,$this->myOwnerDocument);}
function new_child($name,$content)
{
$mySubNode=$this->myDOMNode->ownerDocument->createElement($name);
$mySubNode->appendChild($this->myDOMNode->ownerDocument->createTextNode(html_entity_decode($content,ENT_QUOTES)));
$this->myDOMNode->appendChild($mySubNode);
return new php4DOMElement($mySubNode,$this->myOwnerDocument);
}
function next_sibling() {return self::_newDOMElement($this->myDOMNode->nextSibling,$this->myOwnerDocument);}
function node_name() {return ($this->myDOMNode->nodeType===XML_ELEMENT_NODE) ? $this->myDOMNode->localName : $this->myDOMNode->nodeName;} //Avoid namespace prefix for DOMElement
function node_type() {return $this->myDOMNode->nodeType;}
function node_value() {return $this->myDOMNode->nodeValue;}
function owner_document() {return $this->myOwnerDocument;}
function parent_node() {return self::_newDOMElement($this->myDOMNode->parentNode,$this->myOwnerDocument);}
function prefix() {return $this->myDOMNode->prefix;}
function previous_sibling() {return self::_newDOMElement($this->myDOMNode->previousSibling,$this->myOwnerDocument);}
function remove_child($oldchild) {return self::_newDOMElement($this->myDOMNode->removeChild($oldchild->myDOMNode),$this->myOwnerDocument);}
function replace_child($newnode,$oldnode) {return self::_newDOMElement($this->myDOMNode->replaceChild($this->_importNode($newnode),$oldnode->myDOMNode),$this->myOwnerDocument);}
function set_content($text) {return $this->myDOMNode->appendChild($this->myDOMNode->ownerDocument->createTextNode($text));}
//function set_name($name) {return $this->myOwnerDocument->renameNode($this->myDOMNode,$this->myDOMNode->namespaceURI,$name);}
function set_namespace($uri,$prefix=null)
{//Contributions by Daniel Walker 2006-09-08
$nsprefix=$this->myDOMNode->lookupPrefix($uri);
if ($nsprefix==null)
{
$nsprefix= $prefix==null ? $nsprefix='a'.sprintf('%u',crc32($uri)) : $prefix;
if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
{
if (($prefix!=null)&&$this->myDOMNode->ownerElement->hasAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)&&
($this->myDOMNode->ownerElement->getAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)!=$uri))
{//Remove namespace
$parent=$this->myDOMNode->ownerElement;
$parent->removeAttributeNode($this->myDOMNode);
$parent->setAttribute($this->myDOMNode->localName,$this->myDOMNode->nodeValue);
$this->myDOMNode=$parent->getAttributeNode($this->myDOMNode->localName);
return;
}
$this->myDOMNode->ownerElement->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$nsprefix,$uri);
}
}
if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
{
$parent=$this->myDOMNode->ownerElement;
$parent->removeAttributeNode($this->myDOMNode);
$parent->setAttributeNS($uri,$nsprefix.':'.$this->myDOMNode->localName,$this->myDOMNode->nodeValue);
$this->myDOMNode=$parent->getAttributeNodeNS($uri,$this->myDOMNode->localName);
}
elseif ($this->myDOMNode->nodeType===XML_ELEMENT_NODE)
{
$NewNode=$this->myDOMNode->ownerDocument->createElementNS($uri,$nsprefix.':'.$this->myDOMNode->localName);
foreach ($this->myDOMNode->attributes as $n) $NewNode->appendChild($n->cloneNode(true));
foreach ($this->myDOMNode->childNodes as $n) $NewNode->appendChild($n->cloneNode(true));
$xpath=new DOMXPath($this->myDOMNode->ownerDocument);
$myDOMNodeList=$xpath->query('namespace::*[name()!="xml"]',$this->myDOMNode); //Add old namespaces
foreach ($myDOMNodeList as $n) $NewNode->setAttributeNS('http://www.w3.org/2000/xmlns/',$n->nodeName,$n->nodeValue);
$this->myDOMNode->parentNode->replaceChild($NewNode,$this->myDOMNode);
$this->myDOMNode=$NewNode;
}
}
function unlink_node()
{
if ($this->myDOMNode->parentNode!=null)
{
if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE) $this->myDOMNode->parentNode->removeAttributeNode($this->myDOMNode);
else $this->myDOMNode->parentNode->removeChild($this->myDOMNode);
}
}
protected function _importNode($newnode) {return $this->myOwnerDocument===$newnode->myOwnerDocument ? $newnode->myDOMNode : $this->myOwnerDocument->myDOMNode->importNode($newnode->myDOMNode,true);} //To import DOMNode from another DOMDocument
static function _newDOMElement($aDOMNode,$aOwnerDocument)
{//Check the PHP5 DOMNode before creating a new associated PHP4 DOMNode wrapper
if ($aDOMNode==null) return null;
switch ($aDOMNode->nodeType)
{
case XML_ELEMENT_NODE: return new php4DOMElement($aDOMNode,$aOwnerDocument);
case XML_TEXT_NODE: return new php4DOMText($aDOMNode,$aOwnerDocument);
case XML_ATTRIBUTE_NODE: return new php4DOMAttr($aDOMNode,$aOwnerDocument);
default: return new php4DOMNode($aDOMNode,$aOwnerDocument);
}
}
}
class php4DOMText extends php4DOMNode
{
function __get($name)
{
if ($name==='tagname') return '#text';
else return parent::__get($name);
}
function tagname() {return '#text';}
}
if (!defined('XPATH_NODESET'))
{
define('XPATH_UNDEFINED',0);
define('XPATH_NODESET',1);
/*define('XPATH_BOOLEAN',2);
define('XPATH_NUMBER',3);
define('XPATH_STRING',4);
define('XPATH_POINT',5);
define('XPATH_RANGE',6);
define('XPATH_LOCATIONSET',7);
define('XPATH_USERS',8);
define('XPATH_XSLT_TREE',9);*/
}
class php4DOMNodelist
{//TODO: To be updated for PHP>=5.1 to allow XPath boolean expressions etc. DOMXPath->evaluate()
private $myDOMNodelist;
public $nodeset;
public $type;
function php4DOMNodelist($aDOMNodelist,$aOwnerDocument)
{
$this->myDOMNodelist=$aDOMNodelist;
$this->nodeset=array();
$i=0;
if (isset($this->myDOMNodelist))
{
$this->type=XPATH_NODESET;
while ($node=$this->myDOMNodelist->item($i++)) $this->nodeset[]=php4DOMNode::_newDOMElement($node,$aOwnerDocument);
}
else $this->type=XPATH_UNDEFINED;
}
}
class php4DOMXPath
{
public $myDOMXPath;
private $myOwnerDocument;
function php4DOMXPath($dom_document)
{
//TODO: If $dom_document is a DomElement, make that default $contextnode and modify XPath. Ex: '/test'
$this->myOwnerDocument=$dom_document->myOwnerDocument;
$this->myDOMXPath=new DOMXPath($this->myOwnerDocument->myDOMNode);
}
function xpath_eval($eval_str,$contextnode=null) {return isset($contextnode) ? new php4DOMNodelist($this->myDOMXPath->query($eval_str,$contextnode->myDOMNode),$this->myOwnerDocument) : new php4DOMNodelist($this->myDOMXPath->query($eval_str),$this->myOwnerDocument);}
function xpath_register_ns($prefix,$namespaceURI) {return $this->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
}
if (extension_loaded('xsl'))
{//See also: http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
function domxml_xslt_stylesheet($xslstring) {return new php4DomXsltStylesheet(DOMDocument::loadXML($xslstring));}
function domxml_xslt_stylesheet_doc($dom_document) {return new php4DomXsltStylesheet($dom_document);}
function domxml_xslt_stylesheet_file($xslfile) {return new php4DomXsltStylesheet(DOMDocument::load($xslfile));}
class php4DomXsltStylesheet
{
private $myxsltProcessor;
function php4DomXsltStylesheet($dom_document)
{
$this->myxsltProcessor=new xsltProcessor();
$this->myxsltProcessor->importStyleSheet($dom_document);
}
function process($dom_document,$xslt_parameters=array(),$param_is_xpath=false)
{
foreach ($xslt_parameters as $param=>$value) $this->myxsltProcessor->setParameter('',$param,$value);
$myphp4DOMDocument=new php4DOMDocument();
$myphp4DOMDocument->myDOMNode=$this->myxsltProcessor->transformToDoc($dom_document->myDOMNode);
return $myphp4DOMDocument;
}
function result_dump_file($dom_document,$filename)
{
$html=$dom_document->myDOMNode->saveHTML();
file_put_contents($filename,$html);
return $html;
}
function result_dump_mem($dom_document) {return $dom_document->myDOMNode->saveHTML();}
}
}
?>
--- NEW FILE: freebusy.class.php ---
<?php
/*
* Copyright (c) 2004-2006 Klaraelvdalens Datakonsult AB, Intra2net AG,
* erfrakon Partnerschaftsgesellschaft
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
* Thomas Jarosch <thomas.jarosch at intra2net.com>
* Martin Konold <martin.konold at erfrakon.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
/*
TODO: Looks like we don't get called for deleted folders.
Best thing would be to make the clients make the call.
As an alternative a update_pfbs(user) script which can be
called regularily for all users also helps with incompatible
updates.
*/
require_once 'Kolab/Freebusy/freebusycache.class.php';
require_once 'Kolab/Freebusy/freebusyimapcache.class.php';
require_once 'Kolab/Freebusy/recurrence.class.php';
if (version_compare(PHP_VERSION,'5', '>='))
require_once('Kolab/Freebusy/domxml-php4-to-php5.php');
class FreeBusyRecurrence extends Recurrence {
function FreeBusyRecurrence( &$imapcache, &$imapuid, &$vfb, &$extra ) {
$this->imapcache =& $imapcache;
$this->imapuid =& $imapuid;
$this->vfb =& $vfb;
$this->extra =& $extra;
}
function setBusy( $start, $end, $duration ) {
$this->imapcache->add_imap2fb($this->imapuid, $start, null, $duration, $this->extra);
}
var $imapcache;
var $vfb;
var $extra;
};
class FreeBusy {
function FreeBusy( $cache_dir,
$owner_email,
$username,
$password,
$imaphost,
$imapoptions,
$fbfuture=60,
$fbpast=0 ) {
$this->cache_dir = $cache_dir;
$this->owner_email = $owner_email;
$this->username = $username;
$this->password = $password;
$this->imaphost = $imaphost;
$this->imapoptions = $imapoptions;
$this->fbfuture = $fbfuture;
$this->fbpast = $fbpast;
$this->relevance = null;
}
function imapConnect() {
$this->imap_serverstring = "{".$this->imaphost.":".$this->imapport.$this->imapoptions."}";
$this->imap = imap_open( $this->imap_serverstring, $this->username, $this->password );
return $this->imap;
}
function imapDisconnect() {
return imap_close($this->imap);
}
function imapOpenMailbox($foldername = 'INBOX') {
$this->foldername = $foldername;
// Reset error stack
imap_errors();
$rc = imap_reopen($this->imap, $this->imap_serverstring . $this->foldername);
// PHP only returns false for imap_reopen() if we use an HALF_OPEN connection. doh!
if (imap_last_error() !== false)
$rc = false;
// $a = $this->imap->getAnnotation( '/vendor/kolab/folder-type', '*' );
// myLog( "$folder has annotation: ".print_r($a,true), RM_LOG_DEBUG);
return $rc;
}
function getACL() {
$imap_acls = imap_getacl($this->imap, $this->foldername);
$rtn = array();
while (list($user, $rights) = each($imap_acls))
$rtn[] = array("USER" => $user, "RIGHTS" => $rights);
return $rtn;
}
function getRelevance() {
// cached?
if (isset($this->relevance))
return $this->relevance;
$val = imap_getannotation( $this->imap, $this->foldername, '/vendor/kolab/incidences-for', 'value.shared' );
if( $val === false || empty($val) ) {
myLog("No /vendor/kolab/incidences-for found for ".$this->foldername, RM_LOG_DEBUG);
$this->relevance = "admins";
} else {
myLog("/vendor/kolab/incidences-for = ".print_r($val,true)." for ".$this->foldername, RM_LOG_DEBUG);
}
return $this->relevance;
}
function &generateFreeBusy($startstamp = NULL, $endstamp = NULL ) {
require_once 'PEAR.php';
require_once 'Horde/iCalendar.php';
require_once 'Horde/MIME.php';
require_once 'Horde/MIME/Message.php';
require_once 'Horde/MIME/Structure.php';
// Default the start date to today.
if (is_null($startstamp)) {
$month = date('n');
$year = date('Y');
$day = date('j');
$startstamp = strtotime( '-'.$this->fbpast.' days', mktime(0, 0, 0, $month, $day, $year) );
}
// Default the end date to the start date + fbfuture.
if (is_null($endstamp) || $endstamp < $startstamp) {
$endstamp = strtotime( '+'.$this->fbfuture.' days', $startstamp );
}
myLog("Creating pfb from $startstamp to $endstamp", RM_LOG_DEBUG);
// Create the new iCalendar.
$vCal = &new Horde_iCalendar();
$vCal->setAttribute('PRODID', '-//proko2//freebusy 1.0//EN');
$vCal->setAttribute('METHOD', 'PUBLISH');
// Create new vFreebusy.
$vFb = &Horde_iCalendar::newComponent('vfreebusy', $vCal);
$vFb->setAttribute('ORGANIZER', 'MAILTO:' . $this->username);
$vFb->setAttribute('DTSTAMP', time());
$vFb->setAttribute('DTSTART', $startstamp);
$vFb->setAttribute('DTEND', $endstamp);
// URL is not required, so out it goes...
//$vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
$status = imap_status_current($this->imap, SA_MESSAGES | SA_UIDVALIDITY | SA_UIDNEXT);
if( $status->messages == 0 ) {
//Outlook does not like the fake DTSTART and DTEND information
//see also: https://intevation.de/roundup/kolab/issue1422
//$vFb->setAttribute('DTSTART', 0, array(), false );
//$vFb->setAttribute('DTEND', 0, array(), false );
$vFb->setAttribute('COMMENT', 'This is a dummy vfreebusy that indicates an empty calendar');
/* It seems to be a bad idea to put bogus values in pfbs
* so we accept that they are not completely in line
* with the rfc and take care of the problem when merging
* pfbs to ifbs later
*/
//$vFb->addBusyPeriod( 'BUSY', 0,0, null );
$vCal->addComponent($vFb);
$retval = array($vCal->exportvCalendar(),$vCal->exportvCalendar());
return $retval;
}
myLog("Reading messagelist", RM_LOG_DEBUG);
$getMessages_start = microtime_float();
// $msglist = $this->imap->getMessagesList();
//$msglist = &$this->imap->getMessages();
myLog("FreeBusy::imap->getMessagesList() took ".(microtime_float()-$getMessages_start)." secs.", RM_LOG_DEBUG);
// if( PEAR::isError( $msglist ) ) return array( $msglist, null);
// foreach ($msglist as $msginfo) {
// This only happens in php standalone mode and is needed
// to make the debug log quiet
if (!isset($_SERVER["SERVER_NAME"]))
$_SERVER["SERVER_NAME"] = "localhost";
$imapcache = new FreeBusyIMAPCache($this->cache_dir."/", $this->owner_email, $this->foldername);
$imapcache->cache_load();
$uids = imap_search($this->imap, "UNDELETED", SE_UID);
if ($imapcache->check_folder_changed($status->uidvalidity, $status->uidnext, $this->getRelevance(), $uids)) {
reset ($uids);
while(list($key, $uid) = each($uids))
if (!$imapcache->check_uid_exists($uid))
$this->process_imap_message($imapcache, $uid, $startstamp, $endstamp);
}
// store cache and output free busy list
$imapcache->cache_store();
$imapcache->output_fb($vFb);
$xvCal = $vCal;
$xvCal->addComponent($vFb);
$vCal->addComponent($this->clearExtra($vFb));
// Generate the vCal file.
$result = array( $vCal->exportvCalendar(), $xvCal->exportvCalendar() );
return $result;
}
/********************** Private API below this line ********************/
function process_imap_message(&$imapcache, &$imapuid, &$startstamp, &$endstamp)
{
myLog("Processing new message $imapuid", RM_LOG_DEBUG);
$imapcache->add_empty_imap2fb($imapuid);
$textmsg = imap_fetchheader($this->imap, $imapuid, FT_UID).imap_body($this->imap, $imapuid, FT_UID);
$mimemsg = &MIME_Structure::parseTextMIMEMessage($textmsg);
// Read in a Kolab event object, if one exists
$parts = $mimemsg->contentTypeMap();
$event = false;
foreach ($parts as $mimeid => $conttype) {
if ($conttype == 'application/x-vnd.kolab.event') {
$part = $mimemsg->getPart($mimeid);
$part->transferDecodeContents();
$event = $this->getEventHash($part->getContents());
if ($event === false) {
myLog("No x-vnd.kolab.event in ".$part->getContents(), RM_LOG_DEBUG);
continue;
}
}
}
if ($event === false) {
myLog("No x-vnd.kolab.events at all ", RM_LOG_DEBUG);
return;
}
$uid = $event['uid'];
if( array_key_exists( 'show-time-as', $event ) &&
strtolower(trim($event['show-time-as'])) == 'free' ) {
return;
}
$summary = ($event['sensitivity'] == 'public' ? $event['summary'] : '');
//myLog("Looking at message with uid=$uid and summary=$summary", RM_LOG_DEBUG);
// Get the events initial start
$initial_start = $event['start-date'];
$initial_end = $event['end-date'];
if( $event['allday'] ) {
$initial_end = strtotime( '+1 day', $initial_end );
myLog("Detected all-day event $uid", RM_LOG_DEBUG);
}
$extra = array( 'X-UID' => base64_encode($uid) );
if (!empty($summary)) {
$extra['X-SUMMARY'] = base64_encode($summary);
}
if( !empty($event['scheduling-id']) ) {
$extra['X-SID'] = base64_encode($event['scheduling-id']);
}
if( !empty($event['location'])) {
$extra['X-LOCATION'] = base64_encode($event['location']);
}
if( array_key_exists( 'recurrence', $event ) ) {
myLog("Detected recurring event $uid", RM_LOG_DEBUG);
$rec = $event['recurrence'];
$recurrence =& new FreeBusyRecurrence( $imapcache, $imapuid, $vFb, $extra );
$recurrence->setStartDate( $initial_start );
$recurrence->setEndDate( $initial_end );
$recurrence->setCycletype( $rec['cycle'] );
if( isset($rec['type']) ) $recurrence->setType( $rec['type'] );
if( isset($rec['interval']) ) $recurrence->setInterval( (int)$rec['interval'] );
if( isset($rec['daynumber']) ) $recurrence->setDaynumber( $rec['daynumber'] );
if( isset($rec['day']) ) $recurrence->setDay( $rec['day'] );
if( isset($rec['month']) ) $recurrence->setMonth( $rec['month'] );
if( isset($rec['exclusion'] ) ) $recurrence->setExclusionDates( $rec['exclusion'] );
$rangetype = $rec['rangetype'];
if( $rangetype == 'number' ) {
$range = (int)$rec['range'];
} else if( $rangetype == 'date' ) {
$range = $this->parseDateTime( $rec['range'] );
} else {
$range = false;
}
$recurrence->setRangetype( $rangetype );
$recurrence->setRange( $range );
$recurrence->expand( $startstamp, $endstamp );
} else {
// Normal event
// Don't bother adding the initial event if it's outside our free/busy window
if ($initial_start < $startstamp || $initial_end > $endstamp) {
return;
}
// $initial_start/* + FreeBusy::tzOffset($initial_start)
$imapcache->add_imap2fb($imapuid, $initial_start, $initial_end, null, $extra);
}
}
function tzOffset( $ts ) {
$dstr = date('O',$ts);
return 3600 * substr( $dstr, 0, 3) + 60 * substr( $dstr, 3, 2);
}
// static function to compute foldername for imap
function imapFolderName( $user, $owner, $folder = false, $default_domain = false ) {
$userdom = false;
$ownerdom = false;
if( ereg( '(.*)@(.*)', $user, $regs ) ) {
// Regular user
$user = $regs[1];
$userdom = $regs[2];
} else {
// Domainless user (ie. manager)
}
if( ereg( '(.*)@(.*)', $owner, $regs ) ) {
$owner = $regs[1];
$ownerdom = $regs[2];
} else {
}
$fldrcomp = array('user',$owner );
if( $folder ) $fldrcomp[] = $folder;
$fldr = join('/', $fldrcomp );
if( $ownerdom && !$userdom ) $fldr .= '@'.$ownerdom;
return $fldr;
}
// Date/Time value parsing, courtesy of the Horde iCalendar library
function parseTime($text) {
// There must be a trailing 'Z' on a time
if (strlen($text) != 9) {
return false;
}
$time['hour'] = intval(substr($text, 0, 2));
$time['minute'] = intval(substr($text, 3, 2));
$time['second'] = intval(substr($text, 6, 2));
return $time;
}
function parseDate($text) {
if (strlen($text) != 10) {
return false;
}
$date['year'] = intval(substr($text, 0, 4));
$date['month'] = intval(substr($text, 5, 2));
$date['mday'] = intval(substr($text, 8, 2));
return $date;
}
function parseDateTime($text) {
$dateParts = split('T', $text);
if (count($dateParts) != 2 && !empty($text)) {
// Not a datetime field but may be just a date field.
if (!$date = FreeBusy::parseDate($text)) {
return $date;
}
return @gmmktime(0, 0, 0, $date['month'], $date['mday'], $date['year']);
}
if (!$date = FreeBusy::parseDate($dateParts[0])) {
return $date;
}
if (!$time = FreeBusy::parseTime($dateParts[1])) {
return $time;
}
return @gmmktime($time['hour'], $time['minute'], $time['second'],
$date['month'], $date['mday'], $date['year']);
}
function getEventHash($xml_text) {
$xmldoc = domxml_open_mem($xml_text, DOMXML_LOAD_PARSING +
DOMXML_LOAD_COMPLETE_ATTRS + DOMXML_LOAD_SUBSTITUTE_ENTITIES +
DOMXML_LOAD_DONT_KEEP_BLANKS, $error);
if (!empty($error)) {
// There were errors parsing the XML data - abort
myLog( "Error parsing \"$xml_txt\": $error", RM_LOG_ERROR);
return false;
}
$noderoot = $xmldoc->document_element();
$childnodes = $noderoot->child_nodes();
$event_hash = array();
// Build the event hash
foreach ($childnodes as $value) {
//myLog("Looking at tag ".($value->tagname), RM_LOG_DEBUG);
if( $value->tagname == 'recurrence' ) {
$rhash = array();
$attrs = $value->attributes();
foreach( $attrs as $attr ) {
//myLog("getEventHash setting rhash[".$attr->name."] = ".$attr->value, RM_LOG_DEBUG);
$rhash[$attr->name] = $attr->value;
}
foreach( $value->child_nodes() as $v ) {
if( $v->tagname == 'day' || $v->tagname == 'exclusion' ) {
$rhash[$v->tagname][] = $v->get_content();
} else {
$rhash[$v->tagname] = $v->get_content();
if( $v->tagname == 'range' && $v->has_attribute('type') ) {
$rhash['rangetype'] = $v->get_attribute('type');
}
}
}
$event_hash[$value->tagname] = $rhash;
} else {
$event_hash[$value->tagname] = $value->get_content();
}
}
//myLog("RAW Event: ".print_r($event_hash, true), RM_LOG_DEBUG);
// Perform some sanity checks on the event
if (
empty($event_hash['uid']) ||
empty($event_hash['start-date']) ||
empty($event_hash['end-date'])
) {
return false;
}
if (empty($event_hash['sensitivity'])) {
$event_hash['sensitivity'] = 'public';
}
// Set the summary if it's not present, so we don't get PHP warnings
// about accessing non-present keys
if (empty($event_hash['summary'])) {
$event_hash['summary'] = '';
}
// Convert our date-time values to timestamps
if( strpos( $event_hash['start-date'], 'T' ) === false &&
strpos( $event_hash['end-date'], 'T' ) === false ) {
$event_hash['allday'] = true;
} else {
$event_hash['allday'] = false;
}
$event_hash['start-date'] = FreeBusy::parseDateTime($event_hash['start-date']);
$event_hash['end-date'] = FreeBusy::parseDateTime($event_hash['end-date']);
return $event_hash;
}
function clearExtra( $vFb ) {
$vFb->_extraParams = array();
return $vFb;
}
var $cache_dir;
var $username;
var $password;
var $imaphost;
var $imapport = 143;
var $imapoptions;
var $foldername;
var $relevance;
// Settings
var $fbfuture;
var $fbpast;
var $default_domain = 'foo';
var $week_starts_on_sunday = false;
var $imap;
var $imap_serverstring;
};
?>
--- NEW FILE: freebusycache.class.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
* Copyright (c) 2005 Intra2net AG
* Copyright (c) 2006 erfrakon Partnerschaftsgesellschaft
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
* Written by Thomas Jarosch <thomas.jarosch at intra2net.com>
* Written by Martin Konold <martin.konold at erfrakon.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
/*! To load/store partial freebusy lists
and their ACLs */
require_once('Kolab/Freebusy/misc.php');
class FreeBusyCache {
function FreeBusyCache( $basedir, $dbformat, $params, $extended = false) {
$this->basedir = $basedir;
$this->dbformat = $dbformat;
$this->params = $params;
$this->extended = $extended;
/* make sure that a database really exists before accessing it */
if( !file_exists( $this->basedir.'/pfbcache.db' ) ) {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd' );
dba_close($db);
}
}
function store( $filename, $fbdata, $acl, $relevance ) {
$fbfilename = $this->mkfbfilename($filename);
myLog("FreeBusyCache::store( file=$fbfilename, acl=[ "
.str_replace("\n",", ",$this->aclToString($acl))
."], relevance=$relevance )", RM_LOG_DEBUG);
if( $fbdata === false ) {
// false data means delete the pfb
unlink($fbfilename);
$oldacl = $this->loadACL( $filename );
if( isset($this->params['dbtype']) ) {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->params['dbtype'] );
} else {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd' );
}
if( $db === false ) return false;
foreach( $oldacl as $ac ) {
if( dba_exists( $ac['USER'], $db ) ) {
$lst = dba_fetch( $ac['USER'], $db );
$lst = $this->decodeList( $lst );
$lst = array_diff( $lst, array($fbfilename));
myLog("(delete) dba_replace(".$uid.", \"".$this->encodeList($lst)."\")", RM_LOG_DEBUG);
dba_replace( $uid, $this->encodeList($lst), $db );
}
}
unlink($fbfilename.'.acl');
} else {
//myLog("Storing $filename with acl ".var_export($acl,true), RM_LOG_DEBUG);
// Create directories if missing
$fbdirname = dirname( $fbfilename );
if (!is_dir($fbdirname)) {
if( !$this->mkdirhier($fbdirname) ) {
$this->error = _("Error creating dir $fbdirname");
return false;
}
}
// Store the fb list
$tmpn = tempnam($this->basedir, 'fb');
$tmpf = fopen($tmpn, 'w');
if( !$tmpf ) return false;
fwrite($tmpf, $fbdata);
if( !rename($tmpn, $fbfilename) ) {
$this->error = _("Error renaming $tmpn to $fbfilename");
return false;
}
fclose($tmpf);
// Store the ACL
$oldacl = $this->loadACL( $filename );
if( !$this->storeACL( $filename, $acl ) ) return false;
// Update overview db
switch( $relevance ) {
case 'admins': $perm = 'a'; break;
case 'readers': $perm = 'r'; break;
case 'nobody': $perm = 'false'; break;
default: $perm = 'a';
}
if( isset($this->params['dbtype']) ) {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->params['dbtype'] );
} else {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd' );
}
if( $db === false ) {
myLog('Unable to open freebusy cache db '.$this->basedir.'/pfbcache.db',
RM_LOG_ERROR );
return false;
}
foreach( $acl as $ac ) {
$uid = $ac['USER'];
if( dba_exists( $uid, $db ) ) {
$lst = dba_fetch( $uid, $db );
$lst = $this->decodeList( $lst );
$lst = array_diff( $lst, array($filename));
dba_replace( $uid, $this->encodeList($lst), $db );
}
}
if( $perm !== false ) {
foreach( $acl as $ac ) {
if( strpos( $ac['RIGHTS'], $perm ) !== false ) {
if( dba_exists( $ac['USER'], $db ) ) {
$lst = dba_fetch( $ac['USER'], $db );
$lst = $this->decodeList( $lst );
$lst[] = $filename;
dba_replace( $ac['USER'], $this->encodeList(array_unique($lst)), $db );
} else {
dba_insert( $ac['USER'], $filename, $db );
}
}
}
}
dba_close($db);
return true;
}
}
function load( $filename, &$ts, &$acl ) {
myLog("FreeBusyCache::load( $filename )", RM_LOG_DEBUG);
$fbfilename = $this->mkfbfilename($filename);
if( file_exists($fbfilename) ) {
if( !is_null($ts)) $ts = filectime($fbfilename);
$acl = $this->loadACL($filename);
myLog("FreeBusyCache::load(): ts=$ts acl=[ "
.str_replace("\n",", ",$this->aclToString($acl))
."] )", RM_LOG_DEBUG);
return file_get_contents($fbfilename);
}
myLog("FreeBusyCache: file $fbfilename does not exist", RM_LOG_ERROR);
return false;
}
function delete( $filename ) {
$fbfilename = $this->mkfbfilename($filename);
unlink($fbfilename);
unlink($this->mkaclfilename($filename));
if( isset($this->params['dbtype']) ) {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->params['dbtype'] );
} else {
$db = dba_open( $this->basedir.'/pfbcache.db', 'cd' );
}
if( $db === false ) {
myLog("dba_open(".$this->basedir.'/pfbcache.db'.") failed", RM_LOG_ERROR);
return false;
}
for( $uid = dba_firstkey($db); $uid !== false; $uid = dba_nextkey($db)) {
$lst = dba_fetch( $uid, $db );
$lst = $this->decodeList( $lst );
$lst = array_diff( $lst, array($filename));
myLog("(delete) dba_replace(".$uid.", \"".$this->encodeList($lst)."\")", RM_LOG_DEBUG);
dba_replace( $uid, $this->encodeList($lst), $db );
}
dba_close($db);
}
function findAll( $uid, $groups ) {
$lst = array();
if( isset($this->params['dbtype']) ) {
$db = dba_open( $this->basedir.'/pfbcache.db', 'rd', $this->params['dbtype'] );
} else {
$db = dba_open( $this->basedir.'/pfbcache.db', 'rd' );
}
if( $db === false ) {
myLog("dba_open(".$this->basedir.'/pfbcache.db'.") failed", RM_LOG_ERROR);
return false;
}
$uids = $groups;
for( $i = 0; $i < count($uids); $i++ ) $uids[$i] = 'group:'.$uids[$i];
$uids[] = $uid;
foreach( $uids as $uid ) {
if( dba_exists( $uid, $db ) ) {
$tmplst = dba_fetch( $uid, $db );
myLog("Found ".$uid." := $tmplst", RM_LOG_DEBUG);
$lst = array_merge((array) $lst,(array) $this->decodeList( $tmplst ) );
} else {
myLog("$uid not found", RM_LOG_DEBUG);
}
}
dba_close($db);
$lst = array_unique($lst);
myLog( "FreeBusyCache::findAll( $uid, [".join(', ', $groups).'] ) = ['.join(', ',$lst).']',
RM_LOG_DEBUG );
return $lst;
}
/*************** Private API below this line *************/
// a copy of this function exists in freebusyimapcache.class.php
function mkdirhier( $dirname ) {
$base = substr($dirname,0,strrpos($dirname,'/'));
$base = str_replace(".", "^", $base);
if( !empty( $base ) && !is_dir( $base ) ) {
if( !$this->mkdirhier( $base ) ) return false;
}
if( !file_exists( $dirname ) ) return mkdir( $dirname, 0755 );
return true;
}
function mkfbfilename( $fbfilename ) {
$fbfilename = str_replace( '.', '^', $fbfilename );
$fbfilename = str_replace( "\0", '', $fbfilename );
return $this->basedir.'/'.$fbfilename.($this->extended?'.xpfb':'.pfb');
}
function mkaclfilename( $fbfilename ) {
$fbfilename = str_replace( '.', '^', $fbfilename );
$fbfilename = str_replace( "\0", '', $fbfilename );
return $this->basedir.'/'.$fbfilename.($this->extended?'.xpfb':'.pfb').'.acl';
}
function aclToString( $acl ) {
$aclstr = '';
foreach( $acl as $ac ) {
$aclstr .= $ac['USER'].' '.$ac['RIGHTS']."\n";
}
return $aclstr;
}
function aclFromString( $aclstr ) {
$acl = array();
foreach( split("\n", $aclstr ) as $ac ) {
if( ereg("(.*) (.*)", $ac, $regs ) ) {
$acl[] = array('USER' => $regs[1], 'RIGHTS' => $regs[2] );
}
}
return $acl;
}
function loadACL( $filename ) {
return $this->aclFromString( @file_get_contents($this->mkaclfilename($filename)) );
}
function storeACL( $filename, $acl ) {
$tmpn = tempnam($this->basedir, 'acl');
$tmpf = fopen($tmpn, 'w');
if( !$tmpf ) return false;
fwrite($tmpf, $this->aclToString($acl) );
$aclfilename = $this->mkaclfilename($filename);
if( !rename($tmpn, $aclfilename) ) {
$this->error = _("Error renaming $tmpn to $fbfilename");
return false;
}
fclose($tmpf);
return true;
}
function aclDiff( $oldacl, $newacl ) {
$newuids = array();
foreach( $newacl as $ac ) {
if( strpos( $ac['RIGHTS'], 'r' ) !== false ) {
$newuids[$ac['USER']] = true;
}
}
$deleteduids = array();
foreach( $oldacl as $ac ) {
if( !$newuids[$ac['USER']] ) $deleteduids[] = $ac['USER'];
}
return $deleteduids;
}
/** Returns an array with cyrus permission chars (lrsp...) as keys */
function getRights( $acl, $uid, $groups ) {
if( !isset($uid) ) return false;
if( is_array( $groups ) ) $uids = $groups;
else $uids = array();
for( $i = 0; $i < count($uids); $i++ ) $uids[$i] = 'group:'.$uids[$i];
$uids[] = $uid;
$rights = array();
$negacl = array();
// Calc positive rights
foreach( $acl as $ac ) {
$r = $ac['RIGHTS'];
$u = $ac['USER'];
if( $r{0} == '-' ) {
$negacl[] = array( 'USER' => $u, 'RIGHTS' => $r );
continue;
}
if( in_array( $u, $uids ) ) {
for( $i = 0; $i < strlen($r); $i++ ) {
$rights[$r{$i}] = true;
}
}
}
// Remove negative rights
foreach( $negacl as $ac ) {
$r = $ac['RIGHTS'];
$u = $ac['USER'];
if( in_array( $u, $uids ) ) {
for( $i = 1; $i < strlen($r); $i++ ) {
unset($rights[$r{$i}]);
}
}
}
return $rights;
}
function decodeList( $str ) {
myLog("FreeBusyCache::decodeList($str)", RM_LOG_DEBUG);
return split( ',', $str );
}
function encodeList( $lst ) {
return join(',',$lst);
}
function recursivedir( $dir ) {
$dh = opendir( $dir );
if( $dh === false ) return false;
$dirs = array();
while (($file = readdir($dh)) !== false) {
if( is_dir($dir.'/'.$file) ) {
if($file=='.' || $file=='..') continue;
if( !ereg( ($this->extended?'/.*\.xpfb$/':'/.*\.pfb$/'), $file ) ) continue;
$tmp = $this->recursivedir( $dir.'/'.$file );
if( $tmp !== false ) $dirs = array_merge((array) $dirs,(array) $tmp );
} else if( is_file($dir.'/'.$file) ) {
$dirs[] = $dir.'/'.$file;
}
}
closedir( $dh );
return $dirs;
}
var $basedir;
var $dbformat;
var $error;
};
?>
--- NEW FILE: freebusycollector.class.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
define( 'FB_OK', 0 );
define( 'FB_TOO_OLD', 1 );
define( 'FB_PARSE_ERROR', 2 );
class FreeBusyCollector {
function FreeBusyCollector( $organizer ) {
$this->organizer = $organizer;
$this->init();
}
function init() {
require_once 'PEAR.php';
require_once 'Horde/iCalendar.php';
require_once 'Horde/MIME.php';
require_once 'Horde/MIME/Message.php';
require_once 'Horde/MIME/Structure.php';
// Create the new iCalendar.
$this->vCal = &new Horde_iCalendar();
$this->vCal->setAttribute('PRODID', '-//proko2//freebusy 1.0//EN');
$this->vCal->setAttribute('METHOD', 'PUBLISH');
// Create new vFreebusy.
$vFb = &Horde_iCalendar::newComponent('vfreebusy', $this->vCal);
$vFb->setAttribute('ORGANIZER', 'MAILTO:' . $this->organizer);
$vFb->setAttribute('DTSTAMP', time());
//$vFb->setAttribute('DTSTART', $startstamp);
//$vFb->setAttribute('DTEND', $endstamp);
$vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
$this->vCal->addComponent($vFb);
}
function addFreebusy( $text ) {
$vCal = &new Horde_iCalendar();
if( !$vCal->parsevCalendar($text) ) {
trigger_error("Could not parse ical", E_USER_ERROR);
return FB_PARSE_ERROR;
}
$vFb1 = &$this->vCal->findComponent( 'vfreebusy' );
$vFb2 = &$vCal->findComponent( 'vfreebusy' );
if( !$vFb2 ) {
trigger_error("Could not find freebusy info in ical", E_USER_ERROR);
return FB_PARSE_ERROR;
}
if( $ets = $vFb2->getAttributeDefault( 'DTEND', false ) !== false ) {
// PENDING(steffen): Make value configurable
if( $ets < time() ) {
// Not relevant anymore
return FB_TOO_OLD;
}
}
if( ($sts = $vFb1->getAttributeDefault('DTSTART', false)) === false ) {
$vFb1->setAttribute('DTSTART', $vFb2->getAttribute('DTSTART'), array(), false );
} else {
$vFb1->setAttribute('DTSTART', min( $sts, $vFb2->getAttribute('DTSTART') ), array(), false );
}
if( ($ets = $vFb1->getAttributeDefault('DTEND', false)) === false ) {
$vFb1->setAttribute('DTEND', $vFb2->getAttribute('DTEND'), array(), false );
} else {
$vFb1->setAttribute('DTEND', max( $ets, $vFb2->getAttribute('DTEND')), array(), false );
}
$vFb1->merge( $vFb2 );
return FB_OK;
}
function exportvCalendar() {
//$vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
$vFb = &$this->vCal->findComponent( 'vfreebusy' );
if( !(boolean)$vFb->getBusyPeriods() ) {
/* No busy periods in fb list. We have to add a
* dummy one to be standards compliant
*/
$vFb->setAttribute('COMMENT', 'This is a dummy vfreebusy that indicates an empty calendar');
$vFb->addBusyPeriod( 'BUSY', 0,0, null );
}
return $this->vCal->exportvCalendar();
}
function collect( $user, &$cache ) {
}
var $organizer;
var $vCal;
};
?>
--- NEW FILE: freebusyimapcache.class.php ---
<?php
/*
* Copyright (c) 2005 Intra2net AG
* Copyright (c) 2006 erfrakon Partnerschaftsgesellschaft
*
* Written by Thomas Jarosch <thomas.jarosch at intra2net.com>
* Written by Martin Konold <martin.konold at erfrakon.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
/* Class to efficently cache kolab events stored on an IMAP server */
class FreeBusyIMAPCache {
var $version; // internal version of format
var $store_prefix; // prefix to prepend to all file operations
var $owner; // folder owner
var $foldername; // folder name
var $cache_modified; // indicates if we have to writeout the cache
var $cache; // cache data
function FreeBusyIMAPCache($store_prefix, &$owner, &$foldername) {
$this->store_prefix = $store_prefix;
$this->owner = $owner;
$this->foldername = $foldername;
$this->version = 1;
$this->reset_cache();
}
function reset_cache() {
$this->cache = array();
$this->cache["version"] = $this->version;
$this->cache["uidvalidity"] = -1;
$this->cache["uidnext"] = -1;
$this->cache["incidences-for"] = "";
$this->cache["imap2fb"] = array();
$this->cache_modified = true;
}
function check_folder_changed($uidvalidity, $uidnext, $incidences_for, &$new_uids) {
$changed = false;
// uidvalidity changed?
if ($uidvalidity != $this->cache["uidvalidity"]) {
myLog("uidvalidity changed (old: ".$this->cache["uidvalidity"].", new: $uidvalidity), clearing cache", RM_LOG_DEBUG);
$this->reset_cache();
$changed = true;
}
// uidnext changed?
if ($uidnext != $this->cache["uidnext"]) {
myLog("uidnext on folder changed (old: ".$this->cache["uidnext"].", new: ".$uidnext.")", RM_LOG_DEBUG);
$changed = true;
}
// incidences-for changed?
if ($incidences_for != $this->cache["incidences-for"]) {
myLog("incidences-for changed (old: ".$this->cache["incidences-for"].", new: $incidences_for), clearing cache", RM_LOG_DEBUG);
$this->reset_cache();
$changed = true;
}
$this->cache["uidvalidity"] = $uidvalidity;
$this->cache["uidnext"] = $uidnext;
$this->cache["incidences-for"] = $incidences_for;
// deleted a message?
$old_uids = array_keys($this->cache["imap2fb"]);
while(list($key, $old_uid) = each($old_uids)) {
if (!in_array($old_uid, $new_uids)) {
unset($this->cache["imap2fb"][$old_uid]);
$this->cache_modified = true;
$changed = true;
}
}
if (!$changed)
myLog("check_changed: folder didn't change", RM_LOG_DEBUG);
return $changed;
}
function check_uid_exists($uid) {
return array_key_exists($uid, $this->cache["imap2fb"]);
}
function add_empty_imap2fb(&$imap_uid) {
$this->cache["imap2fb"][$imap_uid] = array();
$this->cache_modified = true;
}
function add_imap2fb(&$imap_uid, $fb_start, $fb_end, $fb_duration, $fb_extra) {
/*
Internal imap2fb array structure:
0..n IMAP uid
|----------- 0..n free/busy periods
|----------- start
|----------- end
|----------- duration
|----------- extra
*/
myLog("added event to store: uid: $imap_uid, start: $fb_start, end: $fb_end, duration: $fb_duration", RM_LOG_DEBUG);
$store = array();
$store["start"] = $fb_start;
$store["end"] = $fb_end;
$store["duration"] = $fb_duration;
$store["extra"] = $fb_extra;
$this->cache["imap2fb"][$imap_uid][] = $store;
$this->cache_modified = true;
}
function output_fb(&$vFb) {
reset($this->cache["imap2fb"]);
while(list($uid, $periods) = each($this->cache["imap2fb"]))
while(list($key, $period) = each($periods))
$vFb->addBusyPeriod('BUSY', $period["start"], $period["end"], $period["duration"], $period["extra"]);
}
function compute_filename() {
$folder_parts = explode('/', $this->foldername);
unset($folder_parts[0]);
$folder_storename = join('/', $folder_parts);
$folder_storename = str_replace(".", "^", $folder_storename);
$folder_storename = str_replace("\0", "", $folder_storename);
if( ereg( '(.*)@(.*)', $this->owner, $regs ) ) {
$domain = $regs[2].'/';
$domain = str_replace(".", "^", $domain);
$domain = str_replace("\0", "", $domain);
} else if( ereg('(.*)@(.*)', $folder_storename, $regs ) ) {
// Folder-encoded domain, ie. domainless user
$domain = $regs[2].'/';
$folder_storename = $regs[1];
} else $domain = '';
$full_path = $this->store_prefix.$domain.$folder_storename.".imapcache";
return $full_path;
}
function cache_load() {
$filename = $this->compute_filename();
myLog("Trying to load file: $filename", RM_LOG_DEBUG);
if (!is_readable($filename))
return false;
$this->cache = unserialize(file_get_contents($filename));
// Delete disc cache if it's from an old version
if ($this->cache["version"] != $this->version) {
myLog("Version mismatch (got: ".$this->cache["version"].", current: ".$this->version.", dropping cache", RM_LOG_WARN);
$this->reset_cache();
}
else $this->cache_modified = false;
return true;
}
function cache_store($force=false) {
if ($this->cache_modified || $force) {
$filename = $this->compute_filename();
myLog("Trying to save cache to file: $filename", RM_LOG_DEBUG);
if (!$this->mkdirhier(dirname($filename))) {
myLog("can't create director hierachy: ".dirname($filename), RM_LOG_ERROR);
return;
}
$tmpname = tempnam(dirname($this->store_prefix), 'imapcache');
$fp = fopen($tmpname, 'w');
if(!$fp) return false;
if (fwrite($fp, serialize($this->cache)) === false) {
fclose ($fp);
myLog("can't write to file: $tmpname. Out of discspace?", RM_LOG_ERROR);
return;
}
if(!rename($tmpname, $filename)) {
myLog("can't rename $tmpname to $filename", RM_LOG_ERROR);
return false;
}
fclose($fp);
$this->cache_modified = false;
}
else {
myLog("IMAPcache unmodified, not saving", RM_LOG_DEBUG);
}
}
function cache_delete() {
unlink($this->compute_filename());
$this->reset_cache();
}
function mkdirhier( $dirname ) {
$base = substr($dirname,0,strrpos($dirname,'/'));
$base = str_replace(".", "^", $base);
if( !empty( $base ) && !is_dir( $base ) ) {
if( !$this->mkdirhier( $base ) ) return false;
}
if( !file_exists( $dirname ) ) return mkdir( $dirname, 0755 );
return true;
}
};
?>
--- NEW FILE: freebusyldap.class.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
class FreeBusyLDAP {
function FreeBusyLDAP( $uri, $base ) {
$this->is_bound = false;
$this->uri = $uri;
$this->base = $base;
return $this->connection=ldap_connect($uri);
}
function error() {
return ldap_error( $this->connection );
}
function close() {
$rc = ldap_close( $this->connection );
$this->connection = false;
return $rc;
}
function bind( $dn = false , $pw = '' ) {
if( $dn ) return $this->is_bound = ldap_bind( $this->connection, $dn, $pw );
else return $this->is_bound = ldap_bind( $this->connection);
}
function freeBusyPast() {
$result = ldap_read( $this->connection, $this->base,
'(&(objectClass=kolab)(k=kolab))',
array( 'kolabFreeBusyPast' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
if( $entries['count'] > 0 && !empty($entries[0]['kolabfreebusypast'][0]) ) {
ldap_free_result($result);
return $entries[0]['kolabfreebusypast'][0];
}
}
return 0; // Default
}
// Return a hash of info about a user
function userInfo( $uid ) {
$result = ldap_search( $this->connection, $this->base,
'(&(objectClass=kolabInetOrgPerson)(|(uid='.
$this->escape($uid).')(mail='.$this->escape($uid).')))',
array( 'dn','mail','uid','kolabHomeServer', 'kolabFreeBusyFuture' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
if( $entries['count'] > 0 && !empty($entries[0]['mail'][0]) ) {
$hash = array();
$hash['DN'] = $this->readLdapAttr( $entries[0], 'dn' );
$hash['UID'] = $this->readLdapAttr( $entries[0], 'uid' );
$hash['MAIL'] = $this->readLdapAttr( $entries[0], 'mail', $uid );
$hash['HOMESERVER'] = $this->readLdapAttr( $entries[0], 'kolabhomeserver' );
$hash['FBFUTURE'] = (int)($this->readLdapAttr( $entries[0], 'kolabfreebusyfuture', 60 ));
ldap_free_result( $result );
return $hash;
}
ldap_free_result( $result );
}
return false;
}
function mailForUid( $uid ) {
return $this->_internalLookupMail('(&(objectClass=kolabInetOrgPerson)(uid='.$uid.'))',$uid);
}
function mailForUidOrAlias( $uid ) {
return $this->_internalLookupMail('(&(objectClass=kolabInetOrgPerson)(|(uid='.$uid.')(alias='.$uid.')))',$uid);
}
function homeServer( $uid ) {
$result = ldap_search( $this->connection, $this->base,
'(&(objectClass=kolabInetOrgPerson)(|(uid='.$uid.')(mail='.$uid.')))',
array( 'kolabhomeserver' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
if( $entries['count'] > 0 && !empty($entries[0]['kolabhomeserver'][0]) )
return $entries[0]['kolabhomeserver'][0];
}
return false;
}
function dn( $uid ) {
$result = ldap_search( $this->connection, $this->base,
'(&(objectClass=kolabInetOrgPerson)(|(uid='.$uid.')(mail='.$uid.')))',
array( 'dn' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
if( $entries['count'] > 0 ) {
return $entries[0]['dn'];
}
}
return false;
}
function distlists( $dn ) {
$result = ldap_search( $this->connection, $this->base,
'(&(objectClass=kolabGroupOfNames)(member='.FreeBusyLDAP::escape($dn).'))',
array( 'cn' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
$lst = array();
for( $i = 0; $i < $entries['count']; $i++ ) {
$lst[] = $entries[$i]['cn'][0];
}
myLog( "FreeBusyLDAP::distlists( $dn ) found ".count($lst)." entries",
RM_LOG_DEBUG );
return $lst;
}
myLog( "FreeBusyLDAP::distlists( $dn ) found nothing",
RM_LOG_DEBUG );
return false;
}
/**********/
function escape( $str ) {
/*
From RFC-2254:
If a value should contain any of the following characters
Character ASCII value
---------------------------
* 0x2a
( 0x28
) 0x29
\ 0x5c
NUL 0x00
the character must be encoded as the backslash '\' character (ASCII
0x5c) followed by the two hexadecimal digits representing the ASCII
value of the encoded character. The case of the two hexadecimal
digits is not significant.
*/
$str = str_replace( '\\', '\\5c', $str );
$str = str_replace( '*', '\\2a', $str );
$str = str_replace( '(', '\\28', $str );
$str = str_replace( ')', '\\29', $str );
$str = str_replace( '\0', '\\00', $str );
return $str;
}
function readLdapAttr( $entry, $attrname, $default = false ) {
$val = $default;
if( !array_key_exists( $attrname, $entry ) ) return $default;
else if( is_array( $entry[$attrname] ) ) {
$val = $entry[$attrname][0];
} else {
$val = $entry[$attrname];
}
if( $val == '' ) $val = $default;
return $val;
}
function _internalLookupMail( $filter, $default ) {
if( !isset( $default ) ) return false;
$result = ldap_search( $this->connection, $this->base, $filter,
array( 'mail' ) );
if( $result ) {
$entries = ldap_get_entries( $this->connection, $result );
if( $entries['count'] > 0 && !empty($entries[0]['mail'][0]) ) return $entries[0]['mail'][0];
}
return $default;
}
var $connection;
var $is_bound;
var $uri;
var $base;
};
?>
--- NEW FILE: freebusyldap_dummy.class.php ---
<?php
/*
* Copyright (c) 2005 Intra2net AG
* Copyright (c) 2006 erfrakon Partnerschaftsgesellschaft
*
* Written by Thomas Jarosch <thomas.jarosch at intra2net.com>
* Written by Martin Konold <martin.konold at erfrakon.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
class FreeBusyLDAP {
function FreeBusyLDAP( $uri, $base ) {
return true;
}
function error() {
return "LDAP::error not implemented";
}
function close() {
return true;
}
function bind( $dn = false , $pw = '' ) {
return true;
}
function freeBusyPast() {
return 0; // Default
}
// Return a hash of info about a user
function userInfo( $uid ) {
$rtn = array();
$rtn["MAIL"] = $uid;
$rtn["HOMESERVER"] = "";
$rtn["FBFUTURE"] = 60;
return $rtn;
}
function mailForUid( $uid ) {
return $uid;
}
function homeServer( $uid ) {
return "localhost";
}
function dn( $uid ) {
return "";
}
function distlists( $dn ) {
return array();
}
};
?>
--- NEW FILE: misc.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
function shutdown() {
global $fb, $ldap;
if (isset($fb) && $fb !== false) {
$fb->imapDisconnect();
$fb = false;
}
if (isset($ldap) && $ldap !== false) {
$ldap->close();
$ldap = false;
}
logClose();
}
function serverError($errortext) {
myLog( $errortext, RM_LOG_ERROR );
shutdown();
header('HTTP/1.0 500 Server Error');
echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Server Error</title>
</head><body>
<h1>Error</h1>
<p>'.htmlentities($_SERVER['REQUEST_URI']) . ':</p>
';
if (!empty($errortext)) {
echo "<hr>
<pre>$errortext</pre>
";
}
echo '<hr>
' . $_SERVER['SERVER_SIGNATURE'] . '</body></html>';
exit;
}
function notFound($errortext) {
myLog( $errortext, RM_LOG_ERROR );
shutdown();
header('HTTP/1.0 404 Not Found');
echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL ' . htmlentities($_SERVER['REQUEST_URI']) . ' was not found on this server.</p>
';
if (!empty($errortext)) {
echo "<hr>
<pre>$errortext</pre>
";
}
echo '<hr>
' . $_SERVER['SERVER_SIGNATURE'] . '</body></html>';
exit;
}
function unauthorized($errortext = '') {
global $params;
myLog( $errortext, RM_LOG_ERROR );
shutdown();
header('WWW-Authenticate: Basic realm="freebusy-'.$params['email_domain'].'"');
header('HTTP/1.0 401 Unauthorized');
echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>You are not authorized to access the requested URL.</p>
';
if (!empty($errortext)) {
echo "<hr>
<pre>$errortext</pre>
";
}
echo '<hr>
' . $_SERVER['SERVER_SIGNATURE'] . '</body></html>
';
exit;
}
// What myLog levels we can use
define('RM_LOG_SUPER', -1);
define('RM_LOG_SILENT', 0);
define('RM_LOG_ERROR', 1);
define('RM_LOG_WARN', 2);
define('RM_LOG_INFO', 3);
define('RM_LOG_DEBUG', 4);
$logLevelPrefixes = array(
RM_LOG_SUPER => '',
RM_LOG_SILENT => '',
RM_LOG_ERROR => 'Error',
RM_LOG_WARN => 'Warning',
RM_LOG_INFO => '',
RM_LOG_DEBUG => 'Debug',
);
// What logging mechanisms are available for use
define('RM_LOG_SYSLOG', 1);
define('RM_LOG_FILE', 2);
define('RM_LOG_STDERR', 3);
$logType = 0;
$logPrefix = '';
$logDestination = NULL;
function logInit($name = '')
{
global $params, $argv, $logType, $logPrefix, $logDestination;
if (empty($name)) {
$name = basename($argv[0]);
}
if (!array_key_exists('log', $params) || empty($params['log'])) {
return;
}
$matches = array();
if (preg_match('/(\w+):(.*)?/', $params['log'], $matches)) {
switch ($matches[1]) {
case 'syslog':
$logType = RM_LOG_SYSLOG;
$txtopts = preg_split('/[\s,]+/', $matches[2], -1, PREG_SPLIT_NO_EMPTY);
$options = 0;
foreach ($txtopts as $txtopt) {
switch ($txtopt) {
case 'cons': $options |= LOG_CONS; break;
case 'ndelay': $options |= LOG_NDELAY; break;
case 'odelay': $options |= LOG_ODELAY; break;
case 'perror': $options |= LOG_PERROR; break;
case 'pid': $options |= LOG_PID; break;
}
}
openlog($name, $options, LOG_USER);
break;
case 'file':
$logType = RM_LOG_FILE;
$logPrefix = $name;
$logDestination = fopen($matches[2], 'a');
break;
case 'stderr':
$logType = RM_LOG_STDERR;
$logPrefix = $name;
$logDestination = fopen('php://stderr', 'a');
break;
}
}
}
function logClose()
{
global $logType, $logDestination;
switch ($logType) {
case RM_LOG_SYSLOG:
closelog();
break;
case RM_LOG_FILE:
fclose($logDestination);
$logDestination = NULL;
break;
}
}
function myLog($text, $priority = RM_LOG_INFO)
{
global $params, $logLevelPrefixes, $logPrefix, $logType, $logDestination;
if ($params['log_level'] >= $priority) {
if (!empty($logLevelPrefixes[$priority])) {
$text = $logLevelPrefixes[$priority] . ": $text";
}
switch ($logType) {
case RM_LOG_SYSLOG:
syslog(RM_LOG_INFO, $text);
break;
case RM_LOG_FILE:
fwrite($logDestination, strftime('%B %d %T') . " ${logPrefix}[" . getmypid() . "]: $text\n");
fflush($logDestination);
break;
case RM_LOG_STDERR:
fwrite($logDestination, strftime('%B %d %T') . " ${logPrefix}[" . getmypid() . "]: $text\n");
fflush($logDestination);
break;
}
}
}
/** Helper function */
function assembleUri($parsed)
{
if (!is_array($parsed)) return false;
$uri = empty($parsed['scheme']) ? '' :
$parsed['scheme'] . ':' . ((strtolower($parsed['scheme']) == 'mailto') ? '' : '//');
$uri .= empty($parsed['user']) ? '' :
($parsed['user']) .
(empty($parsed['pass']) ? '' : ':'.($parsed['pass']))
. '@';
$uri .= empty($parsed['host']) ? '' :
$parsed['host'];
$uri .= empty($parsed['port']) ? '' :
':' . $parsed['port'];
$uri .= empty($parsed['path']) ? '' :
$parsed['path'];
$uri .= empty($parsed['query']) ? '' :
'?' . $parsed['query'];
$uri .= empty($parsed['anchor']) ? '' :
'#' . $parsed['anchor'];
return $uri;
}
function removePassword( $url ) {
$parsed = parse_url($url);
if( !empty($parsed['pass']) ) $parsed['pass'] = 'XXX';
return assembleUri($parsed);
}
?>
--- NEW FILE: recurrence.class.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
/**
* Class for recurring event calculation
*
* Usage: Subclass and implement setBusy(...),
* Instantiate, call setter methods to fill
* in data, then call expand() to expand the
* recurrence. This will cause setBusy() to be
* called for each busy period
*/
/*abstract*/ class Recurrence {
/* public: */
function Recurrence() {}
function setStartDate( $ts ) { $this->initial_start = $ts; }
function setEndDate( $ts ) { $this->initial_end = $ts; }
function setCycletype( $ctype ) { $this->cycle_type = $ctype; }
function setType( $type ) { $this->type = $type; }
function setInterval( $interval ) { $this->interval = $interval; }
function setDaynumber( $num ) { $this->daynumber = is_array($num)?$num:array($num); }
function setDay( $day ) { $this->day = is_array($day)?$day:array($day); }
function setMonth( $month ) { $this->month = is_array($month)?$month:array($month); }
function setExclusionDates( $ex ) {
// Prescale dates for easy comparison
$this->exclusion_dates = array();
foreach( $ex as $e ) {
$e = explode('-',$e);
$e = intval(sprintf('%04d%02d%02d', $e[0],$e[1],$e[2]));
$this->exclusion_dates[] = $e;
}
}
function setRangetype( $type ) { $this->range_type = $type; }
function setRange( $range ) { $this->range = $range; }
function ts2string($ts) {
return gmdate("D, M d Y H:i:s",$ts).'Z';
}
function expand( $startstamp, $endstamp ) {
myLog( 'Recurrence::expand( '.Recurrence::ts2string($startstamp).", "
.Recurrence::ts2string($endstamp).' ), cycletype='.$this->cycle_type,
RM_LOG_DEBUG);
switch( $this->cycle_type ) {
case 'daily':
$this->expand_daily( $startstamp, $endstamp );
break;
case 'weekly':
$this->expand_weekly( $startstamp, $endstamp );
break;
case 'monthly':
$this->expand_monthly( $startstamp, $endstamp );
break;
case 'yearly':
$this->expand_yearly( $startstamp, $endstamp );
break;
default:
myLog('Unknown cycletype '.$this->cycle_type, RM_LOG_ERROR);
}
}
/* Abstract function, please override */
function setBusy( $start, $end, $duration ) {
mylog( "Warning: Abstract method Recurrence::setBusy( $start, $end, $duration ) called", RM_LOG_ERROR);
}
/* private: */
function expand_daily( $startstamp, $endstamp ) {
// Daily recurrence, every 'interval' days
$duration = $this->initial_end-$this->initial_start;
$count = 0;
if( !$this->interval ) $this->interval = 1;
for( $t = $this->initial_start; $t < $endstamp; $t = strtotime( '+'.$this->interval.' days',$t) ) {
//myLog("Adding recurrence $t -> ".($t+$duration), RM_LOG_DEBUG );
if( !$this->isExcluded( $t, $t+$duration) ) $this->setBusy( $t, null, $duration );
$count++;
if( $this->range_type == 'number' && $count > $this->range ) {
break;
} else if( $this->range_type == 'date' && $t > strtotime( '+1 day',$this->range ) ) {
break;
}
}
}
function expand_weekly( $startstamp, $endstamp ) {
// Weekly recurrence, every 'interval' weeks on 'day' days
$duration = $this->initial_end-$this->initial_start;
if( !$this->interval ) $this->interval = 1;
$count = 0;
$delta_days = (int)gmdate('w',$this->initial_start);
for( $t = strtotime("-$delta_days days", $this->initial_start); $t < $endstamp;
$t = strtotime( '+'.$this->interval.' weeks', $t) ) {
//myLog("t=".gmdate("D, M d Y H:i:s",$t), RM_LOG_DEBUG);
foreach( $this->day as $day ) {
$tmp = strtotime( '+'.$this->dayname2number($day).' days', $t);
if( $tmp >= $this->initial_start && $tmp < $endstamp &&
!$this->isExcluded( $tmp, $tmp+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp)." -> "
.gmdate("D, M d Y H:i:s",$tmp+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp, null, $duration );
} else {
break;
}
}
$count++;
if( $this->range_type == 'number' && $count > $this->range ) {
break;
} else if( $this->range_type == 'date' && $t > strtotime( '+1 day',$this->range ) ) {
break;
}
}
}
function expand_monthly( $startstamp, $endstamp ) {
// Weekly recurrence, every 'interval' weeks
$duration = $this->initial_end-$this->initial_start;
if( !$this->interval ) $this->interval = 1;
$count = 0;
$delta_days = (int)gmdate('d',$this->initial_start);
myLog("initial_start=".Recurrence::ts2string($this->initial_start), RM_LOG_DEBUG);
$offset = 0;
$first_of_month = gmdate("M 1 Y H:i:s+0000", $this->initial_start);
for( $t = strtotime( $first_of_month );
$t < $endstamp; $t = strtotime( '+'.$this->interval.' months', $t)) {
if( $this->type == 'daynumber') {
// On numbered days
myLog('t = '.gmdate('M d Y H:i:s',$t), RM_LOG_DEBUG);
foreach( $this->daynumber as $dayno ) {
$t_month = gmdate('m',strtotime("-$offset days", $t));
$tmp = strtotime( '+'.($dayno-$offset-1).' days', $t);
myLog('tmp = '.gmdate('M d Y H:i:s',$tmp), RM_LOG_DEBUG);
$tmp_month = gmdate('m',$tmp); // make sure same month
if( $tmp >= $this->initial_start && $tmp < $endstamp &&
$t_month == $tmp_month &&
!$this->isExcluded( $tmp, $tmp+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp)." -> "
.gmdate("D, M d Y H:i:s",$tmp+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp, null, $duration );
} else {
break;
}
}
$count++;
} else if( $this->type == 'weekday' ) {
// On named weekdays
// Find beginning of first week
$tmp = strtotime("-$offset days",$t);
$firstday = (int)gmdate('w',$tmp);
for( $i = 0; $i < count($this->day); $i++ ) {
$dayno = $this->daynumber[$i];
$wday = $this->dayname2number($this->day[$i]);
$tmp_month = gmdate('m',$tmp); // make sure same month
if( $wday < $firstday ) $tmp2 = strtotime( '+'.($dayno*7+$wday-$firstday).' days', $tmp);
else $tmp2 = strtotime( '+'.(($dayno-1)*7+$wday-$firstday).' days', $tmp);
$tmp2_month = gmdate('m',$tmp2); // make sure same month
if( $tmp_month == $tmp2_month && $tmp2 >= $this->initial_start && $tmp2 < $endstamp
&& !$this->isExcluded( $tmp2, $tmp2+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp2)." -> "
.gmdate("D, M d Y H:i:s",$tmp2+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp2, null, $duration );
} else if($tmp2 >= $endstamp ) {
break;
}
}
$count++;
}
if( $this->range_type == 'number' && $count > $this->range ) {
break;
} else if( $this->range_type == 'date'
&& strtotime('-$offset days',$t) > strtotime( '+1 day',$this->range ) ) {
break;
}
}
}
function expand_yearly( $startstamp, $endstamp ) {
$duration = $this->initial_end-$this->initial_start;
if( !$this->interval ) $this->interval = 1;
$count = 0;
$delta_days = (int)gmdate('z',$this->initial_start);
for( $t = strtotime( "-$delta_days days", $this->initial_start ); $t < $endstamp;
$t = strtotime( '+'.$this->interval.' years', $t) ) {
//myLog("t= ".gmdate("M d Y H:i:s",$t), RM_LOG_DEBUG );
if( $this->type == 'yearday') {
foreach( $this->daynumber as $dayno ) {
$tmp = strtotime( '+'.($dayno-1).' days', $t);
if( $this->initial_start <= $tmp && $tmp < $endstamp
&& !$this->isExcluded( $tmp, $tmp+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp)." -> "
.gmdate("D, M d Y H:i:s",$tmp+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp, null, $duration );
} else if($tmp >= $endstamp ) {
break;
}
}
$count++;
} else if( $this->type == 'monthday' ) {
for( $i = 0; $i < count($this->daynumber); $i++ ) {
$dayno = $this->daynumber[$i];
$month = $this->monthname2number($this->month[$i]);
$year = gmdate('Y', $t );
$time = gmdate('H:i:s', $t);
//myLog("setting tmp to $year-$month-$dayno $time+0000", RM_LOG_DEBUG );
$tmp = strtotime( "$year-$month-$dayno $time+0000");
//myLog("tmp= ".gmdate("M d Y H:i:s",$tmp), RM_LOG_DEBUG );
if( $this->initial_start <= $tmp && $tmp < $endstamp
&& !$this->isExcluded( $tmp, $tmp+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp)." -> "
.gmdate("D, M d Y H:i:s",$tmp+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp, null, $duration );
} else {
break;
}
}
$count++;
} else if( $this->type == 'weekday' ) {
for( $i = 0; $i < count($this->daynumber); $i++ ) {
$dayno = $this->daynumber[$i];
$wday = $this->day[$i];
$month = $this->month[$i];
$year = gmdate('Y',$t);
$time = gmdate('H:i:s',$t);
$tmp = strtotime( "$dayno $wday", strtotime( "1 $month $year") );
$tmp = strtotime( gmdate('Y-m-d',$tmp)." $time+0000");
if( $this->initial_start <= $tmp && $tmp < $endstamp
&& !$this->isExcluded( $tmp, $tmp+$duration) ) {
myLog("Adding recurrence ".gmdate("D, M d Y H:i:s",$tmp)." -> "
.gmdate("D, M d Y H:i:s",$tmp+$duration), RM_LOG_DEBUG );
$this->setBusy( $tmp, null, $duration );
} else {
break;
}
}
$count++;
}
if( $this->range_type == 'number' && $count > $this->range ) {
break;
} else if( $this->range_type == 'date' && $t > strtotime( '+1 day',$this->range ) ) {
break;
}
}
}
function dayname2number( $day ) {
switch( strtolower($day) ) {
case 'sunday': return 0;
case 'monday': return 1;
case 'tuesday': return 2;
case 'wednesday': return 3;
case 'thursday': return 4;
case 'friday': return 5;
case 'saturday': return 6;
default:
myLog("Recurrence::dayname2number($day): Invalid day", RM_LOG_ERROR);
return -1;
}
}
function monthname2number( $month ) {
switch( $month ) {
case 'january': return 1;
case 'february': return 2;
case 'march': return 3;
case 'april': return 4;
case 'may': return 5;
case 'june': return 6;
case 'july': return 7;
case 'august': return 8;
case 'september':return 9;
case 'october': return 10;
case 'november': return 11;
case 'december': return 12;
default:
myLog("Recurrence::monthname2number($month): Invalid month", RM_LOG_ERROR);
return -1;
}
}
function isExcluded( $start, $end ) {
$start = explode(' ', gmdate('Y m d', $start));
$start = intval($start[0].$start[1].$start[2]);
$end = explode(' ', gmdate('Y m d', $end));
$end = intval($end[0].$end[1].$end[2]);
foreach( $this->exclusion_dates as $e ) {
if( $start <= $e && $end >= $e ) {
myLog("$start-$end excluded!", RM_LOG_DEBUG);
return true;
}
}
return false;
}
var $initial_start = NULL; // timestamp
var $initial_end = NULL; // timestamp
var $cycle_type = NULL; // string { 'daily', 'weekly', 'monthly', 'yearly' }
var $type = NULL; // string { 'daynumber', 'weekday', 'monthday', 'yearday' }
var $interval = NULL; // int
var $daynumber = NULL; // array(int)
var $day = NULL; // array(string)
var $month = NULL; // array(int)
var $range_type = NULL; // string { 'number', 'date' }
var $range = NULL; // int or timestamp
var $exclusion_dates = array();
};
?>
More information about the commits
mailing list