steffen: server/kolab-resource-handlers/kolab-resource-handlers/freebusy freebusy.class.php, NONE, 1.1 freebusycache.class.php, NONE, 1.1 freebusycollector.class.php, NONE, 1.1 freebusyldap.class.php, NONE, 1.1 misc.php, NONE, 1.1 pfb.php, NONE, 1.1 freebusy.php, 1.26, 1.27

cvs at intevation.de cvs at intevation.de
Tue Sep 28 15:12:28 CEST 2004


Author: steffen

Update of /kolabrepository/server/kolab-resource-handlers/kolab-resource-handlers/freebusy
In directory doto:/tmp/cvs-serv1384/kolab-resource-handlers/freebusy

Modified Files:
	freebusy.php 
Added Files:
	freebusy.class.php freebusycache.class.php 
	freebusycollector.class.php freebusyldap.class.php misc.php 
	pfb.php 
Log Message:
new freebusy handling

--- NEW FILE: freebusy.class.php ---
<?php

class FreeBusy {
  function FreeBusy( $username, 
		     $password, 
		     $imaphost ) {
    $this->username = $username;
    $this->password = $password;
    $this->imaphost = $imaphost;
  }

  function imapConnect() {
    require_once('Net/IMAP.php');
    $this->imap = &new Net_IMAP( $this->imaphost, $this->imapport );
    #$this->imap->setDebug(true);
    return $this->imap;
  }

  function imapDisconnect() {
    return $this->imap->disconnect();
  }

  function imapLogin() {
    return $this->imap->login($this->username,$this->password, true, false);
  }

  function imapOpenMailbox($foldername = 'INBOX') {
    $this->foldername = $foldername;
    return $this->imap->selectMailbox( $foldername );
  }

  function &generateFreeBusy($extended = false, $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 = mktime(0, 0, 0, $month, $day, $year);
    }
  
    // Default the end date to the start date + freebusy_days.
    if (is_null($endstamp) || $endstamp < $startstamp) {
      $month = date('n', $startstamp);
      $year = date('Y', $startstamp);
      $day = date('j', $startstamp);
    
      $endstamp = mktime(0, 0, 0, $month, $day + $this->freebusy_days, $year);
    }
  
    // 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);
    $vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
  
    $messages = $this->imap->getMessages();
    if( PEAR::isError( $messages ) ) return $messages;
    foreach ($messages as $textmsg) {
      $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);
	
	  $event = $this->getEventHash($part->getContents());
	  if ($event === false) {
	    continue;
	  }
	}
      }
    
      if ($event === false) {
	continue;
      }
      
      /*
      $uid = $event['uid'];
      // See if we need to ignore this event
      if (isset($params['ignore'][$uid])) {
	trigger_error("Ignoring event with uid=$uid", E_USER_NOTICE);
	continue;
      }
      */

      if( array_key_exists( 'show-time-as', $event ) && strtolower(trim($event['show-time-as'])) == 'free' ) {
	continue;
      }
    
      $summary = ($event['sensitivity'] == 'public' ? $event['summary'] : '');
    
      //trigger_error("Looking at message with uid=$uid and summary=$summary", E_USER_NOTICE);
    
      // Get the events initial start
      $initial_start = $event['start-date'];
      $initial_end = $event['end-date'];
    
      // Don't bother adding the initial event if it's outside our free/busy window
      if ($initial_start < $startstamp || $initial_end > $endstamp) {
	continue;
      }
    
      if ($extended ) {
	$extra = array(
		       'X-UID'     => base64_encode($uid),
		       );
	if (!empty($summary)) {
	  $extra['X-SUMMARY'] = base64_encode($summary);
	}
      
	$vFb->addBusyPeriod('BUSY', $initial_start, $initial_end, null, $extra);
      } else {
	$vFb->addBusyPeriod('BUSY', $initial_start, $initial_end);
      }
    }
  
    $vCal->addComponent($vFb);
  
    // Generate the vCal file.
    return $vCal->exportvCalendar();
  }

  /********************** Private API below this line ********************/

  // 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, curtesy 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
      return false;
    }
    
    $noderoot = $xmldoc->document_element();
    $childnodes = $noderoot->child_nodes();
    
    $event_hash = array();
    
    // Build the event hash
    foreach ($childnodes as $value) {
      $event_hash[$value->tagname] = $value->get_content();
    }
    
    // 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
    $event_hash['start-date'] = FreeBusy::parseDateTime($event_hash['start-date']);
    $event_hash['end-date'] = FreeBusy::parseDateTime($event_hash['end-date']);
    
    return $event_hash;
  }

 
  var $username;
  var $password;
  var $imaphost;
  var $imapport = 143;
  var $foldername;

  // Settings
  var $freebusy_days = 60;
  var $default_domain = 'foo';

  var $imap;
};

?>
--- NEW FILE: freebusycache.class.php ---
<?php

class FreeBusyCache {
  function FreeBusyCache( $basedir ) {
    $this->basedir = $basedir;
  }

  function store( $fbfilename, $fbdata ) {
    $tmpn = tempnam($this->basedir, 'fb');
    $tmpf = fopen($tmpn, 'w');
    if( !$tmpf ) return false;
    fwrite($tmpf, $fbdata);
    if( ereg( '\.\.', $fbfilename ) ) {
      $this->error = $fbfilename._(' is not absolute');
      return false;
    }
    $fbfilename = $this->basedir.'/'.$fbfilename;
    $fbdirname  = dirname( $fbfilename );
    if (!is_dir($fbdirname)) {
      if( !$this->mkdirhier($fbdirname) ) {
	$this->error = _("Error creating dir $fbdirname");
	return false;
      }
    }
    if( !rename($tmpn, $fbfilename) ) {
      $this->error = _("Error renaming $tmpn to $fbfilename");
      return false;
    }
    fclose($tmpf);
    return true;
  }

  function load( $fbfilename, &$ts ) {
    $fbfilename = $this->basedir.'/'.$fbfilename;
    if( $fbfilename != realpath( $fbfilename )) return false;    
    if( file_exists($fbfilename) ) {
      if( !is_null($ts)) $ts = filectime($fbfilename);
      return file_get_contents($fbfilename);
    }
    return false;
  }

  function findAll( $user ) {
    $fbdir = $this->basedir.'/'.$user;
    if( $fbdir != realpath($fbdir) ) {
      $this->error = $fbdir._(' is not absolute');      
      return false;
    }
    return FreeBusyCache::recursivedir( $fbdir );
  }

  /*************** Private API below this line *************/
  function mkdirhier( $dirname ) {
    $base = substr($dirname,0,strrpos($dirname,'/'));
    if( !empty( $base ) && !is_dir( $base ) ) {
      if( !$this->mkdirhier( $base ) ) return false;
    }
    if( !file_exists( $dirname ) ) return mkdir( $dirname, 0755 );
    return true;
  }

  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;
	$tmp = FreeBusyCache::recursivedir( $dir.'/'.$file );
	if( $tmp !== false ) $dirs = array_merge( $dirs, $tmp );
      } else if( is_file($dir.'/'.$file) ) {
	$dirs[] = $dir.'/'.$file;
      }
    }
    closedir( $dh );
    return $dirs;
  }

  var $basedir;
  var $error;
};

?>
--- NEW FILE: freebusycollector.class.php ---
<?php

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 false;
    }
    $vFb1 = &$this->vCal->findComponent( 'vfreebusy' );
    $vFb2 = &$vCal->findComponent( 'vfreebusy' );
    if( !$vFb2 ) {
      trigger_error("Could not find freebusy info in ical", E_USER_ERROR);      
      return false;
    }
    if( ($sts = $this->vCal->getAttributeDefault('DTSTART', false)) === false ) {
      $this->vCal->setAttribute('DTSTART', $vCal->getAttribute('DTSTART'), array(), false );
    } else {
      $this->vCal->setAttribute('DTSTART', max( $sts, $vCal->getAttribute('DTSTART') ), array(), false );
    }
    if( ($sts = $this->vCal->getAttributeDefault('DTEND', false)) === false ) {
      $this->vCal->setAttribute('DTEND', $vCal->getAttribute('DTEND'), array(), false );
    } else {
      $this->vCal->setAttribute('DTEND', min( $sts, $vCal->getAttribute('DTEND')), array(), false );
    }
    return $vFb1->merge( $vFb2 );
  }

  function exportvCalendar() {
    return $this->vCal->exportvCalendar();
  }

  function collect( $user, &$cache ) {
    
  }

  var $organizer;
  var $vCal;
};

?>
--- NEW FILE: freebusyldap.class.php ---
<?php

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 mailForUid( $uid ) {
    $result = ldap_search( $this->connection, $this->base, '(&(objectClass=kolabInetOrgPerson)(uid='.$uid.'))',
			   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 $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;
  }


  var $connection;
  var $is_bound;
  var $uri;
  var $base;
};

?>
--- NEW FILE: misc.php ---
<?php
function shutdown() {
    global $fb, $ldap;

    if (defined($fb) && $fb !== false) {
        $fb->disConnect();
        $fb = false;
    }
    if (defined($ldap) && $ldap !== false) {
        $ldap->close();
        $ldap = false;
    }
}

function serverError($errortext) {
  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) {
  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;
    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;
}
?>
--- NEW FILE: pfb.php ---
<?php
error_reporting(E_ALL);

require_once('freebusy/freebusyldap.class.php');
require_once('freebusy/freebusycache.class.php');
require_once('freebusy/misc.php');
require_once('@l_prefix@/etc/resmgr/freebusy.conf');

$imapuser     = $_SERVER['PHP_AUTH_USER'];
$imappw       = $_SERVER['PHP_AUTH_PW'];
$req_cache    = $_REQUEST['cache'];
$req_folder   = $_REQUEST['folder'];
$req_extended = $_REQUEST['extended'];

$ldap =& new FreeBusyLDAP( $params['ldap_uri'], $params['base_dn'] );
if( !$ldap->bind( $params['bind_dn'], $params['bind_pw'] ) ) {
  notFound( "Bind failed: ".$ldap->error() );
  exit;
}

$imapuser = $ldap->mailForUid( $imapuser );
$homeserver = $ldap->homeServer( $imapuser );

if( $homeserver != $params['server'] ) {
  $redirect = 'https://'.$homeserver . $_SERVER['REQUEST_URI'];
  if ($params['redirect']) {
    header("Location: $redirect");
  } else {
    header("X-Redirect-To: $redirect");
    $redirect = 'https://' . urlencode($_SERVER['PHP_AUTH_USER']) . ':'
      . urlencode($_SERVER['PHP_AUTH_PW']) . '@' . $homeserver
      . $_SERVER['REQUEST_URI'];
    if (!@readfile($redirect)) {
      unauthorized("Unable to read free/busy information from $redirect");
    }
  }
  shutdown();
  exit;
}

$cache =& new FreeBusyCache( $params['kolab_prefix'].'/var/kolab/www/pfb/cache' );

if( $req_cache ) {
  $vfb = $cache->load( $req_folder.'.pfb', $ts );
  if( !$vfb ) notFound( $req_folder.'.pfb not found in cache');
} else {
  require_once('freebusy/freebusy.class.php');

  $folder = split('/', $req_folder );
  if( count($folder) < 2 ) {
    // error
    notFound( 'No such folder ').htmlentities($req_folder);
  }
  $owner = $folder[0];
  $mbox = $folder[1];
  unset($folder[0]);
  unset($folder[1]);
  $folder = join('/', $folder);
  if( strtolower( $owner ) != strtolower( $imapuser ) ) {
    unauthorized(_("User $imapuser is not allowed to create freebusy data for $owner"));
    return false;    
  }
  $fb =& new FreeBusy( $imapuser, $imappw, 'localhost' );
  $fb->freebusy_days = $params['freebusy_days'];
  $fb->default_domain = $params['email_domain'];
  $rc = $fb->imapConnect();
  if( PEAR::isError( $rc ) ) {
    unauthorized($rc->toString());
    return false;
  }
  $rc = $fb->imapLogin();
  if( PEAR::isError( $rc ) ) {
    unauthorized("Access denied for user $imapuser: ".$rc->toString());
    return false;
  }
  $rc = $fb->imapOpenMailbox(FreeBusy::imapFolderName( $imapuser, $mbox, 
						       $folder, $params['email_domain']));
  if( PEAR::isError( $rc ) ) {
    notfound( "Folder: ".$fb->foldername.', '.$rc->toString());
    return false;
  }  
  $vfb = $fb->generateFreeBusy();
  $ts = mktime();
  if( PEAR::isError( $vfb ) ) {
    unauthorized($rc->toString());
    return false;
  }
  if( !$cache->store( $owner.'/'.$mbox.'/'.$folder.'.pfb', $vfb ) ) {
    trigger_error('Could not store vfb in cache file '.$owner.'/'.$mbox.'/'.$folder.'.pfb: '
		  .$cache->error, E_USER_WARNING);
  }
}

// And finally send it out, ensuring it doesn't get cached along the way
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate("D, d M Y H:i:s",$ts) . ' GMT');
header('Pragma: public');
header('Content-Transfer-Encoding: none');
if ($params['send_content_type']) {
    header('Content-Type: text/calendar');
}
if ($params['send_content_length']) {
    header('Content-Length: ' . strlen($vfb));
}
if ($params['send_content_disposition']) {
    header('Content-Disposition: attachment; filename="' . $user . '.vfb"');
}

echo $vfb;

// Finish up
shutdown();
?>
Index: freebusy.php
===================================================================
RCS file: /kolabrepository/server/kolab-resource-handlers/kolab-resource-handlers/freebusy/freebusy.php,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -d -r1.26 -r1.27
--- freebusy.php	21 Sep 2004 15:31:20 -0000	1.26
+++ freebusy.php	28 Sep 2004 13:12:26 -0000	1.27
@@ -1,557 +1,23 @@
 <?php
+require_once('freebusy/freebusycache.class.php');
+require_once('freebusy/freebusycollector.class.php');
+require_once('freebusy/misc.php');
+require_once('@l_prefix@/etc/resmgr/freebusy.conf');
 
-// Report all errors except E_NOTICE
-//error_reporting(E_ALL ^ E_NOTICE);
-error_reporting(E_ALL);
-
-$params = array();
-require_once '@l_prefix@/etc/resmgr/freebusy.conf';
-
-function shutdown()
-{
-    global $imap, $ldap;
-
-    if (defined($imap) && $imap !== false) {
-        @imap_close($imap);
-        $imap = false;
-    }
-
-    if (defined($ldap) && $ldap !== false) {
-        @ldap_close($ldap);
-        $ldap = false;
-    }
-}
-
-function notFound($errortext = '')
-{
-    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;
-    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;
-}
-
-function testIMAPError()
-{
-    $errors = imap_alerts();
-    $errortext = '';
-    if ($errors !== false && !empty($errors)) {
-        $errortext = 'IMAP Warning: ' . join("\nIMAP Warning: ", $errors) . "\n";
-    }
-
-    $errors = imap_errors();
-    if ($errors === false) {
-        return $errors;
-    }
-
-    $errortext .= 'IMAP Error: ' . join("\nIMAP Error: ", $errors);
-    trigger_error( $errortext, E_USER_WARNING );
-
-    $err = array_pop($errors);
-    if ($err == 'Permission denied' || $err == 'Invalid credentials' || $err == 'Login aborted') {
-        unauthorized($errortext);
-    }
-
-    notFound($errortext);
-}
-
-function testLDAPError()
-{
-    global $ldap;
-
-    $errno = ldap_errno($ldap);
-    if ($errno == 0) {
-        return false;
-    }
-
-    $error = ldap_err2str($errno);
-    trigger_error( "LDAP Error: $error", E_USER_WARNING );
-
-    notFound("LDAP Error $errno: $error");
-}
-
-// Date/Time value parsing, curtesy 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 = parseDate($text)) {
-            return $date;
-        }
-        return @gmmktime(0, 0, 0, $date['month'], $date['mday'], $date['year']);
-    }
-
-    if (!$date = parseDate($dateParts[0])) {
-        return $date;
-    }
-    if (!$time = 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
-        return false;
-    }
-
-    $noderoot = $xmldoc->document_element();
-    $childnodes = $noderoot->child_nodes();
-
-    $event_hash = array();
-
-    // Build the event hash
-    foreach ($childnodes as $value) {
-        $event_hash[$value->tagname] = $value->get_content();
-    }
-
-    // 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
-    $event_hash['start-date'] = parseDateTime($event_hash['start-date']);
-    $event_hash['end-date'] = parseDateTime($event_hash['end-date']);
-
-    return $event_hash;
-}
-
-function &generateFreeBusy($startstamp = NULL, $endstamp = NULL)
-{
-    global $params, $imap, $user, $messages;
-
-    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 = mktime(0, 0, 0, $month, $day, $year);
-    }
-
-    // Default the end date to the start date + freebusy_days.
-    if (is_null($endstamp) || $endstamp < $startstamp) {
-        $month = date('n', $startstamp);
-        $year = date('Y', $startstamp);
-        $day = date('j', $startstamp);
-
-        $endstamp = mktime(0, 0, 0, $month, $day + $params['freebusy_days'], $year);
-    }
-
-    // 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:' . $user);
-
-    $vFb->setAttribute('DTSTAMP', time());
-    $vFb->setAttribute('DTSTART', $startstamp);
-    $vFb->setAttribute('DTEND', $endstamp);
-    $vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
-
-    foreach ($messages as $msg) {
-        // Fetch the message
-        $textmsg = @imap_fetchheader($imap, $msg, FT_UID | FT_PREFETCHTEXT);
-        if (imap_last_error() == "Unexpected characters at end of address: <>") {
-            // We have a message with an empty address from OL,
-            // just ignore the error
-            imap_errors();
-        }
-
-        $textmsg .= @imap_body($imap, $msg, FT_UID);
-        testIMAPError();
-
-        $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);
-
-                $event = getEventHash($part->getContents());
-                if ($event === false) {
-                    continue;
-                }
-            }
-        }
-
-        if ($event === false) {
-            continue;
-        }
-
-        $uid = $event['uid'];
-        // See if we need to ignore this event
-        if (isset($params['ignore'][$uid])) {
-            trigger_error("Ignoring event with uid=$uid", E_USER_NOTICE);
-            continue;
-        }
-	if( strtolower(trim($event['show-time-as'])) == 'free' ) {
-	    continue;
-	}
-
-        $summary = ($event['sensitivity'] == 'public' ? $event['summary'] : '');
-
-        trigger_error("Looking at message with uid=$uid and summary=$summary", E_USER_NOTICE);
-
-        // Get the events initial start
-        $initial_start = $event['start-date'];
-        $initial_end = $event['end-date'];
-
-        // Don't bother adding the initial event if it's outside our free/busy window
-        if ($initial_start < $startstamp || $initial_end > $endstamp) {
-            continue;
-        }
-
-        if ($params['extended']) {
-            $extra = array(
-                'X-UID'     => base64_encode($uid),
-            );
-            if (!empty($summary)) {
-                $extra['X-SUMMARY'] = base64_encode($summary);
-            }
-
-            $vFb->addBusyPeriod('BUSY', $initial_start, $initial_end, null, $extra);
-        } else {
-            $vFb->addBusyPeriod('BUSY', $initial_start, $initial_end);
-        }
-    }
-
-    $vCal->addComponent($vFb);
-
-    // Generate the vCal file.
-    return $vCal->exportvCalendar();
-}
-
-function storeFreeBusy($fbdirname, $fbfilename ,$fbdata)
-{
-    $tmpn = tempnam($fbdirname, 'fb');
-    $tmpf = fopen($tmpn, 'w');
-    fwrite($tmpf, $fbdata);
-    if (!is_dir($fbdirname)) {
-        mkdir($fbdirname);
-    }
-    rename($tmpn, $fbfilename);
-    fclose($tmpf);
-}
-
-function ldapConnect() {
-    global $params;
-    global $ldap;
-    if( $ldap ) return $ldap;
-    $ldap = @ldap_connect($params['ldap_uri']);
-    if ($ldap === false) {
-        notFound('No LDAP URI speficied');
-    }
-
-    @ldap_bind($ldap, $params['bind_dn'],$params['bind_pw']);
-    testLDAPError();
-    return $ldap;
-}
-
-// ========================================================================== //
-
-// What is the name of our script
-$scriptname = basename($_SERVER['SCRIPT_NAME']);
-
-// Should we return extended information?
-if (isset($_GET['x'])) {
-    $params['extended'] = !empty($_GET['x']);
-}
-
-// Should we use the caching mechanism?
-if (isset($_GET['c'])) {
-    $params['cache'] = !empty($_GET['c']);
-}
-
-// See what UIDs we should ignore when generating the free/busy list
-$params['ignore'] = empty($_GET['i']) ? array() : $_GET['i'];
-if (!is_array($params['ignore'])) {
-    $params['ignore'] = array($params['ignore']);
-}
-$_temp = $params['ignore'];
-$params['ignore'] = array();
-foreach ($_temp as $uid) {
-    $params['ignore'][urldecode($uid)] = 1;
-}
-
-// Turn off caching if we have to ignore some events, as otherwise we'll be
-// returning incorrect information
-if (!empty($params['ignore'])) {
-    $params['cache'] = false;
-}
-
-// Determine who we're supposed to get f/b info for
-//$user = preg_replace('/\.vfb$/i', '', basename($_SERVER['PATH_INFO']));
-$user = strtolower(preg_replace( "'/'", '', urldecode($_GET['uid'])));
-
-$ldap = ldapConnect();
-
-if (empty($user) || $user == $scriptname) {
-    notFound('No user specified');
-}
-
-$fbdir = $params['kolab_prefix'].'/var/kolab/www/freebusy/data/';
-$ifbfilename = $fbdir.$user.'.ifb';
-$xfbfilename = $fbdir.$user.'.xfb';
-$fbfilename = ($params['extended'] ? $xfbfilename : $ifbfilename);
-
-// Handle the case of 'user' instead of 'user at domain'
-if (strstr($user, '@') === false) {
-    $user .= '@' . $params['email_domain'];
-}
-
-if ($params['multi_location']) {
-    // Handle multi-location setups
-
-    // TODO: read in a 'fb_url' attribute, if one exists, and use that instead
-    // This will allow users to explicitly override where their free/busy info
-    // is read from.
-
-    $ldapsearch = @ldap_search($ldap, $params['base_dn'], "(mail=$user)", array($params['home_server']));
-    testLDAPError();
-
-    $ldapresults = @ldap_get_entries($ldap, $ldapsearch);
-    testLDAPError();
-
-    /*
-    if ($ldapresults['count'] == 0) {
-        notFound("$user does not exist in the LDAP hierarchy");
-    } else if ($ldapresults['count'] > 1) {
-        notFound("More than one entry for $user were found in the LDAP hierarchy");
-    }
-    */
-    
-    // We only handle multi-domain situations when we receive exactly one result
-    // back from LDAP; otherwise we fall through and let the script carry on as
-    // usual.
-    if ($ldapresults['count'] == 1) {
-        if (array_key_exists(strtolower($params['home_server']), $ldapresults[0])) {
-            $homeServer = $ldapresults[0][strtolower($params['home_server'])][0];
-        } else {
-            $homeServer = $params['server'];
-        }
-    
-        // Redirect if the user is on a different server
-        if ($homeServer != $params['server']) {
-            $redirect = 'https://'.$homeServer . $_SERVER['REQUEST_URI'];
-            if ($params['redirect']) {
-                header("Location: $redirect");
-            } else {
-                header("X-Redirect-To: $redirect");
-                $redirect = 'https://' . urlencode($_SERVER['PHP_AUTH_USER']) . ':'
-                    . urlencode($_SERVER['PHP_AUTH_PW']) . '@' . $homeServer
-                    . $_SERVER['REQUEST_URI'];
-                if (!@readfile($redirect)) {
-                    notFound("Unable to read free/busy information from $redirect");
-                }
-            }
-            shutdown();
-            exit;
-        }
-    }
-}
-
-// Handle virtual domains
-$prefix = $user;
-$suffix = '';
-if ($params['virtual_domains']) {
-    list($prefix, $suffix) = split('@', $user);
-    if ($params['append_domains'] && !empty($suffix)) {
-        $suffix = '@' . $suffix;
-    } else {
-        $suffix = '';
-    }
-}
-
-// Get our mailbox strings for use in the imap_X functions
-$server = '{' . $params['server'] . ':143/imap/notls/novalidate-cert}';
-$mailbox = "user/$prefix/" . $params['calendar_store'] . "$suffix";
-$fullmbox = $server . $mailbox;
-
-$imapuser = $_SERVER['PHP_AUTH_USER'];
-$ldapsearch = @ldap_search($ldap, $params['base_dn'], "(uid=$imapuser)");
-testLDAPError();
-$ldapresults = @ldap_get_entries($ldap, $ldapsearch);
-testLDAPError();
-if (is_array($ldapresults) && $ldapresults['count'] > 0 && array_key_exists('mail', $ldapresults[0])) {
-    $imapuser = $ldapresults[0]['mail'][0];
-}
-$imappw = $_SERVER['PHP_AUTH_PW'];
-
-if ($_SERVER['REQUEST_METHOD'] == 'PUT') {
-    // We have a file upload, slurp up the data
-    $vfbupload = file_get_contents('php://input');
-
-    trigger_error("PUT user=$user, imapuser=$imapuser", E_USER_NOTICE);
-
-    if ($imapuser == $user) {
-        // Username OK, Apache should have taken care of authentication
-        storeFreeBusy($fbdir, $fbfilename, $vfbupload);
-        exit;
-    } else {
-        unauthorized("Unable to store uploaded free/busy file");
-    }
-}
-
-trigger_error("user=$imapuser, mailbox=$fullmbox, fbfilename=$fbfilename", E_USER_NOTICE);
-
-$vfb = false;
-if ($_SERVER['REQUEST_METHOD'] != 'PUT' && ($imapuser || $params['extended'])) {
-    // Open an IMAP connection to the requested users' calendar
-    $imap = @imap_open($fullmbox, $imapuser, $imappw, OP_HALFOPEN );
-    if (!$imap ) {
-        // Login error, check the cache
-        // Dont bail out on error here,
-        // Just proceed to the cache
-    } else {
-        testIMAPError();
-        trigger_error("Trying to reopen mailbox $fullmbox", E_USER_NOTICE);
-        @imap_reopen($imap, $fullmbox);
-        if (imap_last_error()) {
-            // Dont bail out on error here,
-            // Just proceed to the cache
-            trigger_error("IMAP Error trying to open $fullmbox: " . imap_last_error(), E_USER_WARNING);
-        } else {
-            // Enumerate our calendar events
-            $messages = @imap_sort($imap, SORTDATE, 0, SE_UID);
-            trigger_error('Calendar folder contains ' . count($messages) . ' messages', E_USER_NOTICE);
-            testIMAPError();
+$user = trim($_REQUEST['uid']);
+$cache =& new FreeBusyCache( $params['kolab_prefix'].'/var/kolab/www/pfb/cache' );
+$collector =& new FreeBusyCollector( $user );
 
-            // Generate the VFB file
-            $vfb = &generateFreeBusy();
-            $ts = mktime();
-            // Cache the generated free/busy file
-            if ($vfb && $params['cache'] && !$params['extended']) {
-                storeFreeBusy($fbdir, $fbfilename, $vfb);
-            }
-        }
-    }
+$pfbs = $cache->findAll( $user );
+$ts = 0;
+if( $pfbs === false ) {
+  notFound($pfb->error);
 }
-
-if (!$vfb) {
-    if (!$params['cache'] || (!file_exists($fbfilename) && $imapuser == $user)) {
-        notFound("Unable to generate free/busy list");
-        exit;
-    }
-
-    $vfb = file_get_contents($fbfilename);
-    $ts = filectime($fbfilename);
-    if (!$vfb && $params['extended']) {
-        // Check if we have a plain fb list
-        $vfb = file_get_contents($ifbfilename);
-        $ts = filectime($ifbfilename);
-    }
-    if (!$vfb) notFound("Unable to locate cached free/busy list");
+foreach( $pfbs as $pfb ) {
+  $ts = max( $ts, filectime( $pfb ) );
+  $collector->addFreebusy( file_get_contents( $pfb ) );
 }
+$vfb = $collector->exportvCalendar();
 
 // And finally send it out, ensuring it doesn't get cached along the way
 header('Cache-Control: no-store, no-cache, must-revalidate');
@@ -568,11 +34,8 @@
     header('Content-Length: ' . strlen($vfb));
 }
 if ($params['send_content_disposition']) {
-    header('Content-Disposition: attachment; filename="' . $user . '.vfb"');
+    header('Content-Disposition: attachment; filename="' . $user . '.ifb"');
 }
 
 echo $vfb;
-
-// Finish up
-shutdown();
 ?>





More information about the commits mailing list