martin: server/kolab-resource-handlers freebusy-imap-caching.patch, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Sun Jun 19 17:25:31 CEST 2005


Author: martin

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

Added Files:
	freebusy-imap-caching.patch 
Log Message:
Martin Konold: Post 2.0 code change! Basically this adds the the imap/uid mapping with a persistent cache for the result.

- add imapcache and imapuid parameters to function FreeBusyRecurrance()
- add cachedir parameter to function FreeBusy()
- change from PEAR/IMAP to c-client in imapConnect()
- change from PEAR/IMAP to c-client in imapDisconnect()
- change from PEAR/IMAP to c-client in imapOpenMailbox()
- change from PEAR/IMAP to c-client in imap_reopen()
- change from PEAR/IMAP to c-client in getACL()
- change from PEAR/IMAP to c-client in getRelevance()
- change from PEAR/IMAP to c-client in class FreeBusy
- add caching to class FreeBusy

Kudos to Thomas Jarosch for contributing the nice and clean implementation!




--- NEW FILE: freebusy-imap-caching.patch ---
diff -u -r -p --new-file freebusy.clean/freebusy.class.php freebusy/freebusy.class.php
--- freebusy.clean/freebusy.class.php	Wed Jan 26 05:49:33 2005
+++ freebusy/freebusy.class.php	Mon Jun 13 14:28:31 2005
@@ -1,8 +1,9 @@
 <?php
 /*
- *  Copyright (c) 2004 Klaraelvdalens Datakonsult AB
+ *  Copyright (c) 2004-2005 Klaraelvdalens Datakonsult AB, Intra2net AG
  *
  *    Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
+ *               Thomas Jarosch <thomas.jarosch at intra2net.com>
  *
  *  This  program is free  software; you can redistribute  it and/or
  *  modify it  under the terms of the GNU  General Public License as
@@ -18,75 +19,104 @@
  *  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 have a cron script which deletes
+	  unmodified pfbs/caches after 2 months.
+ */
+ 
+ 
+require_once 'freebusy/freebusycache.class.php';
+require_once 'freebusy/freebusyimapcache.class.php';
 require_once 'freebusy/recurrence.class.php';
 
 class FreeBusyRecurrence extends Recurrence {
-  function FreeBusyRecurrence( &$vfb, &$extra ) {
+  function FreeBusyRecurrence( &$imapcache, &$imapuid, &$vfb, &$extra ) {
+    $this->imapcache =& $imapcache;
+    $this->imapuid =& $imapuid;
     $this->vfb =& $vfb;
     $this->extra =& $extra;
   }
 
   function setBusy( $start, $end, $duration ) {
-    $this->vfb->addBusyPeriod('BUSY', $start, null, $duration, $this->extra);
+    $this->imapcache->add_imap2fb($this->imapuid, $start, null, $duration, $this->extra);
   }
 
+  var $imapcache;
   var $vfb;
   var $extra;
 };
 
 class FreeBusy {
-  function FreeBusy( $username, 
+  function FreeBusy( $cache_dir,
+		     $username, 
 		     $password, 
 		     $imaphost,
+                     $imapoptions,
 		     $fbfuture=60,
 		     $fbpast=0 ) {
+    $this->cache_dir = $cache_dir;
     $this->username = $username;
     $this->password = $password;
     $this->imaphost = $imaphost;
+    $this->imapoptions = $imapoptions;
     $this->fbfuture = $fbfuture;
     $this->fbpast   = $fbpast;
+    
+    $this->relevance = null;
   }
 
   function imapConnect() {
-    require_once('Net/IMAP.php');
-    $this->imap = &new Net_IMAP( $this->imaphost, $this->imapport );
-    #$this->imap->setDebug(true);
+    $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 $this->imap->disconnect();
-  }
-
-  function imapLogin() {
-    return $this->imap->login($this->username,$this->password, true, false);
+    return imap_close($this->imap);
   }
 
   function imapOpenMailbox($foldername = 'INBOX') {
     $this->foldername = $foldername;
-    $rc = $this->imap->selectMailbox( $foldername );
-    $a  = $this->imap->getAnnotation( '/vendor/kolab/folder-type', '*' );
+    
+    $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() {
-    return $this->imap->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() {
-    $val = $this->imap->getAnnotation( '/vendor/kolab/incidences-for', 'value.shared' );
-    if( PEAR::isError($val) || empty($val) ) {
+    // 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);
-      return 'admins';
+      $this->relevance = "admins";
     } else {
       myLog("/vendor/kolab/incidences-for = ".print_r($val,true)." for ".$this->foldername, RM_LOG_DEBUG);
-      return $val;
     }
+
+    return $this->relevance;
   }
 
   function &generateFreeBusy($startstamp = NULL, $endstamp = NULL ) {
-  
     require_once 'PEAR.php';
     require_once 'Horde/iCalendar.php';
     require_once 'Horde/MIME.php';
@@ -102,7 +132,7 @@ class FreeBusy {
       $startstamp = strtotime( '-'.$this->fbpast.' days', mktime(0, 0, 0, $month, $day, $year) );
     }
   
-    // Default the end date to the start date + freebusy_days.
+    // Default the end date to the start date + fbfuture.
     if (is_null($endstamp) || $endstamp < $startstamp) {
       $endstamp = strtotime( '+'.$this->fbfuture.' days', $startstamp );
     }
@@ -124,7 +154,9 @@ class FreeBusy {
     // URL is not required, so out it goes...
     //$vFb->setAttribute('URL', 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
   
-    if( $this->imap->getNumberOfMessages() == 0 ) {
+    $status = imap_status_current($this->imap, SA_MESSAGES | SA_UIDVALIDITY | SA_UIDNEXT);
+
+    if( $status->messages == 0 ) {
       $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');
@@ -137,13 +169,50 @@ class FreeBusy {
       $vCal->addComponent($vFb);
       return array($vCal->exportvCalendar(),$vCal->exportvCalendar());
     }
-    $messages = $this->imap->getMessages();
-    if( PEAR::isError( $messages ) ) return array( $messages, null);
-    foreach ($messages as $textmsg) {
+    
+    // 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->username, $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.
+    return array( $vCal->exportvCalendar(), $xvCal->exportvCalendar() );
+  }
+
+  /********************** 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') {
@@ -159,23 +228,16 @@ class FreeBusy {
     
       if ($event === false) {
 	myLog("No x-vnd.kolab.events at all ", RM_LOG_DEBUG);
-	continue;
+	return;
       }
-      
+    
       $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;
+	return;
       }
-    
+      
       $summary = ($event['sensitivity'] == 'public' ? $event['summary'] : '');
     
       //myLog("Looking at message with uid=$uid and summary=$summary", RM_LOG_DEBUG);
@@ -194,11 +256,11 @@ class FreeBusy {
       if( !empty($event['scheduling-id']) ) {
 	$extra['X-SID'] = base64_encode($event['scheduling-id']);
       }
-
+      
       if( array_key_exists( 'recurrence', $event ) ) {	
 	myLog("Detected recurring event $uid", RM_LOG_DEBUG);
 	$rec = $event['recurrence'];
-	$recurrence =& new FreeBusyRecurrence( $vFb, $extra );
+	$recurrence =& new FreeBusyRecurrence( $imapcache, $imapuid, $vFb, $extra );
 	$recurrence->setStartDate( $initial_start );
 	$recurrence->setEndDate( $initial_end );
 	$recurrence->setCycletype( $rec['cycle'] );
@@ -224,24 +286,14 @@ class FreeBusy {
     
 	// Don't bother adding the initial event if it's outside our free/busy window
 	if ($initial_start < $startstamp || $initial_end > $endstamp) {
-	  continue;
+	  return;
 	}
-		
-	$vFb->addBusyPeriod('BUSY', $initial_start/* + FreeBusy::tzOffset($initial_start)*/, 
-			    $initial_end/* + FreeBusy::tzOffset($initial_end)*/, null, $extra);
+        
+        // $initial_start/* + FreeBusy::tzOffset($initial_start)
+        $imapcache->add_imap2fb($imapuid, $initial_start, $initial_end, null, $extra);
       }
-    }
-  
-    $xvCal = $vCal;
-    $xvCal->addComponent($vFb);
-    $vCal->addComponent($this->clearExtra($vFb));
-  
-    // Generate the vCal file.
-    return array( $vCal->exportvCalendar(), $xvCal->exportvCalendar() );
   }
-
-  /********************** Private API below this line ********************/
-
+  
   function tzOffset( $ts ) {
     $dstr = date('O',$ts);
     return 3600 * substr( $dstr, 0, 3) + 60 * substr( $dstr, 3, 2);
@@ -401,11 +453,14 @@ class FreeBusy {
     return $vFb;
   }
  
+  var $cache_dir;
   var $username;
   var $password;
   var $imaphost;
   var $imapport = 143;
+  var $imapoptions;
   var $foldername;
+  var $relevance;
 
   // Settings
   var $fbfuture;
@@ -414,6 +469,7 @@ class FreeBusy {
   var $week_starts_on_sunday = false;
 
   var $imap;
+  var $imap_serverstring;
 };
 
-?>
\ No newline at end of file
+?>
diff -u -r -p --new-file freebusy.clean/freebusy.conf freebusy/freebusy.conf
--- freebusy.clean/freebusy.conf	Mon May 30 17:13:44 2005
+++ freebusy/freebusy.conf	Mon Jun  6 15:32:40 2005
@@ -51,3 +51,20 @@ $params['send_content_length'] = false;
 // Should we send a Content-Disposition header, indicating what the name of the
 // resulting VFB file should be?
 $params['send_content_disposition'] = false;
+
+// Logging
+$params['log'] = "syslog:";
+$params['log_level'] = 2;
+
+// IMAP options passed to imap_open
+$params['imap_options'] = "/notls/secure/readonly";
+
+// Directory prefixes
+$params['kolab_prefix'] = "";
+$params['cache_dir'] = "/datastore/freebusy";		// default: /var/kolab/www/freebusy/cache
+
+$params['pfb_dbformat'] = "";		// default: gdbm
+
+// don't change this if you don't have to
+$params['ldap_uri'] = "";
+$params['ldap_classname_suffix'] = "_dummy";
diff -u -r -p --new-file freebusy.clean/freebusy.php freebusy/freebusy.php
--- freebusy.clean/freebusy.php	Thu Dec 16 21:53:55 2004
+++ freebusy/freebusy.php	Mon Jun  6 13:11:12 2005
@@ -1,8 +1,9 @@
 <?php
 /*
- *  Copyright (c) 2004 Klaraelvdalens Datakonsult AB
+ *  Copyright (c) 2004-2005 Klaraelvdalens Datakonsult AB, Intra2net AG
  *
  *    Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
+ *               Thomas Jarosch <thomas.jarosch at intra2net.com>
  *
  *  This  program is free  software; you can redistribute  it and/or
  *  modify it  under the terms of the GNU  General Public License as
@@ -18,13 +19,13 @@
  *  Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
  */
 
+require_once('freebusy.conf');
+
 require_once('freebusy/freebusycache.class.php');
 require_once('freebusy/freebusycollector.class.php');
-require_once('freebusy/freebusyldap.class.php');
+require_once('freebusy/freebusyldap'.$params['ldap_classname_suffix'].'.class.php');
 require_once('freebusy/misc.php');
 
-require_once('@l_prefix@/etc/resmgr/freebusy.conf');
-
 logInit( 'freebusy' );
 
 $user = trim($_REQUEST['uid']);
@@ -32,6 +33,14 @@ $imapuser     = $_SERVER['PHP_AUTH_USER'
 $imappw       = $_SERVER['PHP_AUTH_PW'];
 $req_cache    = (bool)$_REQUEST['cache'];
 $req_extended = (bool)$_REQUEST['extended'];
+/*
+// Debug test values
+$user = "groupware";
+$imapuser = "groupware";
+$imappw = "groupware";
+$req_cache = 0;
+$req_extended = 0;
+*/
 
 myLog("---FreeBusy Script starting (".$_SERVER['REQUEST_URI'].")---", RM_LOG_DEBUG );
 myLog("user=$user, imapuser=$imapuser, req_cache=$req_cache, req_extended=$req_extended", RM_LOG_DEBUG );
@@ -66,8 +75,8 @@ if( $homeserver != $params['server'] ) {
   exit;
 }
 
-
-$cache =& new FreeBusyCache( $params['kolab_prefix'].'/var/kolab/www/freebusy/cache', $req_extended );
+$full_cache_dir = $params['kolab_prefix'].$params['cache_dir'];
+$cache =& new FreeBusyCache( $full_cache_dir, $params['pfb_dbformat'], $req_extended );
 $collector =& new FreeBusyCollector( $user );
 
 $groups = $ldap->distlists( $ldap->dn( $user ) );
@@ -126,4 +135,4 @@ if ($params['send_content_disposition'])
 }
 
 echo $vfb;
-?>
\ No newline at end of file
+?>
diff -u -r -p --new-file freebusy.clean/freebusycache.class.php freebusy/freebusycache.class.php
--- freebusy.clean/freebusycache.class.php	Tue Feb 22 17:38:28 2005
+++ freebusy/freebusycache.class.php	Tue Jun 14 13:49:57 2005
@@ -1,8 +1,9 @@
 <?php
 /*
- *  Copyright (c) 2004 Klaraelvdalens Datakonsult AB
+ *  Copyright (c) 2004-2005 Klaraelvdalens Datakonsult AB, Intra2net AG
  *
  *    Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
+ *               Thomas Jarosch <thomas.jarosch at intra2net.com>
  *
  *  This  program is free  software; you can redistribute  it and/or
  *  modify it  under the terms of the GNU  General Public License as
@@ -21,17 +22,13 @@
 /*! To load/store partial freebusy lists
     and their ACLs */
 class FreeBusyCache {
-  function FreeBusyCache( $basedir, $extended = false ) {
+  function FreeBusyCache( $basedir, $dbformat, $extended = false ) {
     $this->basedir = $basedir;
+    $this->dbformat = $dbformat;
     $this->extended = $extended;
   }
 
   function store( $filename, $fbdata, $acl, $relevance ) {
-    if( ereg( '\.\.', $filename ) ) {
-      $this->error = $filename._(' is not absolute');
-      return false;
-    }
-
     $fbfilename = $this->mkfbfilename($filename);
     myLog("FreeBusyCache::store( file=$fbfilename, acl=[ "
 	  .str_replace("\n",", ",$this->aclToString($acl))
@@ -40,7 +37,7 @@ class FreeBusyCache {
       // false data means delete the pfb
       unlink($fbfilename);
       $oldacl = $this->loadACL( $filename );
-      $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', 'gdbm' );
+      $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->dbformat );
       if( $db === false ) return false;
       foreach( $oldacl as $ac ) {
 	  if( dba_exists( $ac['USER'], $db ) ) {
@@ -87,7 +84,7 @@ class FreeBusyCache {
       default: $perm = 'a';
       }
 
-      $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', 'gdbm' );
+      $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->dbformat );
       if( $db === false ) {
 	myLog('Unable to open freebusy cache db '.$this->basedir.'/pfbcache.db',
 	      RM_LOG_ERROR );
@@ -139,7 +136,7 @@ class FreeBusyCache {
     $fbfilename = $this->mkfbfilename($filename);
     unlink($fbfilename);
     unlink($this->mkaclfilename($filename));    
-    $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', 'gdbm' );
+    $db = dba_open( $this->basedir.'/pfbcache.db', 'cd', $this->dbformat );
     if( $db === false ) return false;
     for( $uid = dba_firstkey($db); $uid !== false; $uid = dba_nextkey($db)) {
       $lst = dba_fetch( $uid, $db );
@@ -153,7 +150,7 @@ class FreeBusyCache {
 
   function findAll( $uid, $groups ) {
     $lst = array();
-    $db = dba_open( $this->basedir.'/pfbcache.db', 'rd', 'gdbm' );
+    $db = dba_open( $this->basedir.'/pfbcache.db', 'rd', $this->dbformat );
     if( $db === false ) return false;
     $uids = $groups;
     for( $i = 0; $i < count($uids); $i++ ) $uids[$i] = 'group:'.$uids[$i];
@@ -169,14 +166,17 @@ class FreeBusyCache {
     }
     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;
     }
@@ -185,13 +185,13 @@ class FreeBusyCache {
   }
 
   function mkfbfilename( $fbfilename ) {
-    $fbfilename = str_replace( '..', '', $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( '.', '^', $fbfilename );
     $fbfilename = str_replace( "\0", '', $fbfilename );    
     return $this->basedir.'/'.$fbfilename.($this->extended?'.xpfb':'.pfb').'.acl';
   }
@@ -311,6 +311,7 @@ class FreeBusyCache {
   }
 
   var $basedir;
+  var $dbformat;
   var $error;
 };
 
diff -u -r -p --new-file freebusy.clean/freebusyimapcache.class.php freebusy/freebusyimapcache.class.php
--- freebusy.clean/freebusyimapcache.class.php	Thu Jan  1 01:00:00 1970
+++ freebusy/freebusyimapcache.class.php	Tue Jun 14 13:49:26 2005
@@ -0,0 +1,229 @@
+<?php
+/*
+ *  Copyright (c) 2005 Intra2net AG
+ *
+ *    Written by Thomas Jarosch <thomas.jarosch at intra2net.com>
+ *
+ *  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);
+
+        $full_path = $this->store_prefix.$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;
+    }
+};
+ 
+?>
diff -u -r -p --new-file freebusy.clean/freebusyldap_dummy.class.php freebusy/freebusyldap_dummy.class.php
--- freebusy.clean/freebusyldap_dummy.class.php	Thu Jan  1 01:00:00 1970
+++ freebusy/freebusyldap_dummy.class.php	Thu Jun  2 15:33:35 2005
@@ -0,0 +1,70 @@
+<?php
+/*
+ *  Copyright (c) 2005 Intra2net AG
+ *
+ *    Written by Thomas Jarosch <thomas.jarosch at intra2net.com>
+ *
+ *  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();
+  }
+};
+
+?>
diff -u -r -p --new-file freebusy.clean/pfb.php freebusy/pfb.php
--- freebusy.clean/pfb.php	Tue Feb 22 17:57:15 2005
+++ freebusy/pfb.php	Mon Jun 13 17:18:51 2005
@@ -1,8 +1,9 @@
 <?php
 /*
- *  Copyright (c) 2004 Klaraelvdalens Datakonsult AB
+ *  Copyright (c) 2004-2005 Klaraelvdalens Datakonsult AB, Intra2net AG
  *
  *    Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
+ *               Thomas Jarosch <thomas.jarosch at intra2net.com>
  *
  *  This  program is free  software; you can redistribute  it and/or
  *  modify it  under the terms of the GNU  General Public License as
@@ -20,12 +21,12 @@
 
 error_reporting(E_ALL);
 
-require_once('freebusy/freebusyldap.class.php');
+require_once('freebusy/freebusy.conf');
+
+require_once('freebusy/freebusyldap'.$params['ldap_classname_suffix'].'.class.php');
 require_once('freebusy/freebusycache.class.php');
 require_once('freebusy/misc.php');
 
-require_once('@l_prefix@/etc/resmgr/freebusy.conf');
-
 logInit('pfb');
 
 $imapuser     = isset($_SERVER['PHP_AUTH_USER'])?$_SERVER['PHP_AUTH_USER']:false;
@@ -33,7 +34,18 @@ $imappw       = isset($_SERVER['PHP_AUTH
 $req_cache    = isset($_REQUEST['cache'])?(bool)$_REQUEST['cache']:false;
 $req_folder   = isset($_REQUEST['folder'])?$_REQUEST['folder']:false;
 $req_extended = isset($_REQUEST['extended'])?(bool)$_REQUEST['extended']:false;
-
+// convert character encoding (stores utf7 folder names also on disc)
+require_once "Horde/Util.php";
+require_once "Horde/String.php";
+$req_folder = String::convertCharset($req_folder, "UTF-8", "UTF7-IMAP");
+/*
+// Debug test values
+$imapuser = "groupware";
+$imappw = "groupware";
+$req_cache = 0;
+$req_folder = "groupware/Kalender";
+$req_extended = 0;
+*/
 myLog("pfb.php starting up: user=$imapuser, folder=$req_folder, extended=$req_extended", 
       RM_LOG_DEBUG);
 
@@ -49,7 +61,7 @@ if( $userinfo ) {
   //$homeserver = $userinfo['HOMESERVER'];  
 }
 
-$folder = array_values(array_filter(explode('/', $req_folder )));
+$folder = explode('/', $req_folder);
 if( count($folder) < 1 ) {
   // error
   notFound( _('No such folder ').htmlentities($req_folder) );
@@ -86,9 +98,11 @@ if( $homeserver && $homeserver != $param
   exit;
 }
 
-$cache =& new FreeBusyCache( $params['kolab_prefix'].'/var/kolab/www/freebusy/cache',
+$full_cache_dir = $params['kolab_prefix'] . $params['cache_dir'];
+
+$cache =& new FreeBusyCache( $full_cache_dir, $params['pfb_dbformat'],
 			     false );
-$xcache =& new FreeBusyCache( $params['kolab_prefix'].'/var/kolab/www/freebusy/cache',
+$xcache =& new FreeBusyCache( $full_cache_dir, $params['pfb_dbformat'],
 			     true );
 
 if( $req_cache ) {
@@ -128,23 +142,17 @@ if( $req_cache ) {
   unset($folder[0]);
   $folder = join('/', $folder);
   $fbpast = $ldap->freeBusyPast();
-  $fb =& new FreeBusy( $imapuser, $imappw, 'localhost', $uinfo['FBFUTURE'], $fbpast );
-  $fb->freebusy_days = $params['freebusy_days'];
+  $fb =& new FreeBusy( $full_cache_dir, $imapuser, $imappw, 'localhost', $params['imap_options'], $uinfo['FBFUTURE'], $fbpast );
   $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());
+  if( $rc === false ) {
+    unauthorized(imap_last_error());
     return false;
   }
   $rc = $fb->imapOpenMailbox(FreeBusy::imapFolderName( $imapuser, $owner, 
 						       $folder, $params['email_domain']));
-  if( PEAR::isError( $rc ) ) {
-    notfound( "Folder: ".$fb->foldername.', '.$rc->toString());
+  if( $rc === false ) {
+    notfound( "Folder: ".$fb->foldername.', '.imap_last_error());
     return false;
   }
   $relevance = $fb->getRelevance();
@@ -156,6 +164,7 @@ if( $req_cache ) {
   }
 
   $acl = $fb->getACL();
+  
   if( !$cache->store( $owner.'/'.$folder, $vfb, $acl, $relevance ) ) {
     trigger_error('Could not store pfb in cache file '.$owner.'/'.$folder
 		  .'.pfb: '.$cache->error, E_USER_WARNING);





More information about the commits mailing list