Index: contrib/davical/htdocs/caldav.php
===================================================================
--- contrib/davical/htdocs/caldav.php	(revision 3733)
+++ contrib/davical/htdocs/caldav.php	(revision 3733)
@@ -0,0 +1,132 @@
+<?php
+/**
+* CalDAV Server - main program
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+if ( isset($_SERVER['PATH_INFO']) && preg_match( '{^(/favicon.ico|davical.css|(images|js|css)/.+)$}', $_SERVER['PATH_INFO'], $matches ) ) {
+  $filename = $_SERVER['DOCUMENT_ROOT'] . preg_replace('{(\.\.|\\\\)}', '', $matches[1]);
+  $fh = @fopen($matches[1],'r');
+  if ( ! $fh ) {
+    @header( sprintf("HTTP/1.1 %d %s", 404, 'Not found') );
+  }
+  else {
+    fpassthru($fh);
+  }
+  exit(0);
+}
+elseif ( isset($_SERVER['PATH_INFO']) && preg_match( '{^/\.well-known/(.+)$}', $_SERVER['PATH_INFO'], $matches ) ) {
+	require ('well-known.php');
+  exit(0);
+}
+require_once('./always.php');
+// dbg_error_log( 'caldav', ' User agent: %s', ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Unfortunately Mulberry does not send a "User-agent" header with its requests :-(')) );
+// dbg_log_array( 'headers', '_SERVER', $_SERVER, true );
+require_once('HTTPAuthSession.php');
+$session = new HTTPAuthSession();
+
+function send_dav_header() {
+  global $c;
+
+  /**
+  * access-control is rfc3744, we do most of it, but no way to say that.
+  * calendar-schedule is another one we do most of, but the spec is not final yet either.
+  */
+  if ( isset($c->override_dav_header) ) {
+    $dav = $c->override_dav_header;
+  }
+  else {
+    /** hack to get around bugzilla #463392 - remove sometime after 2011-02-28 */
+    if ( isset($_SERVER['HTTP_USER_AGENT']) && preg_match( '{ Gecko/(20[01]\d[01]\d[0123]\d)(\d+)? }', $_SERVER['HTTP_USER_AGENT'], $matches ) && $matches[1] < 20100520 ) {
+      $dav = '1, 2, 3, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy, bind, addressbook';
+    }
+    else {
+       // We don't actually do calendar-auto-schedule yet - when we do we should add it on here.
+      $dav = '1, 2, 3, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy, bind, addressbook';
+    }
+  }
+  $dav = explode( "\n", wordwrap( $dav ) );
+  foreach( $dav AS $v ) {
+    header( 'DAV: '.trim($v, ', '), false);
+  }
+}
+send_dav_header();  // Avoid polluting global namespace
+
+require_once('CalDAVRequest.php');
+$request = new CalDAVRequest();
+
+$allowed = implode( ', ', array_keys($request->supported_methods) );
+// header( 'Allow: '.$allowed);
+
+if ( ! ($request->IsPrincipal() || isset($request->collection) || $request->method == 'PUT' || $request->method == 'MKCALENDAR' || $request->method == 'MKCOL' ) ) {
+  if ( preg_match( '#^/principals/users(/.*/)$#', $request->path, $matches ) ) {
+    // Although this doesn't work with the iPhone, perhaps it will with iCal...
+    /** @TODO: integrate handling this URL into CalDAVRequest.php */
+    $redirect_url = ConstructURL('/caldav.php'.$matches[1]);
+    dbg_error_log( 'LOG WARNING', 'Redirecting %s for "%s" to "%s"', $request->method, $request->path, $redirect_url );
+    header('Location: '.$redirect_url );
+    exit(0);
+  }
+}
+
+dbg_error_log( 'LOG WARNING', 'Redirecting %s for "%s" to "%s"', $request->method, $request->path, $request->IsAddressBook() );
+
+switch ( $request->method ) {
+  case 'OPTIONS':    include_once('caldav-OPTIONS.php');   break;
+  case 'REPORT':     include_once('caldav-REPORT.php');    break;
+  case 'PROPFIND':   include('caldav-PROPFIND.php');       break;
+  case 'GET':     
+             if ( $request->IsAddressBook())
+                    { 
+                     include('caldav-GET-addressbook.php');         
+                     break;
+                    }
+              else {
+                     include('caldav-GET.php');         
+                     break;
+                   }
+  case 'POST':       include('caldav-POST.php');           break;
+  case 'HEAD':       include('caldav-GET.php');            break;
+  case 'PROPPATCH':  include('caldav-PROPPATCH.php');      break;
+  case 'PUT':
+    $request->CoerceContentType();
+    switch( $request->content_type ) {
+      case 'text/calendar':
+        /** use original DAViCal 'PUT' code which will be rewritten */
+        include('caldav-PUT.php');
+        break;
+      case 'text/vcard':
+      case 'text/x-vcard':
+        dbg_error_log( 'LOG WARNING', 'FLAAAAAAAAAAAAAAAAAA X-VCARD');
+        include('caldav-PUT-vcard.php');
+        break;
+      default:
+        include('caldav-PUT-default.php');
+        break;
+    }
+    break;
+  case 'MKCALENDAR': include('caldav-MKCOL.php');          break;
+  case 'MKCOL':      include('caldav-MKCOL.php');          break;
+  case 'DELETE':     include('caldav-DELETE.php');         break;
+  case 'MOVE':       include('caldav-MOVE.php');           break;
+  case 'ACL':        include('caldav-ACL.php');            break;
+  case 'LOCK':       include('caldav-LOCK.php');           break;
+  case 'UNLOCK':     include('caldav-LOCK.php');           break;
+  case 'MKTICKET':   include('caldav-MKTICKET.php');       break;
+  case 'DELTICKET':  include('caldav-DELTICKET.php');      break;
+  case 'BIND':       include('caldav-BIND.php');           break;
+
+  case 'TESTRRULE':  include('test-RRULE-v2.php');         break;
+
+  default:
+    dbg_error_log( 'caldav', 'Unhandled request method >>%s<<', $request->method );
+    dbg_log_array( 'caldav', '_SERVER', $_SERVER, true );
+    dbg_error_log( 'caldav', 'RAW: %s', str_replace("\n", '',str_replace("\r", '', $request->raw_post)) );
+}
+
+$request->DoResponse( 500, translate('The application program does not understand that request.') );
+
Index: contrib/davical/htdocs/well-known.php
===================================================================
--- contrib/davical/htdocs/well-known.php	(revision 3733)
+++ contrib/davical/htdocs/well-known.php	(revision 3733)
@@ -0,0 +1,99 @@
+<?php
+
+require_once('./always.php');
+
+dbg_error_log( 'well-known', 'iSchedule requested' );
+
+require_once('HTTPAuthSession.php');
+//$c->allow_unauthenticated = true;
+$session = new HTTPAuthSession();
+
+if ( ! isset ( $request ) ) {
+ require_once('CalDAVRequest.php');
+ $request = new CalDAVRequest();
+}
+
+ dbg_log_array( 'well-known', 'SESSAO', $session, true );
+switch ( $request->path ) {
+  case '/.well-known/caldav':
+  case '/.well-known/carddav':
+     header('Location: ' . ConstructURL('/'.$session->username.'/addressbook/',true) );
+    exit(0);
+}
+
+
+
+if ( $c->enable_scheduling != true )
+{
+ $request->DoResponse( 404, translate('The application program does not understand that request.') );
+ exit ();
+}
+
+header ( 'iSchedule-Version: 1.0' );
+
+switch ( $request->method ) {
+  case 'GET':        ischedule_get();                      break;
+  case 'POST':       include('iSchedule.php');             break;
+
+  default:
+    dbg_error_log( 'well-known', 'Unhandled request method >>%s<<', $request->method );
+    dbg_log_array( 'well-known', '_SERVER', $_SERVER, true );
+    dbg_error_log( 'well-known', 'RAW: %s', str_replace("\n", '',str_replace("\r", '', $request->raw_post)) );
+}
+
+$request->DoResponse( 500, translate('The application program does not understand that request.') );
+
+
+
+
+
+function ischedule_get ( )
+{
+ global $request,$c;
+ if ( $request->path != '/.well-known/ischedule' || $_GET['query'] != 'capabilities' )
+ {
+  $request->DoResponse( 404, translate('The application program does not understand that request.' . $request->path ) );
+  return false;
+ }
+ header ( 'Content-Type: application/xml; charset=utf-8' );
+ echo '<?xml version="1.0" encoding="utf-8" ?>';
+ echo <<<RESPONSE
+  <query-result xmlns="urn:ietf:params:xml:ns:ischedule">
+    <capability-set>
+      <supported-version-set>
+        <version>1.0</version>
+      </supported-version-set>
+      <supported-scheduling-message-set>
+        <comp name="VEVENT">
+          <method name="REQUEST"/>
+          <method name="ADD"/>
+          <method name="REPLY"/>
+          <method name="CANCEL"/>
+        </comp>
+        <comp name="VTODO"/>
+        <comp name="VFREEBUSY"/>
+      </supported-scheduling-message-set>
+      <supported-calendar-data-type>
+        <calendar-data-type content-type="text/calendar" version="2.0"/>
+      </supported-calendar-data-type>
+      <supported-attachment-values>
+        <inline-attachment/>
+        <external-attachment/>
+      </supported-attachment-values>
+      <supported-recipient-uri-scheme-set>
+        <scheme>mailto</scheme>
+      </supported-recipient-uri-scheme-set>
+      <max-content-length>102400</max-content-length>
+      <min-date-time>19910101T000000Z</min-date-time>
+      <max-date-time>20381231T000000Z</max-date-time>
+      <max-instances>150</max-instances>
+      <max-recipients>250</max-recipients>
+
+RESPONSE;
+ echo '      <administrator>mailto:' . $c->admin_email . '</administrator>' . "\n";
+ echo <<<RESPONSE
+    </capability-set>
+ </query-result>
+RESPONSE;
+ exit ( 0 );
+}
Index: contrib/davical/htdocs/always.php
===================================================================
--- contrib/davical/htdocs/always.php	(revision 3733)
+++ contrib/davical/htdocs/always.php	(revision 3733)
@@ -0,0 +1,549 @@
+<?php
+/**
+* @package davical
+* @author Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+if ( preg_match('{/always.php$}', $_SERVER['SCRIPT_NAME'] ) ) header('Location: index.php');
+
+// Ensure the configuration starts out as an empty object.
+$c = (object) array();
+$c->script_start_time = microtime(true);
+
+// Ditto for a few other global things
+unset($session); unset($request); unset($dbconn); unset($_awl_dbconn); unset($include_properties);
+
+// An ultra-simple exception handler to catch errors that occur
+// before we get a more functional exception handler in place...
+function early_exception_handler($e) {
+  echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
+
+  $trace = array_reverse($e->getTrace());
+  foreach( $trace AS $k => $v ) {
+    printf( "=====================================================\n%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
+  }
+}
+set_exception_handler('early_exception_handler');
+
+// Default some of the configurable values
+$c->sysabbr     = 'davical';
+$c->admin_email = 'admin@davical.example.com';
+$c->system_name = 'DAViCal CalDAV Server';
+$c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+$c->save_time_zone_defs = true;
+$c->collections_always_exist = false;
+$c->allow_get_email_visibility = false;
+$c->permission_scan_depth = 2;
+$c->expand_pdo_parameters = true;
+$c->home_calendar_name = 'home';
+$c->enable_row_linking = true;
+$c->enable_scheduling = false;
+$c->http_auth_mode = 'Basic';
+// $c->default_locale = array('es_MX', 'es_AR', 'es', 'pt');  // An array of locales to try, or just a single locale
+// $c->local_tzid = 'Pacific/Auckland';  // Perhaps we should read from /etc/timezone - I wonder how standard that is?
+$c->default_locale = 'en';
+$c->locale_path = '../locale';
+$c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
+$c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
+$c->default_privileges = array('read-free-busy', 'schedule-deliver');
+
+$c->stylesheets = array( $c->base_url.'/davical.css' );
+$c->images      = $c->base_url . '/images';
+
+// Add a default for newly created users
+$c->template_usr = array( 'active' => true,
+                          'locale' => 'en_GB',
+                          'date_format_type' => 'E',
+                          'email_ok' => date('Y-m-d')
+                        );
+
+$c->hide_TODO = true;                      // VTODO only visible to collection owner
+$c->readonly_webdav_collections = true;    // WebDAV access is readonly
+
+// Kind of private configuration values
+$c->total_query_time = 0;
+
+$c->dbg = array();
+
+
+// Utilities
+if ( ! @include_once('AWLUtilities.php') ) {
+  $try_paths = array(
+        '../../awl/inc'
+      , '/usr/share/awl/inc'
+      , '/usr/local/share/awl/inc'
+  );
+  foreach( $try_paths AS $awl_include_path ) {
+    if ( @file_exists($awl_include_path.'/AWLUtilities.php') ) {
+      set_include_path( $awl_include_path. PATH_SEPARATOR. get_include_path());
+      break;
+    }
+  }
+  if ( ! @include_once('AWLUtilities.php') ) {
+    echo "Could not find the AWL libraries. Are they installed? Check your include_path in php.ini!\n";
+    exit;
+  }
+}
+
+// Ensure that ../inc is in our included paths as early as possible
+set_include_path( '../inc'. PATH_SEPARATOR. get_include_path());
+
+
+/** We actually discovered this and worked around it earlier, but we can't log it until the utilties are loaded */
+if ( !isset($_SERVER['SERVER_NAME']) ) {
+  @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration.  Using IP address meanhwhile..." );
+}
+
+/**
+* Calculate the simplest form of reference to this page, excluding the PATH_INFO following the script name.
+*/
+$c->protocol_server_port = sprintf( '%s://%s%s',
+                 (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'? 'https' : 'http'),
+                 $_SERVER['SERVER_NAME'],
+                 (
+                   ( (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on') && $_SERVER['SERVER_PORT'] == 80 )
+                           || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' && $_SERVER['SERVER_PORT'] == 443 )
+                   ? ''
+                   : ':'.$_SERVER['SERVER_PORT']
+                 ) );
+$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
+
+
+/**
+* We use @file_exists because things like open_basedir might noisily deny
+* access which could break DAViCal completely by causing output to start
+* too early.
+*/
+ob_start( );
+if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+  include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+}
+else if ( @file_exists('/etc/davical/config.php') ) {
+  include('/etc/davical/config.php');
+}
+else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+  include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+}
+else if ( @file_exists('/usr/local/etc/davical/config.php') ) {
+  include('/usr/local/etc/davical/config.php');
+}
+else if ( @file_exists('../config/config.php') ) {
+  include('../config/config.php');
+}
+else if ( @file_exists('config/config.php') ) {
+  include('config/config.php');
+}
+else {
+  include('davical_configuration_missing.php');
+  exit;
+}
+$config_warnings = trim(ob_get_contents());
+ob_end_clean();
+
+if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
+
+if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) {
+  @dbg_error_log( 'LOG', '==========> Test case =%s=', $_SERVER['HTTP_X_DAVICAL_TESTCASE'] );
+}
+else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
+  // Only log this if more than a little debugging of some sort is turned on, somewhere
+  @dbg_error_log( 'LOG', '==========> method =%s= =%s= =%s= =%s= =%s=',
+         $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
+}
+
+/**
+* Now that we have loaded the configuration file we can switch to a
+* default site locale.  This may be overridden by each user.
+*/
+putenv("LANG=". $c->default_locale);
+awl_set_locale($c->default_locale);
+init_gettext( 'davical', $c->locale_path );
+
+/**
+* Work out our version
+*
+*/
+$c->code_version = 0;
+$c->version_string = '0.9.9.2'; // The actual version # is replaced into that during the build /release process
+if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
+  $c->code_major = $matches[1];
+  $c->code_minor = $matches[2];
+  $c->code_patch = $matches[3];
+  $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
+  dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
+  header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
+}
+
+/**
+* Force the domain name to what was in the configuration file
+*/
+$_SERVER['SERVER_NAME'] = $c->domain_name;
+
+require_once('AwlQuery.php');
+
+$c->want_dbversion = array(1,2,9);
+$c->schema_version = 0;
+$qry = new AwlQuery( 'SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1;' );
+if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
+  $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
+  $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
+  $c->schema_major = $row->schema_major;
+  $c->schema_minor = $row->schema_minor;
+  $c->schema_patch = $row->schema_patch;
+  if ( $c->schema_version < $c->wanted_version ) {
+    $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
+             $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
+  }
+  if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) $qry->QDo('SET TIMEZONE TO \'Pacific/Auckland\'');
+}
+
+
+$_known_users_name = array();
+$_known_users_id   = array();
+$_known_users_pid  = array();
+
+function _davical_get_principal_query_cached( $where, $parameter ) {
+  global $c, $session, $_known_users_name, $_known_users_id, $_known_users_pid;
+
+  $sql = 'SELECT *, to_char(updated at time zone \'GMT\',\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS modified, principal.*, ';
+  if ( isset($session->principal_id) ) {
+    $sql .= 'pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) AS privileges ';
+    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+  }
+  else {
+    $sql .= '0::BIT(24) AS privileges ';
+    $params = array( );
+  }
+  $sql .= 'FROM usr LEFT JOIN principal USING(user_no) WHERE '. $where;
+  $params[':param'] = $parameter;
+
+  $qry = new AwlQuery( $sql, $params );
+  if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
+    if ( isset($session->principal_id) ) {
+      $_known_users_name[$row->username]      = $row;
+      $_known_users_id[$row->user_no]         = $row;
+      $_known_users_pid[$row->principal_id]   = $row;
+    }
+    return $row;
+  }
+
+  return false;
+}
+
+/**
+* Return a user record identified by a username, caching it for any subsequent lookup
+* @param string $username The username of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByName( $username, $use_cache = true ) {
+  global $_known_users_name;
+  settype($username,"string");                           
+  if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
+      
+  if (is_numeric($username))
+       return _davical_get_principal_query_cached( 'username = :param', $username );
+  else
+       return _davical_get_principal_query_cached( 'lower(username) = lower(:param)', $username );
+}
+
+
+/**
+* Return a user record identified by e-mail address, caching it for any subsequent lookup
+* @param string $email The email address of the user record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByEMail( $email, $use_cache = true ) {
+  global $_known_users_name;
+
+  if ( $use_cache ) {
+    /** We don't actually maintain a cache-access by e-mail, since it's rare */
+    foreach( $_known_users_name AS $k => $v ) {
+      if ( strtolower($email) == strtolower($v->email) ) return $v;
+    }
+  }
+  return _davical_get_principal_query_cached( 'email = :param', $email );
+}
+
+
+/**
+* Return a user record identified by a user_no, caching it for any subsequent lookup
+* @param int $user_no The ID of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByID( $user_no, $use_cache = true ) {
+  global $c, $session, $_known_users_id;
+
+  if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
+  return _davical_get_principal_query_cached( 'user_no = :param', $user_no );
+}
+
+
+/**
+* Return a user record identified by a user_no, caching it for any subsequent lookup
+* @param int $user_no The ID of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getPrincipalByID( $principal_id, $use_cache = true ) {
+  global $c, $session, $_known_users_pid;
+
+  if ( $use_cache && isset( $_known_users_pid[$principal_id] ) ) return $_known_users_pid[$principal_id];
+  return _davical_get_principal_query_cached( 'principal_id = :param', $principal_id );
+}
+
+
+/**
+ * Return the HTTP status code description for a given code. Hopefully
+ * this is an efficient way to code this.
+ * @return string The text for a give HTTP status code, in english
+ */
+function getStatusMessage($status) {
+  switch( $status ) {
+    case 100:  $ans = 'Continue';                             break;
+    case 101:  $ans = 'Switching Protocols';                  break;
+    case 200:  $ans = 'OK';                                   break;
+    case 201:  $ans = 'Created';                              break;
+    case 202:  $ans = 'Accepted';                             break;
+    case 203:  $ans = 'Non-Authoritative Information';        break;
+    case 204:  $ans = 'No Content';                           break;
+    case 205:  $ans = 'Reset Content';                        break;
+    case 206:  $ans = 'Partial Content';                      break;
+    case 207:  $ans = 'Multi-Status';                         break;
+    case 300:  $ans = 'Multiple Choices';                     break;
+    case 301:  $ans = 'Moved Permanently';                    break;
+    case 302:  $ans = 'Found';                                break;
+    case 303:  $ans = 'See Other';                            break;
+    case 304:  $ans = 'Not Modified';                         break;
+    case 305:  $ans = 'Use Proxy';                            break;
+    case 307:  $ans = 'Temporary Redirect';                   break;
+    case 400:  $ans = 'Bad Request';                          break;
+    case 401:  $ans = 'Unauthorized';                         break;
+    case 402:  $ans = 'Payment Required';                     break;
+    case 403:  $ans = 'Forbidden';                            break;
+    case 404:  $ans = 'Not Found';                            break;
+    case 405:  $ans = 'Method Not Allowed';                   break;
+    case 406:  $ans = 'Not Acceptable';                       break;
+    case 407:  $ans = 'Proxy Authentication Required';        break;
+    case 408:  $ans = 'Request Timeout';                      break;
+    case 409:  $ans = 'Conflict';                             break;
+    case 410:  $ans = 'Gone';                                 break;
+    case 411:  $ans = 'Length Required';                      break;
+    case 412:  $ans = 'Precondition Failed';                  break;
+    case 413:  $ans = 'Request Entity Too Large';             break;
+    case 414:  $ans = 'Request-URI Too Long';                 break;
+    case 415:  $ans = 'Unsupported Media Type';               break;
+    case 416:  $ans = 'Requested Range Not Satisfiable';      break;
+    case 417:  $ans = 'Expectation Failed';                   break;
+    case 422:  $ans = 'Unprocessable Entity';                 break;
+    case 423:  $ans = 'Locked';                               break;
+    case 424:  $ans = 'Failed Dependency';                    break;
+    case 500:  $ans = 'Internal Server Error';                break;
+    case 501:  $ans = 'Not Implemented';                      break;
+    case 502:  $ans = 'Bad Gateway';                          break;
+    case 503:  $ans = 'Service Unavailable';                  break;
+    case 504:  $ans = 'Gateway Timeout';                      break;
+    case 505:  $ans = 'HTTP Version Not Supported';           break;
+    default:   $ans = 'Unknown HTTP Status Code '.$status;
+  }
+  return $ans;
+}
+
+
+/**
+* Construct a URL from the supplied dav_name.  The URL will be urlencoded,
+* except for any '/' characters in it.
+* @param string $partial_path  The part of the path after the script name
+*/
+function ConstructURL( $partial_path, $force_script = false ) {
+  global $c;
+
+  $partial_path = rawurlencode($partial_path);
+  $partial_path = str_replace( '%2F', '/', $partial_path);
+
+  if ( ! isset($c->_url_script_path) ) {
+    $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
+    $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
+  }
+
+  $url = $c->_url_script_path;
+  if ( $force_script ) {
+    if ( ! preg_match( '#/caldav\.php$#', $url ) ) $url .= '/caldav.php';
+  }
+  $url .= $partial_path;
+  $url = preg_replace( '#^(https?://.+)//#', '$1/', $url );  // Ensure we don't double any '/'
+  $url = preg_replace('#^https?://[^/]+#', '', $url );       // Remove any protocol + hostname portion
+
+  return $url;
+}
+
+
+/**
+* Deconstruct a dav_name from the supplied URL.  The dav_name will be urldecoded.
+*
+* @param string $partial_path  The part of the path after the script name
+*/
+function DeconstructURL( $url, $force_script = false ) {
+  global $c;
+
+  $dav_name = rawurldecode($url);
+
+  /** Allow a path like .../username/calendar.ics to translate into the calendar URL */
+  if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $dav_name, $matches ) ) {
+    $dav_name = $matches[1]. '/';
+  }
+
+  /** remove any leading protocol/server/port/prefix... */
+  if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
+  if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
+    if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
+      $dav_name = $matches[2];
+    }
+  }
+
+  /** strip doubled slashes */
+  if ( strstr($dav_name,'//') ) $dav_name = preg_replace( '#//+#', '/', $dav_name);
+
+  if ( substr($dav_name,0,1) != '/' ) $dav_name = '/'.$dav_name;
+
+  return $dav_name;
+}
+
+
+/**
+* Convert a date from ISO format into the sad old HTTP format.
+* @param string $isodate The date to convert
+*/
+function ISODateToHTTPDate( $isodate ) {
+  // Use strtotime since strptime is not available on Windows platform.
+  return( gmstrftime('%a, %d %b %Y %T GMT', strtotime($isodate)) );
+}
+
+/**
+* Convert a date into ISO format into the sparkly new ISO format.
+* @param string $indate The date to convert
+*/
+function DateToISODate( $indate ) {
+  // Use strtotime since strptime is not available on Windows platform.
+  return( date('c', strtotime($indate)) );
+}
+
+/**
+* Given a privilege string, or an array of privilege strings, return a bit mask
+* of the privileges.
+* @param mixed $raw_privs The string (or array of strings) of privilege names
+* @return integer A bit mask of the privileges.
+*/
+define("DAVICAL_MAXPRIV", "65535");
+define("DAVICAL_ADDRESSBOOK_MAXPRIV", "1023");
+function privilege_to_bits( $raw_privs ) {
+  $out_priv = 0;
+
+  if ( gettype($raw_privs) == 'string' ) $raw_privs = array( $raw_privs );
+
+  if ( ! is_array($raw_privs) ) $raw_privs = array($raw_privs);
+
+  foreach( $raw_privs AS $priv ) {
+    $trim_priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv)));
+    switch( $trim_priv ) {
+      case 'read'                            : $out_priv |=     1;  break;
+      case 'write-properties'                : $out_priv |=     2;  break;
+      case 'write-content'                   : $out_priv |=     4;  break;
+      case 'unlock'                          : $out_priv |=     8;  break;
+      case 'read-acl'                        : $out_priv |=    16;  break;
+      case 'read-current-user-privilege-set' : $out_priv |=    32;  break;
+      case 'bind'                            : $out_priv |=    64;  break;
+      case 'unbind'                          : $out_priv |=   128;  break;
+      case 'write-acl'                       : $out_priv |=   256;  break;
+      case 'read-free-busy'                  : $out_priv |=   512;  break;
+      case 'schedule-deliver-invite'         : $out_priv |=  1024;  break;
+      case 'schedule-deliver-reply'          : $out_priv |=  2048;  break;
+      case 'schedule-query-freebusy'         : $out_priv |=  4096;  break;
+      case 'schedule-send-invite'            : $out_priv |=  8192;  break;
+      case 'schedule-send-reply'             : $out_priv |= 16384;  break;
+      case 'schedule-send-freebusy'          : $out_priv |= 32768;  break;
+
+      /** Aggregates of Privileges */
+      case 'write'                           : $out_priv |=   198;  break; // 2 + 4 + 64 + 128
+      case 'schedule-deliver'                : $out_priv |=  7168;  break; // 1024 + 2048 + 4096
+      case 'schedule-send'                   : $out_priv |= 57344;  break; // 8192 + 16384 + 32768
+      case 'all'                             : $out_priv  = DAVICAL_MAXPRIV;  break;
+      default:
+        dbg_error_log( 'ERROR', 'Cannot convert privilege of "%s" into bits.', $priv );
+
+    }
+  }
+
+  // 'all' will include future privileges
+  if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
+  return $out_priv;
+}
+
+
+/**
+* Given a bit mask of the privileges, will return an array of the
+* text values of privileges.
+* @param integer $raw_bits A bit mask of the privileges.
+* @return mixed The string (or array of strings) of privilege names
+*/
+function bits_to_privilege( $raw_bits, $resourcetype = 'resource' ) {
+  $out_priv = array();
+
+  if ( is_string($raw_bits) ) {
+    $raw_bits = bindec($raw_bits);
+  }
+
+  if ( ($raw_bits & DAVICAL_MAXPRIV) == DAVICAL_MAXPRIV ) $out_priv[] = 'all';
+
+  if ( ($raw_bits &   1) != 0 ) $out_priv[] = 'DAV::read';
+  if ( ($raw_bits &   8) != 0 ) $out_priv[] = 'DAV::unlock';
+  if ( ($raw_bits &  16) != 0 ) $out_priv[] = 'DAV::read-acl';
+  if ( ($raw_bits &  32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
+  if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
+  if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
+
+  if ( ($raw_bits & 198) != 0 ) {
+    if ( ($raw_bits & 198) == 198 ) $out_priv[] = 'DAV::write';
+    if ( ($raw_bits &   2) != 0 ) $out_priv[] = 'DAV::write-properties';
+    if ( ($raw_bits &   4) != 0 ) $out_priv[] = 'DAV::write-content';
+    if ( ($raw_bits &  64) != 0 ) $out_priv[] = 'DAV::bind';
+    if ( ($raw_bits & 128) != 0 ) $out_priv[] = 'DAV::unbind';
+  }
+
+  if ( $resourcetype == 'schedule-inbox' && ($raw_bits & 7168) != 0 ) {
+    if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
+    if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
+    if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
+    if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
+  }
+
+  if ( $resourcetype == 'schedule-outbox' && ($raw_bits & 57344) != 0 ) {
+    if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
+    if ( ($raw_bits &  8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
+    if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
+    if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
+  }
+
+//  dbg_error_log( 'DAVResource', ' Privilege bit "%s" is "%s".', $raw_bits, implode(', ', $out_priv) );
+
+  return $out_priv;
+}
+
+
+/**
+* Returns the array of privilege names converted into XMLElements
+*/
+function privileges_to_XML( $privilege_names, &$xmldoc=null ) {
+  if ( !isset($xmldoc) && isset($GLOBALS['reply']) ) $xmldoc = $GLOBALS['reply'];
+  $privileges = array();
+  foreach( $privilege_names AS $k ) {
+    $privilege = new XMLElement('privilege');
+    if ( isset($xmldoc) )
+      $xmldoc->NSElement($privilege,$k);
+    else
+      $privilege->NewElement($k);
+    $privileges[] = $privilege;
+  }
+  return $privileges;
+}
+
Index: contrib/davical/htdocs/setup.php
===================================================================
--- contrib/davical/htdocs/setup.php	(revision 3733)
+++ contrib/davical/htdocs/setup.php	(revision 3733)
@@ -0,0 +1,245 @@
+<?php
+
+include("./always.php");
+include("DAViCalSession.php");
+
+ob_start( );
+phpinfo();
+$phpinfo = ob_get_contents( );
+ob_end_clean( );
+
+$phpinfo = preg_replace( '{^.*?<body>}s', '', $phpinfo);
+$phpinfo = preg_replace( '{</body>.*?$}s', '', $phpinfo);
+
+$loaded_extensions = array_flip(get_loaded_extensions());
+
+/** @TODO: work out something more than true/false returns for dependency checks */
+function check_pgsql() {
+  return function_exists('pg_connect');
+}
+
+if ( check_pgsql() ) {
+  $session->LoginRequired( (isset($c->restrict_setup_to_admin) && $c->restrict_setup_to_admin ? 'Admin' : null ) );
+}
+
+function check_pdo() {
+  return class_exists('PDO');
+}
+
+function check_pdo_pgsql() {
+  global $phpinfo, $loaded_extensions;
+
+  if ( !class_exists('PDO') ) return false;
+  return isset($loaded_extensions['pdo_pgsql']);
+}
+
+function check_gettext() {
+  global $phpinfo, $loaded_extensions;
+
+  if ( !function_exists('gettext') ) return false;
+  return isset($loaded_extensions['gettext']);
+}
+
+function check_suhosin_server_strip() {
+  global $phpinfo, $loaded_extensions;
+
+  if ( !isset($loaded_extensions['suhosin']) ) return true;
+  if ( ini_get('suhosin.server.strip') == "0" ) {
+    return true;
+  }
+
+  return false;
+}
+
+include("interactive-page.php");
+include("page-header.php");
+
+require_once("AwlQuery.php");
+
+function check_schema_version() {
+  global $c;
+  if ( $c->want_dbversion[0] == $c->schema_major
+    && $c->want_dbversion[1] == $c->schema_minor
+    && $c->want_dbversion[2] == $c->schema_patch ) {
+    return true;
+  }
+  return false;
+}
+
+function check_davical_version() {
+  global $c;
+  $url = 'http://www.davical.org/current_davical_version?v='.$c->version_string;
+  $version_file = @fopen($url, 'r');
+  if ( ! $version_file ) return translate("Could not retrieve") . " '$url'";
+  $current_version = trim(fread( $version_file,12));
+  fclose($version_file);
+  return ( $c->version_string == $current_version ? true : $current_version );
+}
+
+
+function build_site_statistics() {
+  $principals  = translate('No. of Principals');
+  $collections = translate('No. of Collections');
+  $resources   = translate('No. of Resources');
+  $table = <<<EOTABLE
+<table class="statistics">
+<tr><th>$principals</th><th>$collections</th><th>$resources</th></tr>
+<tr>%s</tr>
+</table>
+EOTABLE;
+
+  if ( !check_pdo_pgsql() ) {
+    return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
+  }
+  $sql = 'SELECT
+(SELECT count(1) FROM principal) AS principals,
+(SELECT count(1) FROM collection) AS collections,
+(SELECT count(1) FROM caldav_data) AS resources';
+  $qry = new AwlQuery($sql);
+  if ( $qry->Exec('setup',__LINE__,__FILE__) && $s = $qry->Fetch() ) {
+    $row = sprintf('<td align="center">%s</td><td align="center">%s</td><td align="center">%s</td>',
+                                       $s->principals, $s->collections, $s->resources );
+    return sprintf( $table, $row );
+  }
+  return sprintf( $table, '<td colspan="3">'.translate('Site Statistics require the database to be available!').'</td>');
+}
+
+
+$dependencies = array(
+  translate('Current DAViCal version '). $c->version_string => 'check_davical_version',
+  translate('DAViCal DB Schema version '). implode('.',$c->want_dbversion) => 'check_schema_version',
+  translate('PHP PDO module available') => 'check_pdo',
+  translate('PDO PostgreSQL drivers') => 'check_pdo_pgsql',
+  translate('PHP PostgreSQL available') => 'check_pgsql',
+  translate('GNU gettext support') => 'check_gettext',
+  translate('Suhosin "server.strip" disabled') => 'check_suhosin_server_strip' /*,
+  'YAML' => 'php5-syck' */
+);
+
+$dependencies_table = '';
+$dep_tpl = '<tr class="%s">
+  <td>%s</td>
+  <td>%s</td>
+</tr>
+';
+foreach( $dependencies AS $k => $v ) {
+  $ok = $v();
+  $dependencies_table .= sprintf( $dep_tpl, ($ok === true ? 'dep_ok' : 'dep_fail'), $k,  (is_string($ok) ? $ok : ($ok ? translate('OK') : translate('Failed'))) );
+}
+
+$want_dbversion = implode('.',$c->want_dbversion);
+
+$heading_setup = translate('Setup');
+$paragraph_setup = translate('Currently this page does very little.  Suggestions or patches to make it do more useful stuff will be gratefully received.');
+
+$heading_versions = translate('Current Versions');
+if ( check_schema_version() != true )
+{
+  $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema should be at version %s and it is at version %d.%d.%d.');
+  $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $want_dbversion, $c->schema_major, $c->schema_minor, $c->schema_patch);
+} else {
+  $paragraph_versions = translate('You are currently running DAViCal version %s. The database schema is at version %d.%d.%d.');
+  $paragraph_versions = sprintf( $paragraph_versions, $c->version_string, $c->schema_major, $c->schema_minor, $c->schema_patch);
+}
+
+$heading_dependencies = translate('Dependencies');
+$th_dependency = translate('Dependency');
+$th_status     = translate('Status');
+
+$heading_site_statistics = translate('Site Statistics');
+$site_statistics_table = build_site_statistics();
+
+$heading_config_clients = translate('Configuring Calendar Clients for DAViCal');
+$heading_config_davical = translate('Configuring DAViCal');
+$davical_configuration_errors = '';
+if ( $config_warnings != '' ) {
+  $davical_configuration_errors = '<div class="error"><h3 class="error">' . translate('Your configuration produced PHP errors which should be corrected') . '</h3>
+<pre>
+'.$config_warnings.'
+</pre></div>
+';
+}
+
+  echo <<<EOBODY
+<style>
+tr.dep_ok {
+  background-color:#80ff80;
+}
+tr.dep_fail {
+  background-color:#ffc0c0;
+}
+table, table.dependencies {
+  border: 1px grey solid;
+  border-collapse: collapse;
+  padding: 0.1em;
+  margin: 0 1em 1.5em;
+}
+table tr td, table tr th, table.dependencies tr td, table.dependencies tr th {
+  border: 1px grey solid;
+  padding: 0.1em 0.2em;
+}
+p {
+  padding: 0.3em 0.2em 0.7em;
+}
+</style>
+
+<h1>$heading_setup</h1>
+<p>$paragraph_setup
+
+<h2>$heading_versions</h2>
+<p>$paragraph_versions
+<br>&nbsp;
+</p>
+
+<h2>$heading_dependencies</h2>
+<p>
+<table class="dependencies">
+<tr>
+<th>$th_dependency</th>
+<th>$th_status</th>
+</tr>
+$dependencies_table
+</table>
+<br>&nbsp;
+</p>
+
+<script language="javascript">
+function toggle_visible() {
+  var argv = toggle_visible.arguments;
+  var argc = argv.length;
+
+  var fld_checkbox =  document.getElementById(argv[0]);
+
+  if ( argc < 2 ) {
+    return;
+  }
+
+  for (var i = 1; i < argc; i++) {
+    var block_id = argv[i].substr(1);
+    var block_logical = argv[i].substr(0,1);
+    var b = document.getElementById(block_id);
+    if ( block_logical == '!' )
+      b.style.display = (fld_checkbox.checked ? 'none' : '');
+    else
+      b.style.display = (!fld_checkbox.checked ? 'none' : '');
+  }
+}
+</script><p><label>Show phpinfo() output:<input type="checkbox" value="1" id="fld_show_phpinfo" onclick="toggle_visible('fld_show_phpinfo','=phpinfo')"></label></p>
+<div style="display:none" id="phpinfo">$phpinfo</div>
+
+<h2>$heading_site_statistics</h2>
+<p>$site_statistics_table</p>
+
+<h2>$heading_config_clients</h2>
+<p>The <a href="http://www.davical.org/clients.php">client setup page on the DAViCal website</a> has information on how
+to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.</p>
+<p>The administrative interface has no facility for viewing or modifying calendar data.</p>
+
+<h2>$heading_config_davical</h2>
+<p>If you can read this then things must be mostly working already.</p>
+$davical_configuration_errors
+<p>The <a href="http://www.davical.org/installation.php">installation page on the DAViCal website</a> has
+some further information on how to install and configure this application.</p>
+EOBODY;
+
+include("page-footer.php");
Index: contrib/davical/htdocs/tools.php
===================================================================
--- contrib/davical/htdocs/tools.php	(revision 3733)
+++ contrib/davical/htdocs/tools.php	(revision 3733)
@@ -0,0 +1,161 @@
+<?php
+/**
+* Tools for manipulating calendars
+*
+* @package   davical
+* @subpackage   DAViCalSession
+* @author    Maxime Delorme <mdelorme@tennaxia.com>
+* @copyright Maxime Delorme
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+require_once("./always.php");
+require_once("DAViCalSession.php");
+$session->LoginRequired();
+
+require_once("DataEntry.php");
+require_once("interactive-page.php");
+require_once("classBrowser.php");
+
+if ( !$session->AllowedTo("Admin" ) )
+  exit;
+
+if( function_exists("sync_LDAP") && isset($_POST['Sync_LDAP'])){
+  sync_LDAP();
+}
+
+if( function_exists("sync_LDAP_groups") && isset($_POST['Sync_LDAP_groups'])){
+  sync_LDAP_groups();
+}
+
+if(isset($_POST['import_from_directory'])){
+  Tools::importFromDirectory();
+}
+
+
+class Tools {
+
+  function render(){
+    global $c;
+    echo  $this->renderImportFromDirectory();
+    if ( isset($c->authenticate_hook['call']) && $c->authenticate_hook['call'] == 'LDAP_check' && function_exists("sync_LDAP") ) {
+      echo $this->renderSyncLDAP();
+    }
+  }
+
+  function renderSyncLDAP(){
+    $html = '<div id="entryform">';
+    $html .= '<h1>'.translate('Sync LDAP with DAViCal') .'</h1>';
+
+    $data = (object) array('directory_path' => '/path/to/your/ics/files','calendar_path' => 'home');
+    $ef = new EntryForm( $_SERVER['REQUEST_URI'],$data , true,true );
+    $html .= "<table width=\"100%\" class=\"data\">\n";
+    $html .= $ef->StartForm( array("autocomplete" => "off" ) );
+    $html .= sprintf( "<tr><td style=\"text-align:left\" colspan=\"2\" >%s</td></tr>\n",
+    translate("This operation does the following: <ul><li>check valid users in LDAP directory</li> <li>check users in DAViCal</li></ul> then <ul><li>if a user is present in DAViCal but not in LDAP set him as inactive in DAViCal</li> <li>if a user is present in LDAP but not in DAViCal create the user in DAViCal</li> <li>if a user in present in LDAP and DAViCal then update information in DAViCal</li> </ul>"));
+    $html .= "</table>\n";
+
+    $html .= $ef->SubmitButton( "Sync_LDAP", translate('Submit'));
+
+    $html .= '<h1>'.translate('Sync LDAP Groups with RSCDS') .'</h1>';
+    $html .= "<table width=\"100%\" class=\"data\">\n";
+    $html .= $ef->StartForm( array("autocomplete" => "off" ) );
+    $html .= sprintf( "<tr><td style=\"text-align:left\" colspan=\"2\" >%s</td></tr>\n",
+    translate("This operation does the following: <ul><li>check valid groups in LDAP directory</li> <li>check groups in DAViCal</li></ul> then <ul><li>if a group is present in DAViCal but not in LDAP set as inactive in DAViCal</li> <li>if a group is present in LDAP but not in DAViCal create the group in DAViCal</li> <li>if a group in present in LDAP and DAViCal then update information in DAViCal</li> </ul>"));
+    $html .= "</table>\n";
+
+    $html .= $ef->SubmitButton( "Sync_LDAP_groups", translate('Submit'));
+    $html .= $ef->EndForm();
+
+    $html .= "</div>";
+    return $html;
+  }
+
+  function renderImportFromDirectory(){
+      $html = '<div id="entryform">';
+      $html .= '<h1>'.translate('Import all .ics files of a directory') .'</h1>';
+
+      $data = (object) array('directory_path' => '/path/to/your/ics/files','calendar_path' => 'home');
+      $ef = new EntryForm( $_SERVER['REQUEST_URI'],$data , true,true );
+      $html .= "<table width=\"100%\" class=\"data\">\n";
+      $html .= $ef->StartForm( array("autocomplete" => "off" ) );
+
+      $html .= $ef->DataEntryLine( translate("path to store your ics"), "%s", "text", "calendar_path",
+                array( "size" => 20,
+                        "title" => translate("Set the path to store your ics e.g. 'home' will be referenced as /caldav.php/me/home/"),
+                        "help" => translate("<b>WARNING: all events in this path will be deleted before inserting allof the ics file</b>")
+                      )
+                      , '' );
+
+      $html .= $ef->DataEntryLine( translate("Directory on the server"), "%s", "text", "directory_path",
+                array( "size" => 20, "title" => translate("The path on the server where your .ics files are.")));
+
+      $html .= "</table>\n";
+      $html .= $ef->SubmitButton( "import_from_directory", translate('Submit'));
+      $html .= $ef->EndForm();
+
+      $html .= "</div>";
+      return $html;
+  }
+
+  function importFromDirectory(){
+    global $c;
+    if(!isset($_POST["calendar_path"])){
+      dbg_error_log( "importFromDirectory", "calendar path not given");
+      return ;
+    }
+    $path_ics = $_POST["calendar_path"];
+    if ( substr($path_ics,-1,1) != '/' ) $path_ics .= '/';          // ensure that we target a collection
+    if ( substr($path_ics,0,1) != '/' )  $path_ics = '/'.$path_ics; // ensure that we target a collection
+
+    if(!isset($_POST["directory_path"])){
+      dbg_error_log( "importFromDirectory", "directory path not given");
+      return ;
+    }
+    $dir = $_POST["directory_path"];
+    if(!is_readable($dir)){
+      $c->messages[] = sprintf(i18n('directory %s is not readable'),htmlspecialchars($dir));
+      dbg_error_log( "importFromDirectory", "directory is not readable");
+      return ;
+    }
+    if ($handle = opendir($dir)) {
+      while (false !== ($file = readdir($handle))) {
+        if ($file == "." || $file == ".." || substr($file,-4) != '.ics') continue;
+        if ( !is_readable($dir.'/'.$file) ) {
+          dbg_error_log( "importFromDirectory", "ics file '%s' is not readable",$dir .'/'.$file);
+          continue;
+        }
+        $ics = file_get_contents($dir.'/'.$file);
+        $ics = trim($ics);
+
+
+        if ( $ics != '' ) {
+          include_once('check_UTF8.php');
+          if ( check_string($ics) ) {
+            $path = "/".substr($file,0,-4).$path_ics;
+            dbg_error_log( "importFromDirectory", "importing to $path");
+            $c->readonly_webdav_collections = false;  // Override this setting so we can create collections/events on import.
+            require_once("caldav-PUT-functions.php");
+            if ( $user = getUserByName(substr($file,0,-4),'importFromDirectory',__LINE__,__FILE__)) {
+              $user_no = $user->user_no;
+            }
+            if(controlRequestContainer(substr($file,0,-4),$user_no, $path,false) === -1)
+              continue;
+            import_collection($ics,$user_no,$path,1);
+            $c->messages[] = sprintf(translate('all events of user %s were deleted and replaced by those from file %s'),substr($file,0,-4),$dir.'/'.$file);
+          }
+          else {
+            $c->messages[] = sprintf(translate('the file %s is not UTF-8 encoded, please check error for more details'),$dir.'/'.$file);
+          }
+        }
+      }
+      closedir($handle);
+    }
+  }
+}
+
+$Tools = new Tools();
+
+include("page-header.php");
+$Tools->render();
+include("page-footer.php");
Index: contrib/davical/htdocs/css/browse.css
===================================================================
--- contrib/davical/htdocs/css/browse.css	(revision 3733)
+++ contrib/davical/htdocs/css/browse.css	(revision 3733)
@@ -0,0 +1,45 @@
+/* CSS for browse pages in DAViCal */
+
+tr.header th, td {
+padding: 1px 4px;
+}
+
+tr.header th {
+font-family: Arial Narrow, sans-serif;
+background-color: #CDF;
+color: black;
+}
+
+tr.header a {
+color: inherit;
+text-decoration:none;
+border:0;
+}
+
+tr.r0:hover, tr.r1:hover {
+background-color:#AADEFF;
+}
+
+tr.r0 {
+background-color: white;
+}
+
+tr.r1 {
+background-color: #DEF;
+}
+
+img.order {
+border:0;
+}
+
+.right {
+text-align:right;
+}
+
+.left {
+text-align:left;
+}
+
+#browser h2 {
+  margin: 0.8em 0 0.1em;
+}
Index: contrib/davical/htdocs/css/edit.css
===================================================================
--- contrib/davical/htdocs/css/edit.css	(revision 3733)
+++ contrib/davical/htdocs/css/edit.css	(revision 3733)
@@ -0,0 +1,124 @@
+.left {
+  text-align:left;
+}
+
+
+.center {
+  text-align:center;
+  white-space:nowrap;
+}
+
+
+.money, .right {
+  text-align:right;
+}
+
+.entry, input.money, input.date {
+  padding: 0;
+  margin: 0 -2px;
+/*  border: 1px #fffff0 dashed;
+  background-color: #f0f8d0; */
+  font-size: 0.9em;
+  font-family: Courier New, Courier, fixed;
+}
+input[type="checkbox"].entry {
+  margin-top:0.25em;
+}
+
+select.entry {
+  font-family: Helvetica Narrow, Arial Narrow, sans-serif;
+  font-size: 85%;
+}
+
+th.h2 {
+  font-size: 130%;
+  font-weight: bold;
+  font-family: verdana, sans-serif;
+  padding: 0 0.3em;
+  margin: 1px 0 0 0;
+  line-height: inherit;
+  text-align: left;
+}
+
+
+input {
+  margin: 0;
+  font-family: Courier New, monospace;
+}
+
+
+label, th.prompt {
+  font-family: Verdana, sans-serif;
+  font-weight: bolder;
+  font-size: 110%;
+  text-align: right;
+}
+
+
+a.order, a.invisilink {
+  text-decoration:none;
+  color: inherit;
+}
+
+
+.submit {
+  text-decoration: none;
+  font: small-caps bold 95% sans-serif;
+  color: #fff;
+  background-color: #483D8B;
+  padding: 0px 1px 1px 1px;
+  margin: 0;
+  vertical-align: text-bottom;
+  border: thin outset;
+  -moz-border-radius: 3px;
+}
+
+
+.submit:hover {
+  color: #FFD700;
+  border: thin inset;
+}
+
+a.submit {
+ padding: 0.07em 0.25em;
+}
+
+
+table {
+  background-color: #F0E8FF;
+  border-collapse:collapse;
+  margin-top:1px;
+  margin-bottom:0.1em;
+}
+
+
+th {
+  padding: 0.15em 0.3em 0;
+  font-weight: bolder;
+  background-color: #F0E8FF;
+}
+
+
+td {
+  padding: 0 0.2em;
+  border: thin white solid;
+}
+
+
+tr {
+  vertical-align:top;
+  border: thin white solid;
+}
+
+
+
+table.form_inner, table.form_inner tr, table.form_inner td, table.form_inner th {
+  margin: 0;
+  padding-left: 0;
+  border: 0;
+  border-collapse: collapse;
+}
+
+table.form_inner {
+  width: 100%;
+}
Index: contrib/davical/htdocs/davical.css
===================================================================
--- contrib/davical/htdocs/davical.css	(revision 3733)
+++ contrib/davical/htdocs/davical.css	(revision 3733)
@@ -0,0 +1,354 @@
+/* Basic overridable CSS for DAViCal CalDAV Server */
+
+body, p, li, td {
+padding: 0;
+margin: 0;
+border: none;
+padding-bottom:2em;
+}
+
+.prompt {
+font-weight: bold;
+padding: 1px 1em 1px 0.5em;
+text-align: right;
+}
+
+th.prompt {
+  width:18%;
+}
+
+.error {
+/* font-weight: bold; */
+font-size: 110%;
+font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
+padding: 1px 0.3em;
+background-color: #c00000;
+color: #ffff00;
+}
+
+a.error, a.error:hover, a.error:visited {
+  background-color: #c00000;
+  color: #ffffd0;
+  text-decoration:underline;
+}
+
+h1, h2, h3, h4, h5 {
+font-weight: bold;
+font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
+padding-left: 0.3em;
+padding-right: 0.3em;
+padding-bottom: 0.1em;
+}
+
+h1 {
+font-size: 100%;
+background-color: #ABF;
+margin-top: 10px;
+padding: 0.3em;
+color:white;
+border-bottom:2px dashed #009;
+}
+
+h1:first-letter {
+font-size: 120%;
+}
+
+h2 {
+font-size: 100%;
+background-color: #BCF;
+margin: 0;
+padding-top: 0.3em;
+/*margin-top: 2px;
+margin-bottom: 0.3em;
+margin-left :15px;
+*/
+border:1px dashed #99F;
+}
+
+h3 {
+font-size: 100%;
+background-color: #DEF;
+margin: 0;
+padding-top: 0.2em;
+margin-top: 0.4em;
+margin-bottom: 0.2em;
+margin-left :25px;
+border:1px dashed #BCF;
+}
+
+p {
+padding: 0.1em 0.3em 0.2em;
+}
+
+ul {
+list-style-type: square;
+margin: 0 0 0.5em;
+}
+
+li {
+padding: 0 0.3em 0.2em;
+}
+input.text, input.password, label, input.fcheckbox,
+input.ftext, input.fpassword, input.fdate, input.ftimestamp, select.fselect, select.flookup {
+background-color: #EFFAFF;
+color: navy;
+border: thin solid #c0c0c0;
+width:250px;
+}
+label.fcheckbox {
+display:block;
+width:244px;
+margin-top:2px;
+}
+input.fcheckbox {
+width:13px;
+}
+input.text, input.password,
+input.ftext, input.fpassword, input.fdate, input.ftimestamp {
+padding: 3px;
+margin: 0;
+}
+
+label.fcheckbox {
+padding: 2px;
+margin-right: 2px;
+}
+
+input.fcheckbox {
+margin-top: 6px;
+margin-bottom: 3px;
+vertical-align:-4px;
+border: none;
+}
+
+select.fselect, select.flookup {
+padding: 2px 0;
+margin: 3px 0 0;
+}
+
+.prompt {
+font-family: Arial Narrow, sans-serif;
+background-color: #CDF;
+color: #003010;
+}
+
+td.entry {
+padding: 1px;
+}
+
+.ph {
+font-family:Bitstream Vera Sans, sans-serif;
+color: #003010;
+font-size: 120%;
+background-color: #BCF;
+text-align:left;
+border-top:10px white solid;
+padding-top:3px;
+border-bottom:4px white solid;
+}
+
+
+#messages {
+background-color: #602000;
+color:white;
+border:0;
+padding:2px 6px;
+}
+
+ul.messages, li.messages {
+font-family:Bitstream Vera Sans, sans-serif;
+font-weight:700;
+font-size: 1.1em;
+}
+
+li.messages {
+display:inherit;
+}
+
+
+.fsubmit {
+background-color: white;
+font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
+font-weight: bold;
+border:2px solid #009;
+height:1.4em;
+line-height:1.6em;
+font-size: 1.0em;
+background-color: #ABE;
+-moz-border-radius: 2em;
+text-decoration:none;
+color:black;
+padding:0 1em 0.1em;
+margin:0 1em 0.1em;
+}
+/* To get a matching height on 'input' buttons we have to set height, not line-height */
+input.fsubmit {
+  height:1.6em;
+}
+.fsubmit:hover {
+border:2px outset #009;
+}
+.fsubmit:active {
+border:2px inset #009;
+}
+
+a.error {
+font-family: Bitstream Vera Sans, Vera Sans, sans-serif;
+font-weight: bold;
+border:2px solid #800;
+height:1.4em;
+line-height:1.6em;
+font-size: 1.0em;
+background-color: #F31;
+-moz-border-radius: 2em;
+text-decoration:none;
+color:white;
+padding:0 1em 0.1em;
+margin:0 1em 0.1em;
+}
+a.error:hover {
+border:2px outset #800;
+background-color: #F31;
+}
+a.error:active {
+border:2px inset #800;
+}
+
+.w40 { width: 40em; }
+.w35 { width: 35em; }
+.w30 { width: 30em; }
+.w25 { width: 25em; }
+.w20 { width: 20em; }
+.w15 { width: 15em; }
+.w10 { width: 10em; }
+
+/* Handle the menus, usually at the top of the screen */
+
+div.menu {
+  width:100%;
+  font-size:1em;
+  display:block;
+  height:1.5em;
+/*  position:absolute; */
+/*  z-index:100; */
+  background-color:#e0e0ef;
+/*  border: 1px red solid; */
+/*  background-color: #c7d0d9;
+  border: thin outset #b0b0e0; */
+  z-index: 10;
+}
+
+#related  {
+background-color: white;
+color: white;
+font-weight: bold;
+padding: 0.8em 0.4em 0.2em 0;
+margin-bottom: 0;
+height:1.3em;
+border-bottom:1px solid #ABF;
+}
+
+#related a.menu, #related a.menu:hover, #related a.menu_active, #related a.menu_active:hover {
+color: #999;
+text-decoration: none;
+background-color: #DEF;
+padding: 0.2em 0.4em;
+margin-right: 0.4em;
+height:1.3em;
+border: 1px solid #ABF;
+-moz-border-radius-topright: 0.8em;
+-moz-border-radius-topleft: 0.8em;
+
+}
+
+#related a.menu:hover, #related a.menu_active:hover {
+color: white;
+background-color: #BCF;
+}
+
+#related a.menu_active, #related a.menu_active:hover {
+background-color: white;
+color: #008;
+border-bottom:1px solid white;
+}
+
+
+/* remove all the bullets, borders and padding from the default list styling */
+.menu ul {
+  width:100%;
+  padding:0;
+  margin:0;
+  list-style-type:none;
+  display:block;
+  height:1.4em;
+  position: fixed;
+  top: 0px;
+  background-color: #c7d0d9;
+  border: thin outset #b0b0e0;
+}
+
+/* float the list to make it horizontal and a relative positon so that you can control the dropdown menu positon */
+.menu li {
+  float:left;
+  position:relative;
+  padding:0;
+  display: inline;
+}
+
+/* style the links for the top level */
+.menu a, .menu a:visited {
+  display:block;
+  font-weight: bold;
+  font-family: sans-serif;
+  text-decoration:none;
+  color:navy;
+  height:1.2em;
+  vertical-align:middle;
+  border:0 solid #fff;
+  background-color:#f0f0ff;
+  border-width:0;
+  padding: 0.1em 1em;
+  margin: 0;
+  line-height:100%;
+/*  border: 1px green dashed; */
+}
+
+/* hide the sub levels and give them a positon absolute so that they take up no room */
+.menu ul ul {
+  visibility:hidden;
+  position:absolute;
+  text-decoration:none;
+  height:0;
+  top:1.2em;
+  left:0;
+}
+
+/* style the second level links */
+.menu ul ul a,
+.menu ul ul a:visited {
+  background:#f0f0ff;
+  color:navy;
+  font-family: sans-serif;
+  border-bottom: 1px solid #cccccc;
+  height:auto;
+  line-height:1em;
+  padding:0.2em 0 0.2em 1em;
+  width:12em;
+}
+
+/* style the top level hover */
+.menu :hover > a,
+.menu ul ul :hover > a,
+.menu a:hover,
+.menu ul ul a:hover {
+  background-color:#3050c0;
+  color:#f0f0a0;
+  text-decoration:none;
+}
+
+/* make the second level visible when hover on first level list OR link */
+.menu ul li:hover ul,
+.menu ul a:hover ul{
+  visibility:visible;
+}
+
Index: contrib/davical/htdocs/index.php
===================================================================
--- contrib/davical/htdocs/index.php	(revision 3733)
+++ contrib/davical/htdocs/index.php	(revision 3733)
@@ -0,0 +1,61 @@
+<?php
+if ( $_SERVER['REQUEST_METHOD'] != "GET" && $_SERVER['REQUEST_METHOD'] != "POST" && $_SERVER['REQUEST_METHOD'] != "HEAD" ) {
+  /**
+  * If the request is not a GET or POST then they must really want caldav.php!
+  */
+  include("./caldav.php");
+  exit;  // Not that it should return from that!
+}
+
+include("./always.php");
+include("DAViCalSession.php");
+$session->LoginRequired();
+
+include("interactive-page.php");
+include("page-header.php");
+
+  echo <<<EOBODY
+<h1>Administration</h1>
+<p>You are logged on as $session->username ($session->fullname)</p>
+EOBODY;
+?>
+<h2>Administration Functions</h2>
+<p>The administration of this application should be fairly simple.  You can administer:</p>
+<ul>
+<li>Users (or Resources or Groups) and the relationships between them</li>
+<li>The types of relationships that are available</li>
+</ul>
+<p><i>There is no ability to view and / or maintain calendars or events from within this administrative interface.</i></p>
+<p>To do that you will need to use a CalDAV capable calendaring application such as Evolution, Sunbird, Thunderbird
+(with the Lightning extension) or Mulberry.</p>
+
+<h3>Principals: Users, Resources and Groups</h3>
+<p>These are the things which may have collections of calendar resources (i.e. calendars).</p>
+<p><a href="<?php echo $c->base_url; ?>/admin.php?action=browse&t=principal&type=1">Here is a list of users (maybe :-)</a>.  You can click on any user to see the full detail
+for that person (or group or resource - but from now we'll just call them users).</p>
+<p>The primary differences between them are as follows:</p>
+<ul>
+<li>Users will probably have calendars, and are likely to also log on to the system.</li>
+<li>Resources do have calendars, but they will not usually log on.</li>
+<li>Groups provide an intermediate linking to minimise administration overhead.  They might not have calendars, and they will not usually log on.</li>
+</ul>
+
+<h3>Groups &amp; Grants</h3>
+<ul>
+<li>Grants specify the access rights to a collection or a principal</li>
+<li>Groups allow those granted rights to be assigned to a set of many principals in one action</li>
+<li>Groups may be members of other groups, but complex nesting will hurt system performance</li>
+</ul>
+
+<h2>Configuring Calendar Clients for DAViCal</h2>
+<p>The <a href="http://rscds.sourceforge.net/clients.php">DAViCal client setup page on sourceforge</a> have information on how
+to configure Evolution, Sunbird, Lightning and Mulberry to use remotely hosted calendars.</p>
+<p>The administrative interface has no facility for viewing or modifying calendar data.</p>
+
+<h2>Configuring DAViCal</h2>
+<p>If you can read this then things must be mostly working already.</p>
+<p>The <a href="http://rscds.sourceforge.net/installation.php">DAViCal installation page</a> on sourceforge has
+some further information on how to install and configure this application.</p>
+
+<?php
+include("page-footer.php");
Index: contrib/davical/htdocs/davical
===================================================================
--- contrib/davical/htdocs/davical	(revision 3733)
+++ contrib/davical/htdocs/davical	(revision 3733)
@@ -0,0 +1,1 @@
+link davical
Index: contrib/davical/htdocs/public.php
===================================================================
--- contrib/davical/htdocs/public.php	(revision 3733)
+++ contrib/davical/htdocs/public.php	(revision 3733)
@@ -0,0 +1,53 @@
+<?php
+/**
+* CalDAV Server - main program
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst .Net Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+require("./always.php");
+dbg_error_log( "caldav", " User agent: %s", ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Unfortunately Mulberry does not send a 'User-agent' header with its requests :-(")) );
+dbg_log_array( "headers", '_SERVER', $_SERVER, true );
+require("PublicSession.php");
+$session = new PublicSession();
+
+/** A simplified DAV header in this case */
+$dav = "1, 2, calendar-access";
+header( "DAV: $dav");
+
+require_once("CalDAVRequest.php");
+$request = new CalDAVRequest();
+if ( !isset($request->ticket) && !$request->IsPublic() ) {
+  $request->DoResponse( 403, translate('Anonymous users may only access public calendars') );
+}
+
+switch ( $request->method ) {
+  case 'OPTIONS':    include_once("caldav-OPTIONS.php");    break;
+  case 'REPORT':     include_once("caldav-REPORT.php");     break;
+  case 'PROPFIND':   include_once("caldav-PROPFIND.php");   break;
+  case 'GET':        include_once("caldav-GET.php");        break;
+  case 'HEAD':       include_once("caldav-GET.php");        break;
+
+  case 'PROPPATCH':
+  case 'MKCALENDAR':
+  case 'MKCOL':
+  case 'PUT':
+  case 'DELETE':
+  case 'LOCK':
+  case 'UNLOCK':
+    $request->DoResponse( 403, translate('Anonymous users are not allowed to modify calendars') );
+    break;
+
+  case 'TESTRRULE':  include_once("test-RRULE.php");        break;
+
+  default:
+    dbg_error_log( "caldav", "Unhandled request method >>%s<<", $request->method );
+    dbg_log_array( "caldav", '_SERVER', $_SERVER, true );
+    dbg_error_log( "caldav", "RAW: %s", str_replace("\n", "",str_replace("\r", "", $request->raw_post)) );
+}
+
+$request->DoResponse( 500, translate("The application program does not understand that request.") );
+
Index: contrib/davical/htdocs/freebusy.php
===================================================================
--- contrib/davical/htdocs/freebusy.php	(revision 3733)
+++ contrib/davical/htdocs/freebusy.php	(revision 3733)
@@ -0,0 +1,85 @@
+<?php
+require_once("./always.php");
+dbg_error_log( "freebusy", " User agent: %s", ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Unfortunately Mulberry and Chandler don't send a 'User-agent' header with their requests :-(")) );
+dbg_log_array( "headers", '_SERVER', $_SERVER, true );
+if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
+  require_once("PublicSession.php");
+  $session = new PublicSession();
+}
+else {
+  require_once("HTTPAuthSession.php");
+  $session = new HTTPAuthSession();
+}
+
+
+/**
+* Submission parameters recommended by calconnect, plus some generous alternatives
+*/
+param_to_global('fb_start', '#^[a-z0-9/:.,+-]+$#i', 'start', 'from');
+param_to_global('fb_end', '#^[a-z0-9/:.,+-]+$#i', 'end', 'until', 'finish', 'to');
+param_to_global('fb_period', '#^[+-]?P?(\d+[WD]?)(T(\d+H)?(\d+M)?(\d+S)?)?+$#', 'period');
+param_to_global('fb_format', '#^\S+/\S+$#', 'format');
+param_to_global('fb_user', '#^.*$#', 'user', 'userid', 'user_no', 'email');
+param_to_global('fb_token', '#^[a-z0-9+/-]+$#i', 'token');
+
+if ( isset($fb_period) ) $fb_period = strtoupper($fb_period);
+
+if ( !isset($fb_start) || $fb_start == '' )  $fb_start  = date('Y-m-d\TH:i:s', time() - 86400 ); // no recommended default.  -1 day
+if ( (!isset($fb_period) && !isset($fb_end)) || ($fb_period == '' && $fb_end == '') )
+  $fb_period = 'P44D'; // 44 days - 2 days more than recommended default
+
+
+/**
+* If fb_user (user, userid, user_no or email parameter) then we adjust
+* the path of the request to suit.
+*/
+if ( isset($fb_user) ) $_SERVER['PATH_INFO'] = '/'.$fb_user.'/';
+
+/**
+* We also allow URLs like .../freebusy.php/user@example.com to work, so long as
+* the e-mail matches a single user whose calendar we have rights to.
+* @NOTE: It is OK for there to *be* duplicate e-mail addresses, just so long as we
+* only have read permission (or more) for only one of them.
+*/
+require_once("CalDAVRequest.php");
+$request = new CalDAVRequest(array("allow_by_email" => 1));
+$path_match = '^'.$request->path;
+if ( preg_match( '{^/(\S+@[a-z0-9][a-z0-9-]*[.][a-z0-9.-]+)/?$}i', $request->path, $matches ) ) {
+  $u = getUserByEMail($matches[1]);
+  $path_match = '^/'.$u->username.'/';
+}
+
+if ( isset($fb_format) && $fb_format != 'text/calendar' ) {
+  $request->DoResponse( 406, 'This server only supports the text/calendar format for freebusy URLs' );
+}
+
+if ( ! $request->HavePrivilegeTo('read-free-busy') ) $request->DoResponse( 404 );
+
+require_once("freebusy-functions.php");
+
+switch ( $_SERVER['REQUEST_METHOD'] ) {
+  case 'GET':
+    $range_start = new RepeatRuleDateTime($fb_start);
+    if ( !isset($fb_end) ) {
+      $range_end = clone($range_start);
+      $range_end->modify($fb_period);
+    }
+    else {
+      $range_end = new RepeatRuleDateTime($fb_end);
+    }
+    $freebusy = get_freebusy( $path_match, $range_start, $range_end );
+
+    $result = new iCalComponent();
+    $result->VCalendar();
+    $result->AddComponent($freebusy);
+
+    $request->DoResponse( 200, $result->Render(), 'text/calendar' );
+    break;
+
+  default:
+    dbg_error_log( "freebusy", "Unhandled request method >>%s<<", $_SERVER['REQUEST_METHOD'] );
+    dbg_log_array( "freebusy", 'HEADERS', $raw_headers );
+    dbg_log_array( "freebusy", '_SERVER', $_SERVER, true );
+    dbg_error_log( "freebusy", "RAW: %s", str_replace("\n", "",str_replace("\r", "", $raw_post)) );
+}
+
Index: contrib/davical/htdocs/.htaccess
===================================================================
--- contrib/davical/htdocs/.htaccess	(revision 3733)
+++ contrib/davical/htdocs/.htaccess	(revision 3733)
@@ -0,0 +1,33 @@
+
+# SetHandler php-script
+
+#<LimitExcept MOVE COPY CONNECT>
+#        Order allow,deny
+#        Allow from all
+#</LimitExcept>
+#
+#<Limit MOVE COPY CONNECT>
+#        Order deny,allow
+#        Deny from all
+#</Limit>
+
+# RewriteEngine On
+
+# # Not if it's the root URL.  You might want to comment this out if you
+# # want to use an explicit /index.php for getting to the admin pages.
+# RewriteCond %{REQUEST_URI} !^/$
+# RewriteCond %{REQUEST_URI} !^/davical/$
+
+# # Not if it explicitly specifies a .php program, stylesheet or image
+# RewriteCond %{REQUEST_URI} !\.(php|css|js|png|gif|jpg)
+
+# # Everything else gets rewritten to /caldav.php/...
+# RewriteRule ^(.*)$ /caldav.php$1  [NC,L]
+
+# # php_value include_path /usr/share/awl/inc
+# php_value magic_quotes_gpc 0
+# php_value register_globals 0
+# php_value open_basedir 1
+# php_value error_reporting "E_ALL & ~E_NOTICE"
+# php_value default_charset "utf-8"
+
Index: contrib/davical/htdocs/js/browse.js
===================================================================
--- contrib/davical/htdocs/js/browse.js	(revision 3733)
+++ contrib/davical/htdocs/js/browse.js	(revision 3733)
@@ -0,0 +1,46 @@
+/**
+* Simple function to send the browser to a given URL
+*/
+function Go( url ) {
+  window.location=url;
+  return true;
+}
+
+/**
+* Make this tag into a Link to a given URL
+*/
+function LinkTo( tag, url ) {
+  tag.style.cursor = "pointer";
+  tag.setAttribute('onClick', "Go('" + url.replace(/&amp;/g,'&') + "')");
+  tag.setAttribute('onMouseOut', "window.status='';return true;");
+  window.status = window.location.protocol + '//' + document.domain + url;
+  tag.setAttribute('onMouseover', "window.status = window.location.protocol + '//' + document.domain + '" + url + "';return true;");
+  tag.setAttribute('href', url);
+  return true;
+}
+
+/**
+* Make this tag and all of it's contents into a clickable link, using the link target from an
+* existing link target somewhere within the tag.  Setting 'which1' to '1' will make the target
+* match the 1st href target within the HTML of the tag.
+* @param objectref tag A reference to the object which will become clickable.
+* @param int which1 A one-based index to select which internal href attribute will become the target.
+*/
+function LinkHref( tag, which1 ) {
+  var urls = tag.innerHTML.match( / href="([^"]*)"/ig );
+//  alert(show_props(urls,'urls', 1));
+  try {
+    var url = urls[which1 - 1];
+    urls = url.match( /="([^"]*)"/ );
+  }
+  catch (e) {
+    //alert("Here are the URLs found:\nYou appear to need to choose a different index for your LinkHref call (the second parameter).  Add 1 to the index below for the correct URL shown and use that.\n\n" + show_props(urls,'urls', 0));
+    return false;
+  }
+//  alert(show_props(urls,'urls', 1));
+  url = urls[1];
+//  alert("Linking to >>>" + url + "<<<");
+  LinkTo(tag,url);
+  return true;
+}
+
Index: contrib/davical/htdocs/upgrade.php
===================================================================
--- contrib/davical/htdocs/upgrade.php	(revision 3733)
+++ contrib/davical/htdocs/upgrade.php	(revision 3733)
@@ -0,0 +1,96 @@
+<?php
+
+include("./always.php");
+include("DAViCalSession.php");
+$session->LoginRequired('Admin');
+
+include("interactive-page.php");
+
+ $page_elements = array();
+ $page_elements[] = <<<EOBODY
+<h1>Upgrade Database</h1>
+<p>Currently this page does nothing.  Suggestions or patches to make it do something
+useful will be gratefully received.
+<br>&nbsp;
+</p>
+<h2>Upgrading DAViCal Versions</h2>
+<p>The <a href="http://wiki.davical.org/w/Update-davical-database">update-davical-database</a> should be run
+manually after upgrading the software to a new version of DAViCal.</p>
+
+<p>In due course this program will implement the functionality which is currently contained in that
+script, but until then I'm afraid you do need to run it.
+EOBODY;
+
+
+
+include("classEditor.php");
+include("AwlUpgrader.php");
+
+$editor = new Editor(translate('Upgrade Database'));
+$editor->AddField('dbhost', "''");
+$editor->AddField('dbport', "''");
+$editor->AddField('dbname', "'davical'");
+$editor->AddField('dbuser', "'davical_dba'");
+$editor->AddField('dbpass', "''");
+$editor->AddField('app_user', "'davical_app'");
+$editor->AddField('apply_patches', "'t'");
+$editor->AddField('owner', "davical_dba");
+
+$prompt_dbname = translate('Database Name');
+$prompt_dbuser = translate('Database Username');
+$prompt_dbpass = translate('Database Password');
+$prompt_dbport = translate('Database Port');
+$prompt_dbhost = translate('Database Host');
+
+$prompt_app_user = translate('Application DB User');
+$prompt_do_patch = translate('Apply DB Patches');
+$prompt_owner = translate('Database Owner');
+
+$template = <<<EOTEMPLATE
+##form##
+<table>
+ <tr> <th class="h2" colspan="2">Connection Parameters</th> </tr>
+ <tr> <th class="right">$prompt_dbhost:</th>           <td class="left">##dbhost.input.20##</td> </tr>
+ <tr> <th class="right">$prompt_dbport:</th>      <td class="left">##dbport.input.5##</td> </tr>
+ <tr> <th class="right">$prompt_dbname:</th>    <td class="left">##dbname.input.20##</td> </tr>
+ <tr> <th class="right">$prompt_dbuser:</th>         <td class="left">##dbuser.input.20##</td> </tr>
+ <tr> <th class="right">$prompt_dbpass:</th>        <td class="left">##dbpass.password.20##</td> </tr>
+ <tr> <th class="h2" colspan="2">Operation Parameters</th> </tr>
+ <tr> <th class="right">$prompt_app_user:</th>         <td class="left">##app_user.input.20##</td> </tr>
+ <tr> <th class="right">$prompt_do_patch:</th>      <td class="left">##apply_patches.checkbox##</td> </tr>
+ <tr> <th class="right">$prompt_owner:</th>         <td class="left">##owner.input.20##</td> </tr>
+ <tr> <th class="right"></th>                   <td class="left" colspan="2">##submit##</td> </tr>
+</table>
+</form>
+
+EOTEMPLATE;
+
+
+$editor->SetTemplate( $template );
+$page_elements[] = $editor;
+
+$c->stylesheets[] = 'css/edit.css';
+
+include("page-header.php");
+
+/**
+* Page elements could be an array of viewers, browsers or something else
+* that supports the Render() method... or a non-object which we assume is
+* just a string of text that we echo.
+*/
+$heading_level = null;
+foreach( $page_elements AS $k => $page_element ) {
+  if ( is_object($page_element) ) {
+    echo $page_element->Render($heading_level);
+    $heading_level = 'h2';
+  }
+  else {
+    echo $page_element;
+  }
+}
+
+if (function_exists("post_render_function")) {
+  post_render_function();
+}
+
+include("page-footer.php");
Index: contrib/davical/htdocs/help.php
===================================================================
--- contrib/davical/htdocs/help.php	(revision 3733)
+++ contrib/davical/htdocs/help.php	(revision 3733)
@@ -0,0 +1,30 @@
+<?php
+include("./always.php");
+include("DAViCalSession.php");
+$session->LoginRequired();
+
+include("interactive-page.php");
+
+$c->page_title = "DAViCal CalDAV Server - Configuration Help";
+include("page-header.php");
+
+$wiki_help = '';
+if ( isset($_SERVER['HTTP_REFERER']) ) {
+  $wiki_help = preg_replace('#^.*/#', '', $_SERVER['HTTP_REFERER']);
+  $wiki_help = preg_replace('#\.php.*$#', '', $wiki_help);
+  $wiki_help = 'w/Help/'.$wiki_help;
+}
+
+$content = translate('<h1>Help</h1>
+<p>For initial help you should visit the <a href="http://www.davical.org/" target="_blank">DAViCal Home Page</a> or take
+a look at the <a href="http://wiki.davical.org/'.$wiki_help.'" target="_blank">DAViCal Wiki</a>.</p>
+<p>If you can\'t find the answers there, visit us on <a href="http://wikipedia.org/wiki/Internet_Relay_Chat" target="_blank">IRC</a> in
+the <b>#davical</b> channel on <a href="http://www.oftc.net/" target="_blank">irc.oftc.net</a>,
+or send a question to the <a href="http://lists.sourceforge.net/mailman/listinfo/rscds-general" target="_blank">DAViCal Users mailing list</a>.</p>
+<p>The <a href="http://sourceforge.net/mailarchive/forum.php?forum_id=8348" title="DAViCal Users Mailing List" target="_blank">mailing list
+archives can be helpful too.</p>');
+
+echo $content;
+
+include("page-footer.php");
+
Index: contrib/davical/htdocs/admin.php
===================================================================
--- contrib/davical/htdocs/admin.php	(revision 3733)
+++ contrib/davical/htdocs/admin.php	(revision 3733)
@@ -0,0 +1,52 @@
+<?php
+require_once('./always.php');
+require_once('classEditor.php');
+require_once('classBrowser.php');
+include("DAViCalSession.php");
+$session->LoginRequired();
+
+require_once('AwlQuery.php');
+
+param_to_global('action', '{(edit|browse)}', 'action');
+param_to_global('component', '{[a-z0-9-_]+}', 't');
+param_to_global('id', '{[a-z0-9-_]+}', 'id');
+
+$c->stylesheets[] = 'css/'.$action.'.css';
+if ( $c->enable_row_linking ) {
+  $c->scripts[] = 'js/browse.js';
+}
+
+require_once('interactive-page.php');
+
+$page_elements = array();
+$code_file = sprintf( 'ui/%s-%s.php', $component, $action );
+if ( ! @include_once( $code_file ) ) {
+  $c->messages[] = sprintf('No page found to %s %s%s%s', $action, ($action == 'browse' ? '' : 'a '), $component, ($action == 'browse' ? 's' : ''));
+  include('page-header.php');
+  include('page-footer.php');
+  exit(0);
+}
+
+include('page-header.php');
+
+/**
+* Page elements could be an array of viewers, browsers or something else
+* that supports the Render() method... or a non-object which we assume is
+* just a string of text that we echo.
+*/
+$heading_level = null;
+foreach( $page_elements AS $k => $page_element ) {
+  if ( is_object($page_element) ) {
+    echo $page_element->Render($heading_level);
+    $heading_level = 'h2';
+  }
+  else {
+    echo $page_element;
+  }
+}
+
+if (function_exists("post_render_function")) {
+  post_render_function();
+}
+
+include('page-footer.php');
Index: contrib/davical/dba/base-data.sql
===================================================================
--- contrib/davical/dba/base-data.sql	(revision 3733)
+++ contrib/davical/dba/base-data.sql	(revision 3733)
@@ -0,0 +1,41 @@
+-- Some sample data to prime the database...
+
+-- FIXME: Only insert the rows if they are not there already.
+INSERT INTO roles ( role_no, role_name ) VALUES( 1, 'Admin');
+INSERT INTO roles ( role_no, role_name ) VALUES( 2, 'Group');
+INSERT INTO roles ( role_no, role_name ) VALUES( 3, 'Public');
+INSERT INTO roles ( role_no, role_name ) VALUES( 4, 'Resource');
+
+-- Set the insert sequence to the next number, with a minimum of 10
+SELECT setval('roles_role_no_seq', (SELECT 10 UNION SELECT role_no FROM roles ORDER BY 1 DESC LIMIT 1) );
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES ( 1, TRUE, current_date, current_date, 'admin', '**nimda', 'DAViCal Administrator', 'calendars@example.net' );
+
+INSERT INTO role_member (user_no, role_no) VALUES(1, 1);
+
+-- Set the insert sequence to the next number, with a minimum of 1000
+SELECT setval('usr_user_no_seq', (SELECT 1000 UNION SELECT user_no FROM usr ORDER BY 1 DESC LIMIT 1) );
+
+INSERT INTO relationship_type ( rt_id, rt_name, confers, bit_confers )
+    VALUES( 1, 'Administers', 'A', privilege_to_bits('DAV::all') );
+
+INSERT INTO relationship_type ( rt_id, rt_name, confers, bit_confers )
+    VALUES( 2, 'Can read/write to', 'RW', privilege_to_bits( ARRAY['DAV::read','DAV::write']) );
+
+INSERT INTO relationship_type ( rt_id, rt_name, confers, bit_confers )
+    VALUES( 3, 'Can read from', 'R', privilege_to_bits( 'DAV::read') );
+
+INSERT INTO relationship_type ( rt_id, rt_name, confers, bit_confers )
+    VALUES( 4, 'Can see free/busy time of', 'F', privilege_to_bits( 'caldav:read-free-busy') );
+
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 1, 'Person' );
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 2, 'Resource' );
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 3, 'Group' );
+
+INSERT INTO principal ( type_id, user_no, displayname, default_privileges )
+    VALUES ( 1, 1, 'DAViCal Administrator', 0::BIT(24) );
+
+-- Set the insert sequence to the next number, with a minimum of 1000
+SELECT setval('relationship_type_rt_id_seq', (SELECT 10 UNION SELECT rt_id FROM relationship_type ORDER BY 1 DESC LIMIT 1) );
+
Index: contrib/davical/dba/rrule_functions.sql
===================================================================
--- contrib/davical/dba/rrule_functions.sql	(revision 3733)
+++ contrib/davical/dba/rrule_functions.sql	(revision 3733)
@@ -0,0 +1,716 @@
+/**
+* PostgreSQL Functions for RRULE handling
+*
+* @package rscds
+* @subpackage database
+* @author Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*
+* Coverage of this function set
+*  - COUNT & UNTIL are handled, generally
+*  - DAILY frequency, including BYDAY, BYMONTH, BYMONTHDAY, BYWEEKNO, BYMONTHDAY
+*  - WEEKLY frequency, including BYDAY, BYMONTH, BYMONTHDAY, BYWEEKNO, BYSETPOS
+*  - MONTHLY frequency, including BYDAY, BYMONTH, BYSETPOS
+*  - YEARLY frequency, including BYMONTH, BYMONTHDAY, BYSETPOS, BYDAY
+*
+* Not covered as yet
+*  - DAILY:   BYYEARDAY, BYSETPOS*
+*  - WEEKLY:  BYYEARDAY
+*  - MONTHLY: BYYEARDAY, BYMONTHDAY, BYWEEKNO
+*  - YEARLY:  BYYEARDAY
+*  - SECONDLY
+*  - MINUTELY
+*  - HOURLY
+*
+*/
+
+-- Create a composite type for the parts of the RRULE.
+DROP TYPE rrule_parts CASCADE;
+CREATE TYPE rrule_parts AS (
+  base TIMESTAMP WITH TIME ZONE,
+  until TIMESTAMP WITH TIME ZONE,
+  freq TEXT,
+  count INT,
+  interval INT,
+  bysecond INT[],
+  byminute INT[],
+  byhour INT[],
+  bymonthday INT[],
+  byyearday INT[],
+  byweekno INT[],
+  byday TEXT[],
+  bymonth INT[],
+  bysetpos INT[],
+  wkst TEXT
+);
+
+
+-- Create a function to parse the RRULE into it's composite type
+CREATE or REPLACE FUNCTION parse_rrule_parts( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS rrule_parts AS $$
+DECLARE
+  basedate   ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  result rrule_parts%ROWTYPE;
+  tempstr TEXT;
+BEGIN
+  result.base       := basedate;
+  result.until      := substring(repeatrule from 'UNTIL=([0-9TZ]+)(;|$)');
+  result.freq       := substring(repeatrule from 'FREQ=([A-Z]+)(;|$)');
+  result.count      := substring(repeatrule from 'COUNT=([0-9]+)(;|$)');
+  result.interval   := COALESCE(substring(repeatrule from 'INTERVAL=([0-9]+)(;|$)')::int, 1);
+  result.wkst       := substring(repeatrule from 'WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)');
+
+  result.byday      := string_to_array( substring(repeatrule from 'BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)'), ',');
+
+  result.byyearday  := string_to_array(substring(repeatrule from 'BYYEARDAY=([0-9,+-]+)(;|$)'), ',');
+  result.byweekno   := string_to_array(substring(repeatrule from 'BYWEEKNO=([0-9,+-]+)(;|$)'), ',');
+  result.bymonthday := string_to_array(substring(repeatrule from 'BYMONTHDAY=([0-9,+-]+)(;|$)'), ',');
+  result.bymonth    := string_to_array(substring(repeatrule from 'BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)'), ',');
+  result.bysetpos   := string_to_array(substring(repeatrule from 'BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)'), ',');
+
+  result.bysecond   := string_to_array(substring(repeatrule from 'BYSECOND=([0-9,]+)(;|$)'), ',');
+  result.byminute   := string_to_array(substring(repeatrule from 'BYMINUTE=([0-9,]+)(;|$)'), ',');
+  result.byhour     := string_to_array(substring(repeatrule from 'BYHOUR=([0-9,]+)(;|$)'), ',');
+
+  RETURN result;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
+CREATE or REPLACE FUNCTION rrule_month_byday_set( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  byday ALIAS FOR $2;
+  dayrule TEXT;
+  i INT;
+  dow INT;
+  index INT;
+  first_dow INT;
+  each_day TIMESTAMP WITH TIME ZONE;
+  this_month INT;
+  results TIMESTAMP WITH TIME ZONE[];
+BEGIN
+
+  IF byday IS NULL THEN
+    -- We still return the single date as a SET
+    RETURN NEXT in_time;
+    RETURN;
+  END IF;
+
+  i := 1;
+  dayrule := byday[i];
+  WHILE dayrule IS NOT NULL LOOP
+    dow := position(substring( dayrule from '..$') in 'SUMOTUWETHFRSA') / 2;
+    each_day := date_trunc( 'month', in_time ) + (in_time::time)::interval;
+    this_month := date_part( 'month', in_time );
+    first_dow := date_part( 'dow', each_day );
+
+    -- Coerce each_day to be the first 'dow' of the month
+    each_day := each_day - ( first_dow::text || 'days')::interval
+                        + ( dow::text || 'days')::interval
+                        + CASE WHEN dow < first_dow THEN '1 week'::interval ELSE '0s'::interval END;
+
+    -- RAISE NOTICE 'From "%", for % finding dates. dow=%, this_month=%, first_dow=%', each_day, dayrule, dow, this_month, first_dow;
+    IF length(dayrule) > 2 THEN
+      index := (substring(dayrule from '^[0-9-]+'))::int;
+
+      IF index = 0 THEN
+        RAISE NOTICE 'Ignored invalid BYDAY rule part "%".', bydayrule;
+      ELSIF index > 0 THEN
+        -- The simplest case, such as 2MO for the second monday
+        each_day := each_day + ((index - 1)::text || ' weeks')::interval;
+      ELSE
+        each_day := each_day + '5 weeks'::interval;
+        WHILE date_part('month', each_day) != this_month LOOP
+          each_day := each_day - '1 week'::interval;
+        END LOOP;
+        -- Note that since index is negative, (-2 + 1) == -1, for example
+        index := index + 1;
+        IF index < 0 THEN
+          each_day := each_day + (index::text || ' weeks')::interval ;
+        END IF;
+      END IF;
+
+      -- Sometimes (e.g. 5TU or -5WE) there might be no such date in some months
+      IF date_part('month', each_day) = this_month THEN
+        results[date_part('day',each_day)] := each_day;
+        -- RAISE NOTICE 'Added "%" to list for %', each_day, dayrule;
+      END IF;
+
+    ELSE
+      -- Return all such days that are within the given month
+      WHILE date_part('month', each_day) = this_month LOOP
+        results[date_part('day',each_day)] := each_day;
+        each_day := each_day + '1 week'::interval;
+        -- RAISE NOTICE 'Added "%" to list for %', each_day, dayrule;
+      END LOOP;
+    END IF;
+
+    i := i + 1;
+    dayrule := byday[i];
+  END LOOP;
+
+  FOR i IN 1..31 LOOP
+    IF results[i] IS NOT NULL THEN
+      RETURN NEXT results[i];
+    END IF;
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
+CREATE or REPLACE FUNCTION rrule_month_bymonthday_set( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  bymonthday ALIAS FOR $2;
+  month_start TIMESTAMP WITH TIME ZONE;
+  daysinmonth INT;
+  i INT;
+BEGIN
+
+  month_start := date_trunc( 'month', in_time ) + (in_time::time)::interval;
+  daysinmonth := date_part( 'days', (month_start + interval '1 month') - interval '1 day' );
+
+  FOR i IN 1..31 LOOP
+    EXIT WHEN bymonthday[i] IS NULL;
+
+    CONTINUE WHEN bymonthday[i] > daysinmonth;
+    CONTINUE WHEN bymonthday[i] < (-1 * daysinmonth);
+
+    IF bymonthday[i] > 0 THEN
+      RETURN NEXT month_start + ((bymonthday[i] - 1)::text || 'days')::interval;
+    ELSIF bymonthday[i] < 0 THEN
+      RETURN NEXT month_start + ((daysinmonth + bymonthday[i])::text || 'days')::interval;
+    ELSE
+      RAISE NOTICE 'Ignored invalid BYMONTHDAY part "%".', bymonthday[i];
+    END IF;
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Return a SETOF dates within the week of a particular date which match a single BYDAY rule specification
+CREATE or REPLACE FUNCTION rrule_week_byday_set( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  byday ALIAS FOR $2;
+  dayrule TEXT;
+  dow INT;
+  our_day TIMESTAMP WITH TIME ZONE;
+  i INT;
+BEGIN
+
+  IF byday IS NULL THEN
+    -- We still return the single date as a SET
+    RETURN NEXT in_time;
+    RETURN;
+  END IF;
+
+  our_day := date_trunc( 'week', in_time ) + (in_time::time)::interval;
+
+  i := 1;
+  dayrule := byday[i];
+  WHILE dayrule IS NOT NULL LOOP
+    dow := position(dayrule in 'SUMOTUWETHFRSA') / 2;
+    RETURN NEXT our_day + ((dow - 1)::text || 'days')::interval;
+    i := i + 1;
+    dayrule := byday[i];
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+CREATE or REPLACE FUNCTION event_has_exceptions( TEXT ) RETURNS BOOLEAN AS $$
+  SELECT $1 ~ E'\nRECURRENCE-ID(;TZID=[^:]+)?:[[:space:]]*[[:digit:]]{8}(T[[:digit:]]{6})?'
+$$ LANGUAGE 'sql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the weekday of this date against the array of weekdays from the BYDAY rule (FREQ=WEEKLY or less)
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_byday_rule( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  byday ALIAS FOR $2;
+BEGIN
+  -- Note that this doesn't work for MONTHLY/YEARLY BYDAY clauses which might have numbers prepended
+  -- so don't call it that way...
+  IF byday IS NOT NULL THEN
+    RETURN ( substring( to_char( testme, 'DY') for 2 from 1) = ANY (byday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the month of this date against the array of months from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_bymonth_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  bymonth ALIAS FOR $2;
+BEGIN
+  IF bymonth IS NOT NULL THEN
+    RETURN ( date_part( 'month', testme) = ANY (bymonth) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the day in month of this date against the array of monthdays from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_bymonthday_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  bymonthday ALIAS FOR $2;
+BEGIN
+  IF bymonthday IS NOT NULL THEN
+    RETURN ( date_part( 'day', testme) = ANY (bymonthday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the day in year of this date against the array of yeardays from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_byyearday_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  byyearday ALIAS FOR $2;
+BEGIN
+  IF byyearday IS NOT NULL THEN
+    RETURN ( date_part( 'doy', testme) = ANY (byyearday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Given a cursor into a set, process the set returning the subset matching the BYSETPOS
+--
+-- Note that this function *requires* PostgreSQL 8.3 or later for the cursor handling syntax
+-- to work.  I guess we could do it with an array, instead, for compatibility with earlier
+-- releases, since there's a maximum of 366 positions in a set.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_bysetpos_filter( REFCURSOR, INT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  curse ALIAS FOR $1;
+  bysetpos ALIAS FOR $2;
+  valid_date TIMESTAMP WITH TIME ZONE;
+  i INT;
+BEGIN
+
+  IF bysetpos IS NULL THEN
+    LOOP
+      FETCH curse INTO valid_date;
+      EXIT WHEN NOT FOUND;
+      RETURN NEXT valid_date;
+    END LOOP;
+  ELSE
+    FOR i IN 1..366 LOOP
+      EXIT WHEN bysetpos[i] IS NULL;
+      IF bysetpos[i] > 0 THEN
+        FETCH ABSOLUTE bysetpos[i] FROM curse INTO valid_date;
+      ELSE
+        MOVE LAST IN curse;
+        FETCH RELATIVE (bysetpos[i] + 1) FROM curse INTO valid_date;
+      END IF;
+      IF valid_date IS NOT NULL THEN
+        RETURN NEXT valid_date;
+      END IF;
+    END LOOP;
+  END IF;
+  CLOSE curse;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another day's worth of events: i.e. one day that matches the criteria, since we don't
+-- currently implement sub-day scheduling.
+--
+-- This is cheeky:  The incrementing by a day is done outside the call, so we either return the
+-- empty set (if the input date fails our filters) or we return a set containing the input date.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION daily_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL AND NOT date_part('month',after) = ANY ( rrule.bymonth ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byweekno IS NOT NULL AND NOT date_part('week',after) = ANY ( rrule.byweekno ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byyearday IS NOT NULL AND NOT date_part('doy',after) = ANY ( rrule.byyearday ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.bymonthday IS NOT NULL AND NOT date_part('day',after) = ANY ( rrule.bymonthday ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byday IS NOT NULL AND NOT substring( to_char( after, 'DY') for 2 from 1) = ANY ( rrule.byday ) THEN
+    RETURN;
+  END IF;
+
+  -- Since we don't do BYHOUR, BYMINUTE or BYSECOND yet this becomes a trivial
+  RETURN NEXT after;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another week's worth of events
+--
+-- Doesn't handle truly obscure and unlikely stuff like BYWEEKNO=5;BYMONTH=1;BYDAY=WE,TH,FR;BYSETPOS=-2
+-- Imagine that.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION weekly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  valid_date TIMESTAMP WITH TIME ZONE;
+  curse REFCURSOR;
+  weekno INT;
+  i INT;
+BEGIN
+
+  IF rrule.byweekno IS NOT NULL THEN
+    weekno := date_part('week',after);
+    IF NOT weekno = ANY ( rrule.byweekno ) THEN
+      RETURN;
+    END IF;
+  END IF;
+
+  OPEN curse SCROLL FOR SELECT r FROM rrule_week_byday_set(after, rrule.byday ) r;
+  RETURN QUERY SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another month's worth of events
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION monthly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  valid_date TIMESTAMP WITH TIME ZONE;
+  curse REFCURSOR;
+  setpos INT;
+  i INT;
+BEGIN
+
+  /**
+  * Need to investigate whether it is legal to set both of these, and whether
+  * we are correct to UNION the results, or whether we should INTERSECT them.
+  * So at this point, we refer to the specification, which grants us this
+  * wonderfully enlightening vision:
+  *
+  *     If multiple BYxxx rule parts are specified, then after evaluating the
+  *     specified FREQ and INTERVAL rule parts, the BYxxx rule parts are
+  *     applied to the current set of evaluated occurrences in the following
+  *     order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR,
+  *     BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.
+  *
+  * My guess is that this means 'INTERSECT'
+  */
+  IF rrule.byday IS NOT NULL AND rrule.bymonthday IS NOT NULL THEN
+    OPEN curse SCROLL FOR SELECT r FROM rrule_month_byday_set(after, rrule.byday ) r
+                INTERSECT SELECT r FROM rrule_month_bymonthday_set(after, rrule.bymonthday ) r
+                    ORDER BY 1;
+  ELSIF rrule.bymonthday IS NOT NULL THEN
+    OPEN curse SCROLL FOR SELECT r FROM rrule_month_bymonthday_set(after, rrule.bymonthday ) r ORDER BY 1;
+  ELSE
+    OPEN curse SCROLL FOR SELECT r FROM rrule_month_byday_set(after, rrule.byday ) r ORDER BY 1;
+  END IF;
+
+  RETURN QUERY SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- If this is YEARLY;BYMONTH, abuse MONTHLY;BYMONTH for everything except the BYSETPOS
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_yearly_bymonth_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  current_base TIMESTAMP WITH TIME ZONE;
+  rr rrule_parts;
+  i INT;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL THEN
+    -- Ensure we don't pass BYSETPOS down
+    rr := rrule;
+    rr.bysetpos := NULL;
+    FOR i IN 1..12 LOOP
+      EXIT WHEN rr.bymonth[i] IS NULL;
+      current_base := date_trunc( 'year', after ) + ((rr.bymonth[i] - 1)::text || ' months')::interval + (after::time)::interval;
+      RETURN QUERY SELECT r FROM monthly_set(current_base,rr) r;
+    END LOOP;
+  ELSE
+    -- We don't yet implement byweekno, byblah
+    RETURN NEXT after;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another year's worth of events
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION yearly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  current_base TIMESTAMP WITH TIME ZONE;
+  curse REFCURSOR;
+  curser REFCURSOR;
+  i INT;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL THEN
+    OPEN curse SCROLL FOR SELECT r FROM rrule_yearly_bymonth_set(after, rrule ) r;
+    FOR current_base IN SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d LOOP
+      current_base := date_trunc( 'day', current_base ) + (after::time)::interval;
+      RETURN NEXT current_base;
+    END LOOP;
+  ELSE
+    -- We don't yet implement byweekno, byblah
+    RETURN NEXT after;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Combine all of that into something which we can use to generate a series from an arbitrary DTSTART/RRULE
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_event_instances_range( TIMESTAMP WITH TIME ZONE, TEXT, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE, INT )
+                                         RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  basedate ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  mindate ALIAS FOR $3;
+  maxdate ALIAS FOR $4;
+  max_count ALIAS FOR $5;
+  loopmax INT;
+  loopcount INT;
+  base_day TIMESTAMP WITH TIME ZONE;
+  current_base TIMESTAMP WITH TIME ZONE;
+  current TIMESTAMP WITH TIME ZONE;
+  rrule rrule_parts%ROWTYPE;
+BEGIN
+  loopcount := 0;
+
+  SELECT * INTO rrule FROM parse_rrule_parts( basedate, repeatrule );
+
+  IF rrule.count IS NOT NULL THEN
+    loopmax := rrule.count;
+  ELSE
+    -- max_count is pretty arbitrary, so we scale it somewhat here depending on the frequency.
+    IF rrule.freq = 'DAILY' THEN
+      loopmax := max_count * 20;
+    ELSIF rrule.freq = 'WEEKLY' THEN
+      loopmax := max_count * 10;
+    ELSE
+      loopmax := max_count;
+    END IF;
+  END IF;
+
+  current_base := basedate;
+  base_day := date_trunc('day',basedate);
+  WHILE loopcount < loopmax AND current_base <= maxdate LOOP
+    IF rrule.freq = 'DAILY' THEN
+      FOR current IN SELECT d FROM daily_set(current_base,rrule) d WHERE d >= base_day LOOP
+--        IF test_byday_rule(current,rrule.byday) AND test_bymonthday_rule(current,rrule.bymonthday) AND test_bymonth_rule(current,rrule.bymonth) THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+--        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' days')::interval;
+    ELSIF rrule.freq = 'WEEKLY' THEN
+      FOR current IN SELECT w FROM weekly_set(current_base,rrule) w WHERE w >= base_day LOOP
+        IF test_byyearday_rule(current,rrule.byyearday)
+               AND test_bymonthday_rule(current,rrule.bymonthday)
+               AND test_bymonth_rule(current,rrule.bymonth)
+        THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' weeks')::interval;
+    ELSIF rrule.freq = 'MONTHLY' THEN
+      FOR current IN SELECT m FROM monthly_set(current_base,rrule) m WHERE m >= base_day LOOP
+--        IF /* test_byyearday_rule(current,rrule.byyearday)
+--               AND */ test_bymonth_rule(current,rrule.bymonth)
+--        THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+--        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' months')::interval;
+    ELSIF rrule.freq = 'YEARLY' THEN
+      FOR current IN SELECT y FROM yearly_set(current_base,rrule) y WHERE y >= base_day LOOP
+        EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+        IF current >= mindate THEN
+          RETURN NEXT current;
+        END IF;
+        loopcount := loopcount + 1;
+        EXIT WHEN loopcount >= loopmax;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' years')::interval;
+    ELSE
+      RAISE NOTICE 'A frequency of "%" is not handled', rrule.freq;
+      RETURN;
+    END IF;
+    EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+  END LOOP;
+  -- RETURN QUERY;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- A simplified DTSTART/RRULE only interface which applies some performance assumptions
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION event_instances( TIMESTAMP WITH TIME ZONE, TEXT )
+                                         RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  basedate ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  maxdate TIMESTAMP WITH TIME ZONE;
+BEGIN
+  maxdate := current_date + '10 years'::interval;
+  RETURN QUERY SELECT d FROM rrule_event_instances_range( basedate, repeatrule, basedate, maxdate, 300 ) d;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- In most cases we just want to know if there *is* an event overlapping the range, so we have a
+-- specific function for that.  Note that this is *not* strict, and can be called with NULLs.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_event_overlaps( TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE, TEXT, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE )
+                                         RETURNS BOOLEAN AS $$
+DECLARE
+  dtstart ALIAS FOR $1;
+  dtend ALIAS FOR $2;
+  repeatrule ALIAS FOR $3;
+  in_mindate ALIAS FOR $4;
+  in_maxdate ALIAS FOR $5;
+  base_date TIMESTAMP WITH TIME ZONE;
+  mindate TIMESTAMP WITH TIME ZONE;
+  maxdate TIMESTAMP WITH TIME ZONE;
+BEGIN
+
+  IF dtstart IS NULL THEN
+    RETURN NULL;
+  END IF;
+  IF dtend IS NULL THEN
+    base_date := dtstart;
+  ELSE
+    base_date := dtend;
+  END IF;
+
+  IF in_mindate IS NULL THEN
+    mindate := current_date - '10 years'::interval;
+  ELSE
+    mindate := in_mindate;
+  END IF;
+
+  IF in_maxdate IS NULL THEN
+    maxdate := current_date + '10 years'::interval;
+  ELSE
+    -- If we add the duration onto the event, then an overlap occurs if dtend <= increased end of range.
+    maxdate := in_maxdate + (base_date - dtstart);
+  END IF;
+
+  IF repeatrule IS NULL THEN
+    RETURN (dtstart <= maxdate AND base_date >= mindate);
+  END IF;
+
+  SELECT d INTO mindate FROM rrule_event_instances_range( base_date, repeatrule, mindate, maxdate, 60 ) d LIMIT 1;
+  RETURN FOUND;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+-- Create a composite type for the parts of the RRULE.
+DROP TYPE rrule_instance CASCADE;
+CREATE TYPE rrule_instance AS (
+  dtstart TIMESTAMP WITH TIME ZONE,
+  rrule TEXT,
+  instance TIMESTAMP WITH TIME ZONE
+);
+
+CREATE or REPLACE FUNCTION rrule_event_instances( TIMESTAMP WITH TIME ZONE, TEXT )
+                                         RETURNS SETOF rrule_instance AS $$
+DECLARE
+  basedate ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  maxdate TIMESTAMP WITH TIME ZONE;
+  current TIMESTAMP WITH TIME ZONE;
+  result rrule_instance%ROWTYPE;
+BEGIN
+  maxdate := current_date + '10 years'::interval;
+
+  result.dtstart := basedate;
+  result.rrule   := repeatrule;
+
+  FOR current IN SELECT d FROM rrule_event_instances_range( basedate, repeatrule, basedate, maxdate, 300 ) d LOOP
+    result.instance := current;
+    RETURN NEXT result;
+  END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION icalendar_interval_to_SQL( TEXT ) RETURNS interval AS $function$
+  SELECT CASE WHEN substring($1,1,1) = '-' THEN -1 ELSE 1 END * regexp_replace( regexp_replace($1, '[PT-]', '', 'g'), '([A-Z])', E'\\1 ', 'g')::interval;
+$function$ LANGUAGE 'SQL' IMMUTABLE STRICT;
+
Index: contrib/davical/dba/caldav_functions.sql
===================================================================
--- contrib/davical/dba/caldav_functions.sql	(revision 3733)
+++ contrib/davical/dba/caldav_functions.sql	(revision 3733)
@@ -0,0 +1,1245 @@
+/**
+* PostgreSQL Functions for CalDAV handling
+*
+* @package rscds
+* @subpackage database
+* @author Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst IT Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+CREATE or REPLACE FUNCTION apply_month_byday( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  byday ALIAS FOR $2;
+  weeks INT;
+  dow INT;
+  temp_txt TEXT;
+  dd INT;
+  mm INT;
+  yy INT;
+  our_dow INT;
+  our_answer TIMESTAMP WITH TIME ZONE;
+BEGIN
+  dow := position(substring( byday from '..$') in 'SUMOTUWETHFRSA') / 2;
+  temp_txt   := substring(byday from '([0-9]+)');
+  weeks      := temp_txt::int;
+
+  -- RAISE NOTICE 'DOW: %, Weeks: %(%s)', dow, weeks, temp_txt;
+
+  IF substring(byday for 1) = '-' THEN
+    -- Last XX of month, or possibly second-to-last, but unlikely
+    mm := extract( 'month' from in_time);
+    yy := extract( 'year' from in_time);
+
+    -- Start with the last day of the month
+    our_answer := (yy::text || '-' || (mm+1)::text || '-01')::timestamp - '1 day'::interval;
+    dd := extract( 'dow' from our_answer);
+    dd := dd - dow;
+    IF dd < 0 THEN
+      dd := dd + 7;
+    END IF;
+
+    -- Having calculated the right day of the month, we now apply that back to in_time
+    -- which contains the otherwise-unobtainable timezone detail (and the time)
+    our_answer = our_answer - (dd::text || 'days')::interval;
+    dd := extract( 'day' from our_answer) - extract( 'day' from in_time);
+    our_answer := in_time + (dd::text || 'days')::interval;
+
+    IF weeks > 1 THEN
+      weeks := weeks - 1;
+      our_answer := our_answer - (weeks::text || 'weeks')::interval;
+    END IF;
+
+  ELSE
+
+    -- Shift our date to the correct day of week..
+    our_dow := extract( 'dow' from in_time);
+    our_dow := our_dow - dow;
+    dd := extract( 'day' from in_time);
+    IF our_dow >= dd THEN
+      our_dow := our_dow - 7;
+    END IF;
+    our_answer := in_time - (our_dow::text || 'days')::interval;
+    dd = extract( 'day' from our_answer);
+
+    -- Shift the date to the correct week...
+    dd := weeks - ((dd+6) / 7);
+    IF dd != 0 THEN
+      our_answer := our_answer + ((dd::text || 'weeks')::interval);
+    END IF;
+
+  END IF;
+
+  RETURN our_answer;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION calculate_later_timestamp( TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  earliest ALIAS FOR $1;
+  basedate ALIAS FOR $2;
+  repeatrule ALIAS FOR $3;
+  frequency TEXT;
+  temp_txt TEXT;
+  length INT;
+  count INT;
+  byday TEXT;
+  bymonthday INT;
+  basediff INTERVAL;
+  past_repeats INT8;
+  units TEXT;
+  dow TEXT;
+  our_answer TIMESTAMP WITH TIME ZONE;
+  loopcount INT;
+BEGIN
+  IF basedate > earliest THEN
+    RETURN basedate;
+  END IF;
+
+  temp_txt   := substring(repeatrule from 'UNTIL=([0-9TZ]+)(;|$)');
+  IF temp_txt IS NOT NULL AND temp_txt::timestamp with time zone < earliest THEN
+    RETURN NULL;
+  END IF;
+
+  frequency  := substring(repeatrule from 'FREQ=([A-Z]+)(;|$)');
+  IF frequency IS NULL THEN
+    RETURN NULL;
+  END IF;
+
+  past_repeats = 0;
+  length = 1;
+  temp_txt   := substring(repeatrule from 'INTERVAL=([0-9]+)(;|$)');
+  IF temp_txt IS NOT NULL THEN
+    length     := temp_txt::int;
+    basediff   := earliest - basedate;
+
+    -- RAISE NOTICE 'Frequency: %, Length: %(%), Basediff: %', frequency, length, temp_txt, basediff;
+
+    -- Calculate the number of past periods between our base date and our earliest date
+    IF frequency = 'WEEKLY' OR frequency = 'DAILY' THEN
+      past_repeats := extract('epoch' from basediff)::INT8 / 86400;
+      -- RAISE NOTICE 'Days: %', past_repeats;
+      IF frequency = 'WEEKLY' THEN
+        past_repeats := past_repeats / 7;
+      END IF;
+    ELSE
+      past_repeats = extract( 'years' from basediff );
+      IF frequency = 'MONTHLY' THEN
+        past_repeats = (past_repeats *12) + extract( 'months' from basediff );
+      END IF;
+    END IF;
+    IF length IS NOT NULL THEN
+      past_repeats = (past_repeats / length) + 1;
+    END IF;
+  END IF;
+
+  -- Check that we have not exceeded the COUNT= limit
+  temp_txt := substring(repeatrule from 'COUNT=([0-9]+)(;|$)');
+  IF temp_txt IS NOT NULL THEN
+    count := temp_txt::int;
+    -- RAISE NOTICE 'Periods: %, Count: %(%), length: %', past_repeats, count, temp_txt, length;
+    IF ( count <= past_repeats ) THEN
+      RETURN NULL;
+    END IF;
+  ELSE
+    count := NULL;
+  END IF;
+
+  temp_txt := substring(repeatrule from 'BYSETPOS=([0-9-]+)(;|$)');
+  byday := substring(repeatrule from 'BYDAY=([0-9A-Z,]+-)(;|$)');
+  IF byday IS NOT NULL AND frequency = 'MONTHLY' THEN
+    -- Since this could move the date around a month we go back one
+    -- period just to be extra sure.
+    past_repeats = past_repeats - 1;
+
+    IF temp_txt IS NOT NULL THEN
+      -- Crudely hack the BYSETPOS onto the front of BYDAY.  While this
+      -- is not as per rfc2445, RRULE syntax is so complex and overblown
+      -- that nobody correctly uses comma-separated BYDAY or BYSETPOS, and
+      -- certainly not within a MONTHLY RRULE.
+      byday := temp_txt || byday;
+    END IF;
+  END IF;
+
+  past_repeats = past_repeats * length;
+
+  units := CASE
+    WHEN frequency = 'DAILY' THEN 'days'
+    WHEN frequency = 'WEEKLY' THEN 'weeks'
+    WHEN frequency = 'MONTHLY' THEN 'months'
+    WHEN frequency = 'YEARLY' THEN 'years'
+  END;
+
+  temp_txt   := substring(repeatrule from 'BYMONTHDAY=([0-9,]+)(;|$)');
+  bymonthday := temp_txt::int;
+
+  -- With all of the above calculation, this date should be close to (but less than)
+  -- the target, and we should only loop once or twice.
+  our_answer := basedate + (past_repeats::text || units)::interval;
+
+  IF our_answer IS NULL THEN
+    RAISE EXCEPTION 'our_answer IS NULL! basedate:% past_repeats:% units:%', basedate, past_repeats, units;
+  END IF;
+
+
+  loopcount := 500;  -- Desirable to stop an infinite loop if there is something we cannot handle
+  LOOP
+    -- RAISE NOTICE 'Testing date: %', our_answer;
+    IF frequency = 'DAILY' THEN
+      IF byday IS NOT NULL THEN
+        LOOP
+          dow = substring( to_char( our_answer, 'DY' ) for 2);
+          EXIT WHEN byday ~* dow;
+          -- Increment for our next time through the loop...
+          our_answer := our_answer + (length::text || units)::interval;
+        END LOOP;
+      END IF;
+    ELSIF frequency = 'WEEKLY' THEN
+      -- Weekly repeats are only on specific days
+      -- This is really not right, since a WEEKLY on MO,WE,FR should
+      -- occur three times each week and this will only be once a week.
+      dow = substring( to_char( our_answer, 'DY' ) for 2);
+    ELSIF frequency = 'MONTHLY' THEN
+      IF byday IS NOT NULL THEN
+        -- This works fine, except that maybe there are multiple BYDAY
+        -- components.  e.g. 1TU,3TU might be 1st & 3rd tuesdays.
+        our_answer := apply_month_byday( our_answer, byday );
+      ELSE
+        -- If we did not get a BYDAY= then we kind of have to assume it is the same day each month
+        our_answer := our_answer + '1 month'::interval;
+      END IF;
+    ELSIF bymonthday IS NOT NULL AND frequency = 'MONTHLY' AND bymonthday < 1 THEN
+      -- We do not deal with this situation at present
+      RAISE NOTICE 'The case of negative BYMONTHDAY is not handled yet.';
+    END IF;
+
+    EXIT WHEN our_answer >= earliest;
+
+    -- Give up if we have exceeded the count
+    IF ( count IS NOT NULL AND past_repeats > count ) THEN
+      RETURN NULL;
+    ELSE
+      past_repeats := past_repeats + 1;
+    END IF;
+
+    loopcount := loopcount - 1;
+    IF loopcount < 0 THEN
+      RAISE NOTICE 'Giving up on repeat rule "%" - after 100 increments from % we are still not after %', repeatrule, basedate, earliest;
+      RETURN NULL;
+    END IF;
+
+    -- Increment for our next time through the loop...
+    our_answer := our_answer + (length::text || units)::interval;
+
+  END LOOP;
+
+  RETURN our_answer;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION usr_is_role( INT, TEXT ) RETURNS BOOLEAN AS $$
+  SELECT EXISTS( SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_member.user_no=$1 AND roles.role_name=$2 )
+$$ LANGUAGE 'sql' IMMUTABLE STRICT;
+
+CREATE or REPLACE FUNCTION legacy_get_permissions( INT, INT ) RETURNS TEXT AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_to   ALIAS FOR $2;
+  out_confers TEXT;
+  tmp_confers1 TEXT;
+  tmp_confers2 TEXT;
+  tmp_txt TEXT;
+  dbg TEXT DEFAULT '';
+  r RECORD;
+  counter INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN 'A';
+  END IF;
+
+  -- dbg := 'S-';
+  SELECT rt1.confers INTO out_confers FROM relationship r1 JOIN relationship_type rt1 USING ( rt_id )
+                    WHERE r1.from_user = in_from AND r1.to_user = in_to AND NOT usr_is_role(r1.to_user,'Group');
+  IF FOUND THEN
+    RETURN dbg || out_confers;
+  END IF;
+  -- RAISE NOTICE 'No simple relationships between % and %', in_from, in_to;
+
+  out_confers := '';
+  FOR r IN SELECT rt1.confers AS r1, rt2.confers AS r2 FROM relationship r1 JOIN relationship_type rt1 USING(rt_id)
+              JOIN relationship r2 ON r1.to_user=r2.from_user JOIN relationship_type rt2 ON r2.rt_id=rt2.rt_id
+         WHERE r1.from_user=in_from AND r2.to_user=in_to
+           AND EXISTS( SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_member.user_no=r1.to_user AND roles.role_name='Group')
+           AND NOT EXISTS( SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_member.user_no=r2.to_user AND roles.role_name='Group')
+           AND NOT EXISTS( SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_member.user_no=r1.from_user AND roles.role_name='Group')
+  LOOP
+    -- RAISE NOTICE 'Permissions to group % from group %', r.r1, r.r2;
+    -- FIXME: This is an oversimplification
+    -- dbg := 'C-';
+    tmp_confers1 := r.r1;
+    tmp_confers2 := r.r2;
+    IF tmp_confers1 != tmp_confers2 THEN
+      IF tmp_confers1 ~* 'A' THEN
+        -- Ensure that A is expanded to all supported privs before being used as a mask
+        tmp_confers1 := 'AFBRWU';
+      END IF;
+      IF tmp_confers2 ~* 'A' THEN
+        -- Ensure that A is expanded to all supported privs before being used as a mask
+        tmp_confers2 := 'AFBRWU';
+      END IF;
+      -- RAISE NOTICE 'Expanded permissions to group % from group %', tmp_confers1, tmp_confers2;
+      tmp_txt = '';
+      FOR counter IN 1 .. length(tmp_confers2) LOOP
+        IF tmp_confers1 ~* substring(tmp_confers2,counter,1) THEN
+          tmp_txt := tmp_txt || substring(tmp_confers2,counter,1);
+        END IF;
+      END LOOP;
+      tmp_confers2 := tmp_txt;
+    END IF;
+    FOR counter IN 1 .. length(tmp_confers2) LOOP
+      IF NOT out_confers ~* substring(tmp_confers2,counter,1) THEN
+        out_confers := out_confers || substring(tmp_confers2,counter,1);
+      END IF;
+    END LOOP;
+  END LOOP;
+  IF out_confers ~* 'A' OR (out_confers ~* 'B' AND out_confers ~* 'F' AND out_confers ~* 'R' AND out_confers ~* 'W' AND out_confers ~* 'U') THEN
+    out_confers := 'A';
+  END IF;
+  IF out_confers != '' THEN
+    RETURN dbg || out_confers;
+  END IF;
+
+  -- RAISE NOTICE 'No complex relationships between % and %', in_from, in_to;
+
+  SELECT rt1.confers INTO out_confers, tmp_confers1 FROM relationship r1 JOIN relationship_type rt1 ON ( r1.rt_id = rt1.rt_id )
+              LEFT OUTER JOIN relationship r2 ON ( rt1.rt_id = r2.rt_id )
+       WHERE r1.from_user = in_from AND r2.from_user = in_to AND r1.from_user != r2.from_user AND r1.to_user = r2.to_user
+         AND NOT EXISTS( SELECT 1 FROM relationship r3 WHERE r3.from_user = r1.to_user )
+          AND usr_is_role(r1.to_user,'Group');
+
+  IF FOUND THEN
+    -- dbg := 'H-';
+    -- RAISE NOTICE 'Permissions to shared group % ', out_confers;
+    RETURN dbg || out_confers;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN '';
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Function to convert a PostgreSQL date into UTC + the format used by iCalendar
+CREATE or REPLACE FUNCTION to_ical_utc( TIMESTAMP WITH TIME ZONE ) RETURNS TEXT AS $$
+  SELECT to_char( $1 at time zone 'UTC', 'YYYYMMDD"T"HH24MISS"Z"' )
+$$ LANGUAGE 'sql' IMMUTABLE STRICT;
+
+-- Function to set an arbitrary DAV property
+CREATE or REPLACE FUNCTION set_dav_property( TEXT, INTEGER, TEXT, TEXT ) RETURNS BOOLEAN AS $$
+DECLARE
+  path ALIAS FOR $1;
+  user ALIAS FOR $2;
+  key ALIAS FOR $3;
+  value ALIAS FOR $4;
+  tmp_int INT;
+BEGIN
+  -- Check that there is either a resource, collection or user at this location.
+  IF NOT EXISTS(        SELECT 1 FROM caldav_data WHERE dav_name = path
+                  UNION SELECT 1 FROM collection WHERE dav_name = path
+                  UNION SELECT 1 FROM dav_principal WHERE dav_name = path
+                  UNION SELECT 1 FROM dav_binding WHERE dav_name = path
+               ) THEN
+    RETURN FALSE;
+  END IF;
+  SELECT changed_by INTO tmp_int FROM property WHERE dav_name = path AND property_name = key;
+  IF FOUND THEN
+    UPDATE property SET changed_by=user, changed_on=current_timestamp, property_value=value WHERE dav_name = path AND property_name = key;
+  ELSE
+    INSERT INTO property ( dav_name, changed_by, changed_on, property_name, property_value ) VALUES( path, user, current_timestamp, key, value );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' STRICT;
+
+-- List a user's relationships as a text string
+CREATE or REPLACE FUNCTION relationship_list( INT8 ) RETURNS TEXT AS $$
+DECLARE
+  user ALIAS FOR $1;
+  r RECORD;
+  rlist TEXT;
+BEGIN
+  rlist := '';
+  FOR r IN SELECT rt_name, fullname FROM relationship
+                          LEFT JOIN relationship_type USING(rt_id) LEFT JOIN usr tgt ON to_user = tgt.user_no
+                          WHERE from_user = user
+  LOOP
+    rlist := rlist
+             || CASE WHEN rlist = '' THEN '' ELSE ', ' END
+             || r.rt_name || '(' || r.fullname || ')';
+  END LOOP;
+  RETURN rlist;
+END;
+$$ LANGUAGE 'plpgsql';
+
+DROP FUNCTION rename_davical_user( TEXT, TEXT );
+DROP TRIGGER usr_modified ON usr CASCADE;
+CREATE or REPLACE FUNCTION usr_modified() RETURNS TRIGGER AS $$
+DECLARE
+  oldpath TEXT;
+  newpath TEXT;
+BEGIN
+  -- in case we trigger on other events in future
+  IF TG_OP = 'UPDATE' THEN
+    IF NEW.username != OLD.username THEN
+      oldpath := '/' || OLD.username || '/';
+      newpath := '/' || NEW.username || '/';
+      UPDATE collection
+        SET parent_container = replace( parent_container, oldpath, newpath),
+            dav_name = replace( dav_name, oldpath, newpath)
+      WHERE substring(dav_name from 1 for char_length(oldpath)) = oldpath;
+    END IF;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER usr_modified AFTER UPDATE ON usr
+    FOR EACH ROW EXECUTE PROCEDURE usr_modified();
+
+
+DROP TRIGGER collection_modified ON collection CASCADE;
+CREATE or REPLACE FUNCTION collection_modified() RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+  -- in case we trigger on other events in future
+  IF TG_OP = 'UPDATE' THEN
+    IF NEW.dav_name != OLD.dav_name THEN
+      UPDATE caldav_data
+        SET dav_name = replace( dav_name, OLD.dav_name, NEW.dav_name),
+            user_no = NEW.user_no
+      WHERE substring(dav_name from 1 for char_length(OLD.dav_name)) = OLD.dav_name;
+    END IF;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER collection_modified AFTER UPDATE ON collection
+    FOR EACH ROW EXECUTE PROCEDURE collection_modified();
+
+
+DROP TRIGGER caldav_data_modified ON caldav_data CASCADE;
+CREATE or REPLACE FUNCTION caldav_data_modified() RETURNS TRIGGER AS $$
+DECLARE
+  coll_id caldav_data.collection_id%TYPE;
+BEGIN
+  IF TG_OP = 'UPDATE' THEN
+    IF NEW.caldav_data = OLD.caldav_data AND NEW.collection_id = OLD.collection_id THEN
+      -- Nothing for us to do
+      RETURN NEW;
+    END IF;
+  END IF;
+
+  IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
+    -- On insert or update modified, we set the NEW collection tag to the md5 of the
+    -- etag of the updated row which gives us something predictable for our regression
+    -- tests, but something different from the actual etag of the new event.
+    UPDATE collection
+       SET modified = current_timestamp, dav_etag = md5(NEW.dav_etag)
+     WHERE collection_id = NEW.collection_id;
+    IF TG_OP = 'INSERT' THEN
+      RETURN NEW;
+    END IF;
+  END IF;
+
+  IF TG_OP = 'DELETE' THEN
+    -- On delete we set the OLD collection tag to the md5 of the old path & the old
+    -- etag, which again gives us something predictable for our regression tests.
+    UPDATE collection
+       SET modified = current_timestamp, dav_etag = md5(OLD.dav_name::text||OLD.dav_etag)
+     WHERE collection_id = OLD.collection_id;
+    RETURN OLD;
+  END IF;
+
+  IF NEW.collection_id != OLD.collection_id THEN
+    -- If we've switched the collection_id of this event, then we also need to update
+    -- the etag of the old collection - as we do for delete.
+    UPDATE collection
+       SET modified = current_timestamp, dav_etag = md5(OLD.dav_name::text||OLD.dav_etag)
+     WHERE collection_id = OLD.collection_id;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER caldav_data_modified AFTER INSERT OR UPDATE OR DELETE ON caldav_data
+    FOR EACH ROW EXECUTE PROCEDURE caldav_data_modified();
+
+
+DROP TRIGGER caldav_data_sync_dav_id ON caldav_data CASCADE;
+DROP TRIGGER calendar_item_sync_dav_id ON calendar_item CASCADE;
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS $$
+  DECLARE
+  BEGIN
+
+    IF TG_OP = 'DELETE' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval('dav_id_seq');
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN
+      IF OLD.dav_id != NEW.dav_id OR OLD.collection_id != NEW.collection_id
+                 OR OLD.user_no != NEW.user_no OR OLD.dav_name != NEW.dav_name THEN
+        UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
+                        collection_id = NEW.collection_id, dav_name = NEW.dav_name
+            WHERE dav_name = OLD.dav_name OR dav_id = OLD.dav_id;
+      END IF;
+      RETURN NEW;
+    END IF;
+
+    UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
+                    collection_id = NEW.collection_id, dav_name = NEW.dav_name
+          WHERE dav_name = NEW.dav_name OR dav_id = NEW.dav_id;
+
+    RETURN NEW;
+
+  END
+$$ LANGUAGE 'plpgsql';
+CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+
+
+-- New in 1.2.6
+
+CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_priv ALIAS FOR $1;
+  out_bits BIT(24);
+BEGIN
+  out_bits := 0::BIT(24);
+  IF in_priv ~* 'A' THEN
+    out_bits = ~ out_bits;
+    RETURN out_bits;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'R' THEN
+    out_bits := out_bits | 4609::BIT(24);
+  END IF;
+
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind
+  --  128 DAV:unbind
+  IF in_priv ~* 'W' THEN
+    out_bits := out_bits |   198::BIT(24);
+  END IF;
+
+  --   64 DAV:bind
+  IF in_priv ~* 'B' THEN
+    out_bits := out_bits | 64::BIT(24);
+  END IF;
+
+  --  128 DAV:unbind
+  IF in_priv ~* 'U' THEN
+    out_bits := out_bits | 128::BIT(24);
+  END IF;
+
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'F' THEN
+    out_bits := out_bits | 4608::BIT(24);
+  END IF;
+
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+--
+-- NOTE: Round-trip through this and then back through legacy_privilege_to_bits
+--       function is lossy!  Through legacy_privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_legacy_privilege( BIT(24) ) RETURNS TEXT AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT;
+BEGIN
+  out_priv := '';
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv = 'A';
+    RETURN out_priv;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF (in_bits & 4609::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := 'R';
+    ELSE
+      out_priv := 'F';
+    END IF;
+  END IF;
+
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind
+  --  128 DAV:unbind
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 6::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || 'W';
+    ELSE
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'B';
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'U';
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN out_priv;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+CREATE or REPLACE FUNCTION get_permissions( INT, INT ) RETURNS TEXT AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_to   ALIAS FOR $2;
+  out_confers TEXT;
+  bit_confers BIT(24);
+  group_role_no INT;
+  tmp_txt TEXT;
+  dbg TEXT DEFAULT '';
+  r RECORD;
+  counter INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN 'A';
+  END IF;
+
+  -- dbg := 'S-';
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1
+                    WHERE r1.from_user = in_from AND r1.to_user = in_to AND NOT usr_is_role(r1.to_user,'Group');
+  IF FOUND THEN
+    RETURN dbg || out_confers;
+  END IF;
+  -- RAISE NOTICE 'No simple relationships between % and %', in_from, in_to;
+
+  SELECT bit_or(r1.confers & r2.confers) INTO bit_confers
+              FROM relationship r1
+              JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM roles LEFT JOIN role_member USING(role_no) WHERE role_name='Group');
+  IF bit_confers != 0::BIT(24) THEN
+    RETURN dbg || bits_to_legacy_privilege(bit_confers);
+  END IF;
+
+  RETURN '';
+  -- RAISE NOTICE 'No complex relationships between % and %', in_from, in_to;
+
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1 LEFT OUTER JOIN relationship r2 ON(r1.to_user = r2.to_user)
+       WHERE r1.from_user = in_from AND r2.from_user = in_to AND r1.from_user != r2.from_user
+         AND NOT EXISTS( SELECT 1 FROM relationship r3 WHERE r3.from_user = r1.to_user ) ;
+
+  IF FOUND THEN
+    -- dbg := 'H-';
+    -- RAISE NOTICE 'Permissions to shared group % ', out_confers;
+    RETURN dbg || out_confers;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN '';
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION get_group_role_no() RETURNS INT AS $$
+  SELECT role_no FROM roles WHERE role_name = 'Group'
+$$ LANGUAGE 'SQL' IMMUTABLE;
+
+CREATE or REPLACE FUNCTION has_legacy_privilege( INT, TEXT, INT ) RETURNS BOOLEAN AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_legacy_privilege ALIAS FOR $2;
+  in_to   ALIAS FOR $3;
+  in_confers BIT(24);
+  group_role_no INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN TRUE;
+  END IF;
+
+  SELECT get_group_role_no() INTO group_role_no;
+  SELECT legacy_privilege_to_bits(in_legacy_privilege) INTO in_confers;
+
+  IF EXISTS(SELECT 1 FROM relationship WHERE from_user = in_from AND to_user = in_to
+                      AND (in_confers & confers) = in_confers
+                      AND NOT EXISTS(SELECT 1 FROM role_member WHERE to_user = user_no AND role_no = group_role_no) ) THEN
+    -- A direct relationship from A to B that grants sufficient
+    -- RAISE NOTICE 'Permissions directly granted';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers
+           AND r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no) ) THEN
+    -- An indirect relationship from A to B via group G that grants sufficient
+    -- RAISE NOTICE 'Permissions mediated via group';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.to_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers
+           AND r1.from_user=in_from AND r2.from_user=in_to
+           AND r2.to_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no)
+           AND NOT EXISTS(SELECT 1 FROM relationship WHERE from_user=r2.to_user) ) THEN
+    -- An indirect reflexive relationship from both A & B to group G which grants sufficient
+    -- RAISE NOTICE 'Permissions to shared group';
+    RETURN TRUE;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN FALSE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Given a verbose DAV: or CalDAV: privilege name return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_priv ALIAS FOR $1;
+  in_priv TEXT;
+BEGIN
+  in_priv := trim(lower(regexp_replace(raw_priv, '^.*:', '')));
+  IF in_priv = 'all' THEN
+    RETURN ~ 0::BIT(24);
+  END IF;
+
+  RETURN (CASE
+            WHEN in_priv = 'read'                            THEN  4609 -- 1 + 512 + 4096
+            WHEN in_priv = 'write'                           THEN   198 -- 2 + 4 + 64 + 128
+            WHEN in_priv = 'write-properties'                THEN     2
+            WHEN in_priv = 'write-content'                   THEN     4
+            WHEN in_priv = 'unlock'                          THEN     8
+            WHEN in_priv = 'read-acl'                        THEN    16
+            WHEN in_priv = 'read-current-user-privilege-set' THEN    32
+            WHEN in_priv = 'bind'                            THEN    64
+            WHEN in_priv = 'unbind'                          THEN   128
+            WHEN in_priv = 'write-acl'                       THEN   256
+            WHEN in_priv = 'read-free-busy'                  THEN  4608 --  512 + 4096
+            WHEN in_priv = 'schedule-deliver'                THEN  7168 -- 1024 + 2048 + 4096
+            WHEN in_priv = 'schedule-deliver-invite'         THEN  1024
+            WHEN in_priv = 'schedule-deliver-reply'          THEN  2048
+            WHEN in_priv = 'schedule-query-freebusy'         THEN  4096
+            WHEN in_priv = 'schedule-send'                   THEN 57344 -- 8192 + 16384 + 32768
+            WHEN in_priv = 'schedule-send-invite'            THEN  8192
+            WHEN in_priv = 'schedule-send-reply'             THEN 16384
+            WHEN in_priv = 'schedule-send-freebusy'          THEN 32768
+          ELSE 0 END)::BIT(24);
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- Given an array of verbose DAV: or CalDAV: privilege names return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT[] ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_privs ALIAS FOR $1;
+  in_priv TEXT;
+  out_bits BIT(24);
+  i INT;
+  allprivs BIT(24);
+  start INT;
+  finish INT;
+BEGIN
+  out_bits := 0::BIT(24);
+  allprivs := ~ out_bits;
+  SELECT array_lower(raw_privs,1) INTO start;
+  SELECT array_upper(raw_privs,1) INTO finish;
+  FOR i IN start .. finish  LOOP
+    SELECT out_bits | privilege_to_bits(raw_privs[i]) INTO out_bits;
+    IF out_bits = allprivs THEN
+      RETURN allprivs;
+    END IF;
+  END LOOP;
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- NOTE: Round-trip through this and then back through privilege_to_bits
+--       function is lossy!  Through privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_privilege( BIT(24) ) RETURNS TEXT[] AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT[];
+BEGIN
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv := out_priv || ARRAY['DAV:all'];
+  END IF;
+
+  IF (in_bits & 513::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:read'];
+    END IF;
+    IF (in_bits & 512::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:read-free-busy'];
+    END IF;
+  END IF;
+
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 198::BIT(24)) = 198::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:write'];
+    ELSE
+      IF (in_bits & 2::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-properties'];
+      END IF;
+      IF (in_bits & 4::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-content'];
+      END IF;
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:bind'];
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:unbind'];
+      END IF;
+    END IF;
+  END IF;
+
+  IF (in_bits & 8::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:unlock'];
+  END IF;
+
+  IF (in_bits & 16::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-acl'];
+  END IF;
+
+  IF (in_bits & 32::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-current-user-privilege-set'];
+  END IF;
+
+  IF (in_bits & 256::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:write-acl'];
+  END IF;
+
+  IF (in_bits & 7168::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 7168::BIT(24)) = 7168::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-deliver'];
+    ELSE
+      IF (in_bits & 1024::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-invite'];
+      END IF;
+      IF (in_bits & 2048::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-reply'];
+      END IF;
+      IF (in_bits & 4096::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-query-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+
+  IF (in_bits & 57344::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 57344::BIT(24)) = 57344::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-send'];
+    ELSE
+      IF (in_bits & 8192::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-invite'];
+      END IF;
+      IF (in_bits & 16384::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-reply'];
+      END IF;
+      IF (in_bits & 32768::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN out_priv;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- Expanded group memberships out to some depth
+CREATE or REPLACE FUNCTION expand_memberships( INT8, INT ) RETURNS SETOF INT8 AS $$
+  SELECT group_id FROM group_member WHERE member_id = $1
+      UNION
+  SELECT expanded.g_id FROM (SELECT CASE WHEN $2 > 0 THEN expand_memberships( group_id, $2 - 1) END AS g_id
+                               FROM group_member WHERE member_id = $1) AS expanded
+                       WHERE expanded.g_id IS NOT NULL;
+$$ LANGUAGE 'SQL' STABLE STRICT;
+
+-- Expanded group members out to some depth
+CREATE or REPLACE FUNCTION expand_members( INT8, INT ) RETURNS SETOF INT8 AS $$
+  SELECT member_id FROM group_member WHERE group_id = $1
+      UNION
+  SELECT expanded.m_id FROM (SELECT CASE WHEN $2 > 0 THEN expand_members( member_id, $2 - 1) END AS m_id
+                               FROM group_member WHERE group_id = $1) AS expanded
+                       WHERE expanded.m_id IS NOT NULL;
+$$ LANGUAGE 'SQL' STABLE STRICT;
+
+
+
+
+-- Privileges from accessor to grantor, by principal_id
+CREATE or REPLACE FUNCTION pprivs( INT8, INT8, INT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_accessor ALIAS FOR $1;
+  in_grantor  ALIAS FOR $2;
+  in_depth    ALIAS FOR $3;
+  out_conferred BIT(24);
+BEGIN
+  out_conferred := 0::BIT(24);
+  -- Self can always have full access
+  IF in_grantor = in_accessor THEN
+    RETURN ~ out_conferred;
+  END IF;
+
+  SELECT bit_or(subquery.privileges) INTO out_conferred FROM
+    (
+      SELECT privileges FROM grants WHERE by_principal=in_grantor AND by_collection IS NULL
+                                          AND (to_principal=in_accessor OR to_principal IN (SELECT expand_memberships(in_accessor,in_depth)))
+            UNION
+      SELECT 32::BIT(24) AS privileges FROM expand_memberships(in_accessor,in_depth) WHERE expand_memberships = in_grantor
+    ) AS subquery ;
+
+  IF out_conferred IS NULL THEN
+    SELECT default_privileges INTO out_conferred FROM principal WHERE principal_id = in_grantor;
+  END IF;
+
+  RETURN out_conferred;
+END;
+$$ LANGUAGE 'plpgsql' STABLE STRICT;
+
+
+-- Privileges from accessor to grantor, by user_no
+CREATE or REPLACE FUNCTION uprivs( INT8, INT8, INT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_accessor ALIAS FOR $1;
+  in_grantor  ALIAS FOR $2;
+  in_depth    ALIAS FOR $3;
+  out_conferred BIT(24);
+BEGIN
+  out_conferred := 0::BIT(24);
+  -- Self can always have full access
+  IF in_grantor = in_accessor THEN
+    RETURN ~ out_conferred;
+  END IF;
+
+  SELECT pprivs( p1.principal_id, p2.principal_id, in_depth ) INTO out_conferred
+          FROM principal p1, principal p2
+          WHERE p1.user_no = in_accessor AND p2.user_no = in_grantor;
+
+  RETURN out_conferred;
+END;
+$$ LANGUAGE 'plpgsql' STABLE STRICT;
+
+
+-- Privileges from accessor (by principal_id) to path
+CREATE or REPLACE FUNCTION path_privs( INT8, TEXT, INT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_accessor ALIAS FOR $1;
+  in_path  ALIAS FOR $2;
+  in_depth    ALIAS FOR $3;
+
+  alt1_path TEXT;
+  alt2_path TEXT;
+  grantor_collection    INT8;
+  grantor_principal     INT8;
+  collection_path       TEXT;
+  collection_privileges BIT(24);
+  out_conferred         BIT(24);
+BEGIN
+  out_conferred := 0::BIT(24);
+
+  IF in_path ~ '^/?$' THEN
+    -- RAISE NOTICE 'Collection is root: Collection: %', in_path;
+    RETURN 1; -- basic read privileges on root directory
+  END IF;
+
+  -- We need to canonicalise the path, so:
+  -- If it matches '/' + some characters (+ optional '/')  => a principal URL
+  IF in_path ~ '^/[^/]+/?$' THEN
+    alt1_path := replace(in_path, '/', '');
+    SELECT pprivs(in_accessor,principal_id, in_depth) INTO out_conferred FROM usr JOIN principal USING(user_no) WHERE username = alt1_path;
+    -- RAISE NOTICE 'Path is Principal: Principal: %, Collection: %, Permissions: %', in_accessor, in_path, out_conferred;
+    RETURN out_conferred;
+  END IF;
+
+  -- Otherwise look for the longest segment matching up to the last '/', or if we append one, or if we replace a final '.ics' with one.
+  alt1_path := in_path;
+  IF alt1_path ~ E'\\.ics$' THEN
+    alt1_path := substr(alt1_path, 1, length(alt1_path) - 4) || '/';
+  END IF;
+  alt2_path := regexp_replace( in_path, '[^/]*$', '');
+  SELECT collection.collection_id, grantor.principal_id, collection.dav_name, collection.default_privileges
+    INTO grantor_collection, grantor_principal, collection_path, collection_privileges
+                      FROM collection JOIN principal grantor USING (user_no)
+                      WHERE dav_name = in_path || '/' OR dav_name = alt1_path OR dav_name = alt2_path
+                      ORDER BY LENGTH(collection.dav_name) DESC LIMIT 1;
+
+  -- Self will always need full access to their own collections!
+  IF grantor_principal = in_accessor THEN
+    -- RAISE NOTICE 'Principal IS owner: Principal: %, Collection: %', in_accessor, in_path;
+    RETURN ~ out_conferred;
+  END IF;
+
+  SELECT privileges INTO out_conferred FROM grants
+                   WHERE by_collection = grantor_collection
+                     AND (to_principal=in_accessor OR to_principal IN (SELECT expand_memberships(in_accessor,in_depth)));
+
+  IF out_conferred IS NULL THEN
+    IF collection_privileges IS NULL THEN
+      IF grantor_principal IS NULL THEN
+        alt1_path := regexp_replace( in_path, '/[^/]+/?$', '/');
+        SELECT path_privs(in_accessor,alt1_path,in_depth) INTO out_conferred;
+        -- RAISE NOTICE 'Collection is NULL: Principal: %, Collection: %, Permissions: %', in_accessor, in_path, out_conferred;
+      ELSE
+        SELECT pprivs(in_accessor,grantor_principal,in_depth) INTO out_conferred;
+        -- RAISE NOTICE 'Collection priveleges are NULL: Principal: %, Collection: %, Permissions: %', in_accessor, in_path, out_conferred;
+      END IF;
+    ELSE
+      out_conferred := collection_privileges;
+      -- RAISE NOTICE 'Default Collection priveleges apply: Principal: %, Collection: %, Permissions: %', in_accessor, in_path, out_conferred;
+    END IF;
+  END IF;
+
+  RETURN out_conferred;
+END;
+$$ LANGUAGE 'plpgsql' STABLE STRICT;
+
+
+-- List a user's memberships as a text string
+CREATE or REPLACE FUNCTION is_member_of_list( INT8 ) RETURNS TEXT AS $$
+DECLARE
+  in_member_id ALIAS FOR $1;
+  m RECORD;
+  mlist TEXT;
+BEGIN
+  mlist := '';
+  FOR m IN SELECT displayname, group_id FROM group_member JOIN principal ON (group_id = principal_id)
+                          WHERE member_id = in_member_id
+  LOOP
+    mlist := mlist
+             || CASE WHEN mlist = '' THEN '' ELSE ', ' END
+             || COALESCE( m.displayname, m.group_id::text);
+  END LOOP;
+  RETURN mlist;
+END;
+$$ LANGUAGE 'plpgsql' STRICT;
+
+
+-- List a user's members as a text string
+CREATE or REPLACE FUNCTION has_members_list( INT8 ) RETURNS TEXT AS $$
+DECLARE
+  in_member_id ALIAS FOR $1;
+  m RECORD;
+  mlist TEXT;
+BEGIN
+  mlist := '';
+  FOR m IN SELECT displayname, group_id FROM group_member JOIN principal ON (member_id = principal_id)
+                          WHERE group_id = in_member_id
+  LOOP
+    mlist := mlist
+             || CASE WHEN mlist = '' THEN '' ELSE ', ' END
+             || COALESCE( m.displayname, m.group_id::text);
+  END LOOP;
+  RETURN mlist;
+END;
+$$ LANGUAGE 'plpgsql' STRICT;
+
+
+-- List the privileges as a text string
+CREATE or REPLACE FUNCTION privileges_list( BIT(24) ) RETURNS TEXT AS $$
+DECLARE
+  in_privileges ALIAS FOR $1;
+  privileges TEXT[];
+  plist TEXT;
+  start INT;
+  finish INT;
+  i INT;
+BEGIN
+  plist := '';
+
+  privileges := bits_to_privilege(in_privileges);
+  SELECT array_lower(privileges,1) INTO start;
+  IF start IS NOT NULL THEN
+    SELECT array_upper(privileges,1) INTO finish;
+    FOR i IN start .. finish  LOOP
+      plist := plist
+              || CASE WHEN plist = '' THEN '' ELSE ', ' END
+              || privileges[i];
+    END LOOP;
+  END IF;
+  RETURN plist;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+DROP TRIGGER principal_modified ON principal CASCADE;
+CREATE or REPLACE FUNCTION principal_modified() RETURNS TRIGGER AS $$
+DECLARE
+BEGIN
+  -- in case we trigger on other events in future
+  IF TG_OP = 'UPDATE' THEN
+    IF NEW.type_id != OLD.type_id THEN
+      UPDATE grants
+        SET is_group = (NEW.type_id = 3)
+      WHERE grants.to_principal = NEW.principal_id;
+    END IF;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER principal_modified AFTER UPDATE ON principal
+    FOR EACH ROW EXECUTE PROCEDURE principal_modified();
+
+
+DROP TRIGGER grants_modified ON grants CASCADE;
+CREATE or REPLACE FUNCTION grants_modified() RETURNS TRIGGER AS $$
+DECLARE
+  old_to_principal INT8;
+  new_is_group BOOL;
+BEGIN
+  -- in case we trigger on other events in future
+  IF TG_OP = 'INSERT' THEN
+    old_to_principal := NULL;
+  ELSE
+    old_to_principal := OLD.to_principal;
+  END IF;
+  IF TG_OP = 'INSERT' OR NEW.to_principal != old_to_principal THEN
+    SELECT (type_id = 3) INTO new_is_group FROM principal WHERE principal_id = NEW.to_principal;
+    IF NEW.is_group != new_is_group THEN
+      NEW.is_group := new_is_group;
+    END IF;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER grants_modified AFTER INSERT OR UPDATE ON grants
+    FOR EACH ROW EXECUTE PROCEDURE grants_modified();
+
+
+
+-- An expanded list of the grants this principal has access to
+CREATE or REPLACE FUNCTION p_has_proxy_access_to( INT8, INT ) RETURNS SETOF INT8 AS $$
+  SELECT by_principal
+    FROM (
+      SELECT by_principal FROM grants
+           WHERE to_principal IN (SELECT $1 UNION SELECT expand_memberships($1,$2))
+             AND (privileges & 5::BIT(24)) != 0::BIT(24)
+             AND by_collection IS NULL
+             AND by_principal != $1
+      UNION
+      SELECT principal_id AS by_principal FROM principal
+           WHERE (default_privileges & 5::BIT(24)) != 0::BIT(24)
+             AND principal_id != $1
+    ) subquery;
+$$ LANGUAGE 'SQL' STABLE STRICT;
+
+
+-- A list of the principals who can proxy to this principal
+CREATE or REPLACE FUNCTION grants_proxy_access_from_p( INT8, INT ) RETURNS SETOF INT8 AS $$
+  SELECT DISTINCT by_principal
+    FROM grants
+   WHERE by_collection IS NULL AND by_principal != $1
+     AND by_principal IN (SELECT expand_members(g2.to_principal,$2) FROM grants g2 WHERE g2.by_principal = $1)
+   ;
+$$ LANGUAGE 'SQL' STABLE STRICT;
+
+
+
+-- New in 1.2.7
+
+CREATE or REPLACE FUNCTION write_sync_change( INT8, INT, TEXT ) RETURNS BOOLEAN AS $$
+DECLARE
+  in_collection_id ALIAS FOR $1;
+  in_status ALIAS FOR $2;
+  in_dav_name ALIAS FOR $3;
+  tmp_int INT8;
+BEGIN
+  SELECT 1 INTO tmp_int FROM sync_tokens
+           WHERE collection_id = in_collection_id
+           LIMIT 1;
+  IF NOT FOUND THEN
+    RETURN FALSE;
+  END IF;
+  SELECT dav_id INTO tmp_int FROM calendar_item WHERE dav_name = in_dav_name;
+  INSERT INTO sync_changes ( collection_id, sync_status, dav_id, dav_name)
+                     VALUES( in_collection_id, in_status, tmp_int, in_dav_name);
+  RETURN TRUE;
+END
+$$ LANGUAGE 'PlPgSQL' VOLATILE STRICT;
+
+
+CREATE or REPLACE FUNCTION new_sync_token( INT8, INT8 ) RETURNS INT8 AS $$
+DECLARE
+  in_old_sync_token ALIAS FOR $1;
+  in_collection_id ALIAS FOR $2;
+  tmp_int INT8;
+BEGIN
+  IF in_old_sync_token > 0 THEN
+    SELECT 1 INTO tmp_int FROM sync_changes
+            WHERE collection_id = in_collection_id
+              AND sync_time > (SELECT modification_time FROM sync_tokens WHERE sync_token = in_old_sync_token)
+            LIMIT 1;
+    IF NOT FOUND THEN
+      RETURN in_old_sync_token;
+    END IF;
+  END IF;
+  SELECT nextval('sync_tokens_sync_token_seq') INTO tmp_int;
+  INSERT INTO sync_tokens(collection_id, sync_token) VALUES( in_collection_id, tmp_int );
+  RETURN tmp_int;
+END
+$$ LANGUAGE 'PlPgSQL' STRICT;
+
+
+DROP TRIGGER alarm_changed ON calendar_alarm CASCADE;
+CREATE or REPLACE FUNCTION alarm_changed() RETURNS TRIGGER AS $$
+DECLARE
+  oldcomponent TEXT;
+  newcomponent TEXT;
+BEGIN
+  -- in case we trigger on other events in future
+  IF TG_OP = 'UPDATE' THEN
+    IF NEW.component != OLD.component THEN
+      UPDATE caldav_data
+         SET caldav_data = replace( caldav_data, OLD.component, NEW.component ),
+             dav_etag = md5(replace( caldav_data, OLD.component, NEW.component ))
+       WHERE caldav_data.dav_id = NEW.dav_id;
+    END IF;
+  END IF;
+  RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+CREATE TRIGGER alarm_changed AFTER UPDATE ON calendar_alarm
+    FOR EACH ROW EXECUTE PROCEDURE alarm_changed();
Index: contrib/davical/dba/davical.sql
===================================================================
--- contrib/davical/dba/davical.sql	(revision 3733)
+++ contrib/davical/dba/davical.sql	(revision 3733)
@@ -0,0 +1,396 @@
+-- DAViCal CalDAV Server - Database Schema
+--
+
+
+-- Given a verbose DAV: or CalDAV: privilege name return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_priv ALIAS FOR $1;
+  in_priv TEXT;
+BEGIN
+  in_priv := trim(lower(regexp_replace(raw_priv, '^.*:', '')));
+  IF in_priv = 'all' THEN
+    RETURN ~ 0::BIT(24);
+  END IF;
+
+  RETURN (CASE
+            WHEN in_priv = 'read'                            THEN  4609 -- 1 + 512 + 4096
+            WHEN in_priv = 'write'                           THEN   198 -- 2 + 4 + 64 + 128
+            WHEN in_priv = 'write-properties'                THEN     2
+            WHEN in_priv = 'write-content'                   THEN     4
+            WHEN in_priv = 'unlock'                          THEN     8
+            WHEN in_priv = 'read-acl'                        THEN    16
+            WHEN in_priv = 'read-current-user-privilege-set' THEN    32
+            WHEN in_priv = 'bind'                            THEN    64
+            WHEN in_priv = 'unbind'                          THEN   128
+            WHEN in_priv = 'write-acl'                       THEN   256
+            WHEN in_priv = 'read-free-busy'                  THEN  4608 --  512 + 4096
+            WHEN in_priv = 'schedule-deliver'                THEN  7168 -- 1024 + 2048 + 4096
+            WHEN in_priv = 'schedule-deliver-invite'         THEN  1024
+            WHEN in_priv = 'schedule-deliver-reply'          THEN  2048
+            WHEN in_priv = 'schedule-query-freebusy'         THEN  4096
+            WHEN in_priv = 'schedule-send'                   THEN 57344 -- 8192 + 16384 + 32768
+            WHEN in_priv = 'schedule-send-invite'            THEN  8192
+            WHEN in_priv = 'schedule-send-reply'             THEN 16384
+            WHEN in_priv = 'schedule-send-freebusy'          THEN 32768
+          ELSE 0 END)::BIT(24);
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+-- Given an array of verbose DAV: or CalDAV: privilege names return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT[] ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_privs ALIAS FOR $1;
+  in_priv TEXT;
+  out_bits BIT(24);
+  i INT;
+  allprivs BIT(24);
+  start INT;
+  finish INT;
+BEGIN
+  out_bits := 0::BIT(24);
+  allprivs := ~ out_bits;
+  SELECT array_lower(raw_privs,1) INTO start;
+  SELECT array_upper(raw_privs,1) INTO finish;
+  FOR i IN start .. finish  LOOP
+    SELECT out_bits | privilege_to_bits(raw_privs[i]) INTO out_bits;
+    IF out_bits = allprivs THEN
+      RETURN allprivs;
+    END IF;
+  END LOOP;
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- This sequence is used in a number of places so that any DAV resource will have a unique ID
+CREATE SEQUENCE dav_id_seq;
+
+
+-- Not particularly needed, perhaps, except as a way to collect
+-- a bunch of valid iCalendar time zone specifications... :-)
+CREATE TABLE time_zone (
+  tz_id TEXT PRIMARY KEY,
+  tz_locn TEXT,
+  tz_spec TEXT
+);
+
+
+-- Something that can look like a filesystem hierarchy where we store stuff
+CREATE TABLE collection (
+  user_no INT references usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  parent_container TEXT,
+  dav_name TEXT,
+  dav_etag TEXT,
+  dav_displayname TEXT,
+  is_calendar BOOLEAN,
+  created TIMESTAMP WITH TIME ZONE,
+  modified TIMESTAMP WITH TIME ZONE,
+  public_events_only BOOLEAN NOT NULL DEFAULT FALSE,
+  publicly_readable BOOLEAN NOT NULL DEFAULT FALSE,
+  collection_id INT8 PRIMARY KEY DEFAULT nextval('dav_id_seq'),
+  default_privileges BIT(24),
+  is_addressbook BOOLEAN DEFAULT FALSE,
+  resourcetypes TEXT DEFAULT '<DAV::collection/>',
+  schedule_transp TEXT DEFAULT 'opaque',
+  timezone TEXT REFERENCES time_zone(tz_id) ON DELETE SET NULL ON UPDATE CASCADE,
+  description TEXT DEFAULT '',
+  UNIQUE(user_no,dav_name)
+);
+
+
+-- The main event.  Where we store the things the calendar throws at us.
+CREATE TABLE caldav_data (
+  user_no INT references usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  dav_name TEXT,
+  dav_etag TEXT,
+  created TIMESTAMP WITH TIME ZONE,
+  modified TIMESTAMP WITH TIME ZONE,
+  caldav_data TEXT,
+  caldav_type TEXT,
+  logged_user INT references usr(user_no) ON UPDATE CASCADE ON DELETE SET DEFAULT DEFERRABLE,
+  dav_id INT8 UNIQUE DEFAULT nextval('dav_id_seq'),
+  collection_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  weak_etag TEXT DEFAULT NULL,
+
+  PRIMARY KEY ( user_no, dav_name )
+);
+CREATE INDEX caldav_data_collection_id_fkey ON caldav_data(collection_id);
+
+-- The parsed calendar item.  Here we have pulled those events/todos/journals apart somewhat.
+CREATE TABLE calendar_item (
+  user_no INT references usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  dav_name TEXT,
+  dav_etag TEXT,
+
+  -- Extracted vEvent/vTodo data
+  uid TEXT,
+  created TIMESTAMP,
+  last_modified TIMESTAMP,
+  dtstamp TIMESTAMP,
+  dtstart TIMESTAMP WITH TIME ZONE,
+  dtend TIMESTAMP WITH TIME ZONE,
+  due TIMESTAMP WITH TIME ZONE,
+  summary TEXT,
+  location TEXT,
+  description TEXT,
+  priority INT,
+  class TEXT,
+  transp TEXT,
+  rrule TEXT,
+  url TEXT,
+  percent_complete NUMERIC(7,2),
+  tz_id TEXT REFERENCES time_zone( tz_id ),
+  status TEXT,
+  completed TIMESTAMP WITH TIME ZONE,
+  dav_id INT8 UNIQUE,
+  collection_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+
+  -- Cascade updates / deletes from the caldav_data table
+  CONSTRAINT caldav_exists FOREIGN KEY ( user_no, dav_name )
+                REFERENCES caldav_data ( user_no, dav_name )
+                MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE,
+
+  PRIMARY KEY ( user_no, dav_name )
+);
+CREATE INDEX calendar_item_collection_id_fkey ON calendar_item(collection_id);
+
+
+
+-- Each user can be related to each other user.  This mechanism can also
+-- be used to define groups of users, since some relationships are transitive.
+CREATE TABLE relationship_type (
+  rt_id SERIAL PRIMARY KEY,
+  rt_name TEXT,
+  rt_togroup BOOLEAN,
+  confers TEXT DEFAULT 'RW',
+  rt_fromgroup BOOLEAN,
+  bit_confers BIT(24) DEFAULT privilege_to_bits(ARRAY['DAV::read','DAV::write'])
+);
+
+
+CREATE TABLE relationship (
+  from_user INT REFERENCES usr (user_no) ON UPDATE CASCADE ON DELETE CASCADE,
+  to_user INT REFERENCES usr (user_no) ON UPDATE CASCADE ON DELETE CASCADE,
+  rt_id INT REFERENCES relationship_type (rt_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  confers BIT(24) DEFAULT privilege_to_bits(ARRAY['DAV::read','DAV::write']),
+
+  PRIMARY KEY ( from_user, to_user, rt_id )
+);
+
+
+CREATE TABLE locks (
+  dav_name TEXT,
+  opaquelocktoken TEXT UNIQUE NOT NULL,
+  type TEXT,
+  scope TEXT,
+  depth INT,
+  owner TEXT,
+  timeout INTERVAL,
+  start TIMESTAMP DEFAULT current_timestamp
+);
+CREATE INDEX locks_dav_name_idx ON locks(dav_name);
+
+
+CREATE TABLE property (
+  dav_name TEXT,
+  property_name TEXT,
+  property_value TEXT,
+  changed_on TIMESTAMP DEFAULT current_timestamp,
+  changed_by INT REFERENCES usr ( user_no ) ON UPDATE CASCADE ON DELETE SET DEFAULT,
+  PRIMARY KEY ( dav_name, property_name )
+);
+CREATE INDEX properties_dav_name_idx ON property(dav_name);
+
+
+CREATE TABLE freebusy_ticket (
+  ticket_id TEXT NOT NULL PRIMARY KEY,
+  user_no integer NOT NULL REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE,
+  created timestamp with time zone DEFAULT current_timestamp NOT NULL
+);
+
+
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS $$
+  DECLARE
+  BEGIN
+
+    IF TG_OP = 'DELETE' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval('dav_id_seq');
+    END IF;
+
+    IF TG_OP = 'UPDATE' THEN
+      IF OLD.dav_id != NEW.dav_id OR OLD.collection_id != NEW.collection_id
+                 OR OLD.user_no != NEW.user_no OR OLD.dav_name != NEW.dav_name THEN
+        UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
+                        collection_id = NEW.collection_id, dav_name = NEW.dav_name
+            WHERE dav_name = OLD.dav_name OR dav_id = OLD.dav_id;
+      END IF;
+      RETURN NEW;
+    END IF;
+
+    UPDATE calendar_item SET dav_id = NEW.dav_id, user_no = NEW.user_no,
+                    collection_id = NEW.collection_id, dav_name = NEW.dav_name
+          WHERE dav_name = NEW.dav_name OR dav_id = NEW.dav_id;
+
+    RETURN NEW;
+
+  END
+$$ LANGUAGE 'plpgsql';
+CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+
+-- Only needs SELECT access by website.
+CREATE TABLE principal_type (
+  principal_type_id SERIAL PRIMARY KEY,
+  principal_type_desc TEXT
+);
+
+
+-- web needs SELECT,INSERT,UPDATE,DELETE
+CREATE TABLE principal (
+  principal_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
+  type_id INT8 NOT NULL REFERENCES principal_type(principal_type_id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE,
+  user_no INT8 NULL REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  displayname TEXT,
+  default_privileges BIT(24)
+);
+
+
+
+-- Allowing identification of group members.
+CREATE TABLE group_member (
+  group_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  member_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
+);
+CREATE UNIQUE INDEX group_member_pk ON group_member(group_id,member_id);
+CREATE INDEX group_member_sk ON group_member(member_id);
+
+
+CREATE TABLE grants (
+  by_principal INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  by_collection INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  to_principal INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  privileges BIT(24),
+  is_group BOOLEAN
+) WITHOUT OIDS;
+CREATE UNIQUE INDEX grants_pk1 ON grants(by_principal,to_principal);
+CREATE UNIQUE INDEX grants_pk2 ON grants(by_collection,to_principal);
+
+
+CREATE TABLE sync_tokens (
+  sync_token SERIAL PRIMARY KEY,
+  collection_id INT8 REFERENCES collection(collection_id) ON DELETE CASCADE ON UPDATE CASCADE,
+  modification_time TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp
+);
+
+CREATE TABLE sync_changes (
+  sync_time TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp,
+  collection_id INT8 REFERENCES collection(collection_id) ON DELETE CASCADE ON UPDATE CASCADE,
+  sync_status INT,
+  dav_id INT8, -- can't REFERENCES calendar_item(dav_id) ON DELETE SET NULL ON UPDATE RESTRICT
+  dav_name TEXT
+);
+CREATE INDEX sync_processing_index ON sync_changes( collection_id, dav_id, sync_time );
+
+-- Revision 1.2.7 endeth here.
+
+CREATE TABLE access_ticket (
+  ticket_id TEXT PRIMARY KEY,
+  dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  privileges BIT(24),
+  target_collection_id INT8 NOT NULL REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  target_resource_id INT8 REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  expires TIMESTAMP
+);
+
+
+-- At this point we only support binding collections
+CREATE TABLE dav_binding (
+  bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
+  bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
+  dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  parent_container TEXT NOT NULL,
+  dav_name TEXT UNIQUE NOT NULL,
+  dav_displayname TEXT
+);
+
+
+CREATE TABLE addressbook_resource (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE PRIMARY KEY,
+  version TEXT,
+  uid TEXT,
+  nickname TEXT,
+  fn TEXT, -- fullname
+  n TEXT, -- Name Surname;First names
+  note TEXT,
+  org TEXT,
+  url TEXT,
+  fburl TEXT,
+  caladruri TEXT,
+  caluri TEXT
+);
+
+CREATE TABLE addressbook_address_adr (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  box_no TEXT,
+  unit_no TEXT,
+  street_address TEXT,
+  locality TEXT,
+  region TEXT,
+  postcode TEXT,
+  country TEXT,
+  property TEXT -- The full text of the property
+);
+
+CREATE TABLE addressbook_address_tel (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  tel TEXT,
+  property TEXT -- The full text of the property
+);
+
+CREATE TABLE addressbook_address_email (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  email TEXT,
+  property TEXT -- The full text of the property
+);
+
+
+CREATE TABLE calendar_alarm (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  action TEXT,
+  trigger TEXT,
+  summary TEXT,
+  description TEXT,
+  next_trigger TIMESTAMP WITH TIME ZONE,
+  component TEXT, -- The full text of the component
+  trigger_state CHAR DEFAULT 'N' -- 'N' => 'New/Needs setting', 'A' = 'Active', 'O' = 'Old'
+);
+
+CREATE TABLE calendar_attendee (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  status TEXT,
+  partstat TEXT,
+  cn TEXT,
+  attendee TEXT,
+  role TEXT,
+  rsvp BOOLEAN,
+  property TEXT, -- The full text of the property
+  attendee_state TEXT, -- Internal DAViCal processing state
+  weak_etag TEXT, -- The week_etag applying for this attendee state
+  PRIMARY KEY ( dav_id, attendee )
+);
+
+SELECT new_db_revision(1,2,9, 'Septembre' );
Index: contrib/davical/dba/rrule_functions-8.1.sql
===================================================================
--- contrib/davical/dba/rrule_functions-8.1.sql	(revision 3733)
+++ contrib/davical/dba/rrule_functions-8.1.sql	(revision 3733)
@@ -0,0 +1,709 @@
+/**
+* PostgreSQL Functions for RRULE handling
+*
+* @package rscds
+* @subpackage database
+* @author Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*
+* Coverage of this function set
+*  - COUNT & UNTIL are handled, generally
+*  - DAILY frequency, including BYDAY, BYMONTH, BYMONTHDAY, BYWEEKNO, BYMONTHDAY
+*  - WEEKLY frequency, including BYDAY, BYMONTH, BYMONTHDAY, BYWEEKNO, BYSETPOS
+*  - MONTHLY frequency, including BYDAY, BYMONTH, BYSETPOS
+*  - YEARLY frequency, including BYMONTH, BYMONTHDAY, BYSETPOS, BYDAY
+*
+* Not covered as yet
+*  - DAILY:   BYYEARDAY, BYSETPOS*
+*  - WEEKLY:  BYYEARDAY
+*  - MONTHLY: BYYEARDAY, BYMONTHDAY, BYWEEKNO
+*  - YEARLY:  BYYEARDAY
+*  - SECONDLY
+*  - MINUTELY
+*  - HOURLY
+*
+*/
+
+-- Create a composite type for the parts of the RRULE.
+DROP TYPE rrule_parts CASCADE;
+CREATE TYPE rrule_parts AS (
+  base TIMESTAMP WITH TIME ZONE,
+  until TIMESTAMP WITH TIME ZONE,
+  freq TEXT,
+  count INT,
+  interval INT,
+  bysecond INT[],
+  byminute INT[],
+  byhour INT[],
+  bymonthday INT[],
+  byyearday INT[],
+  byweekno INT[],
+  byday TEXT[],
+  bymonth INT[],
+  bysetpos INT[],
+  wkst TEXT
+);
+
+
+-- Create a function to parse the RRULE into it's composite type
+CREATE or REPLACE FUNCTION parse_rrule_parts( TIMESTAMP WITH TIME ZONE, TEXT ) RETURNS rrule_parts AS $$
+DECLARE
+  basedate   ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  result rrule_parts%ROWTYPE;
+  tempstr TEXT;
+BEGIN
+  result.base       := basedate;
+  result.until      := substring(repeatrule from 'UNTIL=([0-9TZ]+)(;|$)');
+  result.freq       := substring(repeatrule from 'FREQ=([A-Z]+)(;|$)');
+  result.count      := substring(repeatrule from 'COUNT=([0-9]+)(;|$)');
+  result.interval   := COALESCE(substring(repeatrule from 'INTERVAL=([0-9]+)(;|$)')::int, 1);
+  result.wkst       := substring(repeatrule from 'WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)');
+
+  result.byday      := string_to_array( substring(repeatrule from 'BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)'), ',');
+
+  result.byyearday  := string_to_array(substring(repeatrule from 'BYYEARDAY=([0-9,+-]+)(;|$)'), ',');
+  result.byweekno   := string_to_array(substring(repeatrule from 'BYWEEKNO=([0-9,+-]+)(;|$)'), ',');
+  result.bymonthday := string_to_array(substring(repeatrule from 'BYMONTHDAY=([0-9,+-]+)(;|$)'), ',');
+  result.bymonth    := string_to_array(substring(repeatrule from 'BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)'), ',');
+  result.bysetpos   := string_to_array(substring(repeatrule from 'BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)'), ',');
+
+  result.bysecond   := string_to_array(substring(repeatrule from 'BYSECOND=([0-9,]+)(;|$)'), ',');
+  result.byminute   := string_to_array(substring(repeatrule from 'BYMINUTE=([0-9,]+)(;|$)'), ',');
+  result.byhour     := string_to_array(substring(repeatrule from 'BYHOUR=([0-9,]+)(;|$)'), ',');
+
+  RETURN result;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
+CREATE or REPLACE FUNCTION rrule_month_byday_set( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  byday ALIAS FOR $2;
+  dayrule TEXT;
+  i INT;
+  dow INT;
+  index INT;
+  first_dow INT;
+  each_day TIMESTAMP WITH TIME ZONE;
+  this_month INT;
+  results TIMESTAMP WITH TIME ZONE[];
+BEGIN
+
+  IF byday IS NULL THEN
+    -- We still return the single date as a SET
+    RETURN NEXT in_time;
+    RETURN;
+  END IF;
+
+  -- It seems that the array needs to be initialised to non-null values in PostgreSQL 8.1
+  -- The date chosen is the earliest valid date in PostgreSQL 8.1, and nobody should be
+  -- scheduling appointments for then!
+  FOR i IN 1..31 LOOP
+    results[i] := '4713-01-01 00:00:00 BC'::timestamp;
+  END LOOP;
+
+  i := 1;
+  dayrule := byday[i];
+  WHILE dayrule IS NOT NULL LOOP
+    dow := position(substring( dayrule from '..$') in 'SUMOTUWETHFRSA') / 2;
+    each_day := date_trunc( 'month', in_time ) + (in_time::time)::interval;
+    this_month := date_part( 'month', in_time );
+    first_dow := date_part( 'dow', each_day );
+
+    -- Coerce each_day to be the first 'dow' of the month
+    each_day := each_day - ( first_dow::text || 'days')::interval
+                        + ( dow::text || 'days')::interval
+                        + CASE WHEN dow < first_dow THEN '1 week'::interval ELSE '0s'::interval END;
+
+    -- RAISE NOTICE 'From "%", for % finding dates. dow=%, this_month=%, first_dow=%', each_day, dayrule, dow, this_month, first_dow;
+    IF length(dayrule) > 2 THEN
+      index := (substring(dayrule from '^[0-9-]+'))::int;
+
+      IF index = 0 THEN
+        RAISE NOTICE 'Ignored invalid BYDAY rule part "%".', bydayrule;
+      ELSIF index > 0 THEN
+        -- The simplest case, such as 2MO for the second monday
+        each_day := each_day + ((index - 1)::text || ' weeks')::interval;
+      ELSE
+        each_day := each_day + '5 weeks'::interval;
+        WHILE date_part('month', each_day) != this_month LOOP
+          each_day := each_day - '1 week'::interval;
+        END LOOP;
+        -- Note that since index is negative, (-2 + 1) == -1, for example
+        index := index + 1;
+        IF index < 0 THEN
+          each_day := each_day + (index::text || ' weeks')::interval ;
+        END IF;
+      END IF;
+
+      -- Sometimes (e.g. 5TU or -5WE) there might be no such date in some months
+      IF date_part('month', each_day) = this_month THEN
+        results[date_part('day',each_day)] := each_day;
+        -- RAISE NOTICE 'Added "%" to list for %', each_day, dayrule;
+      END IF;
+
+    ELSE
+      -- Return all such days that are within the given month
+      WHILE date_part('month', each_day) = this_month LOOP
+        index := date_part('day',each_day);
+        -- RAISE NOTICE 'Adding "%" to list[%] for %', each_day, index, dayrule;
+        results[index] := each_day;
+        -- RAISE NOTICE 'Added "%" to list for %', each_day, dayrule;
+        each_day := each_day + '1 week'::interval;
+      END LOOP;
+    END IF;
+
+    i := i + 1;
+    dayrule := byday[i];
+  END LOOP;
+
+  FOR i IN 1..31 LOOP
+    IF results[i] > '4713-01-01 00:00:00 BC'::timestamp THEN
+      RETURN NEXT results[i];
+    END IF;
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+-- Return a SETOF dates within the month of a particular date which match a string of BYDAY rule specifications
+CREATE or REPLACE FUNCTION rrule_month_bymonthday_set( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  bymonthday ALIAS FOR $2;
+  month_start TIMESTAMP WITH TIME ZONE;
+  daysinmonth INT;
+  i INT;
+BEGIN
+
+  month_start := date_trunc( 'month', in_time ) + (in_time::time)::interval;
+  daysinmonth := date_part( 'days', (month_start + interval '1 month') - interval '1 day' );
+
+  FOR i IN 1..31 LOOP
+    EXIT WHEN bymonthday[i] IS NULL;
+
+    CONTINUE WHEN bymonthday[i] > daysinmonth;
+    CONTINUE WHEN bymonthday[i] < (-1 * daysinmonth);
+
+    IF bymonthday[i] > 0 THEN
+      RETURN NEXT month_start + ((bymonthday[i] - 1)::text || 'days')::interval;
+    ELSIF bymonthday[i] < 0 THEN
+      RETURN NEXT month_start + ((daysinmonth + bymonthday[i])::text || 'days')::interval;
+    ELSE
+      RAISE NOTICE 'Ignored invalid BYMONTHDAY part "%".', bymonthday[i];
+    END IF;
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Return a SETOF dates within the week of a particular date which match a single BYDAY rule specification
+CREATE or REPLACE FUNCTION rrule_week_byday_set( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  in_time ALIAS FOR $1;
+  byday ALIAS FOR $2;
+  dayrule TEXT;
+  dow INT;
+  our_day TIMESTAMP WITH TIME ZONE;
+  i INT;
+BEGIN
+
+  IF byday IS NULL THEN
+    -- We still return the single date as a SET
+    RETURN NEXT in_time;
+    RETURN;
+  END IF;
+
+  our_day := date_trunc( 'week', in_time ) + (in_time::time)::interval;
+
+  i := 1;
+  dayrule := byday[i];
+  WHILE dayrule IS NOT NULL LOOP
+    dow := position(dayrule in 'SUMOTUWETHFRSA') / 2;
+    RETURN NEXT our_day + ((dow - 1)::text || 'days')::interval;
+    i := i + 1;
+    dayrule := byday[i];
+  END LOOP;
+
+  RETURN;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+CREATE or REPLACE FUNCTION event_has_exceptions( TEXT ) RETURNS BOOLEAN AS $$
+  SELECT $1 ~ E'\nRECURRENCE-ID(;TZID=[^:]+)?:[[:space:]]*[[:digit:]]{8}(T[[:digit:]]{6})?'
+$$ LANGUAGE 'sql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the weekday of this date against the array of weekdays from the BYDAY rule (FREQ=WEEKLY or less)
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_byday_rule( TIMESTAMP WITH TIME ZONE, TEXT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  byday ALIAS FOR $2;
+BEGIN
+  -- Note that this doesn't work for MONTHLY/YEARLY BYDAY clauses which might have numbers prepended
+  -- so don't call it that way...
+  IF byday IS NOT NULL THEN
+    RETURN ( substring( to_char( testme, 'DY') for 2 from 1) = ANY (byday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the month of this date against the array of months from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_bymonth_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  bymonth ALIAS FOR $2;
+BEGIN
+  IF bymonth IS NOT NULL THEN
+    RETURN ( date_part( 'month', testme) = ANY (bymonth) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the day in month of this date against the array of monthdays from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_bymonthday_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  bymonthday ALIAS FOR $2;
+BEGIN
+  IF bymonthday IS NOT NULL THEN
+    RETURN ( date_part( 'day', testme) = ANY (bymonthday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Test the day in year of this date against the array of yeardays from the rule
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION test_byyearday_rule( TIMESTAMP WITH TIME ZONE, INT[] ) RETURNS BOOLEAN AS $$
+DECLARE
+  testme ALIAS FOR $1;
+  byyearday ALIAS FOR $2;
+BEGIN
+  IF byyearday IS NOT NULL THEN
+    RETURN ( date_part( 'doy', testme) = ANY (byyearday) );
+  END IF;
+  RETURN TRUE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Given a cursor into a set, process the set returning the subset matching the BYSETPOS
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_bysetpos_filter( REFCURSOR, INT[] ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  curse ALIAS FOR $1;
+  bysetpos ALIAS FOR $2;
+  valid_date TIMESTAMP WITH TIME ZONE;
+  i INT;
+  setsize INT;
+  ourset TIMESTAMP WITH TIME ZONE[];
+BEGIN
+
+  IF bysetpos IS NULL THEN
+    LOOP
+      FETCH curse INTO valid_date;
+      EXIT WHEN NOT FOUND;
+      RETURN NEXT valid_date;
+    END LOOP;
+  ELSE
+    setsize := 0;
+    FOR i IN 1..366 LOOP
+      FETCH curse INTO valid_date;
+      EXIT WHEN NOT FOUND;
+      ourset[i] := valid_date;
+      setsize = setsize + 1;
+    END LOOP;
+    -- RAISE NOTICE 'We have % in our set.', setsize;
+    FOR i IN 1..366 LOOP
+      EXIT WHEN bysetpos[i] IS NULL;
+      IF bysetpos[i] > 0 THEN
+        valid_date := ourset[bysetpos[i]];
+        -- RAISE NOTICE 'Extracted % from position %.', valid_date, bysetpos[i];
+      ELSE
+        valid_date := ourset[ setsize + bysetpos[i] + 1 ];
+        -- RAISE NOTICE 'Extracted % from position (% % + 1) = %.', valid_date, setsize, bysetpos[i], setsize + bysetpos[i] + 1;
+      END IF;
+      IF valid_date IS NOT NULL THEN
+        RETURN NEXT valid_date;
+      END IF;
+    END LOOP;
+  END IF;
+  CLOSE curse;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another day's worth of events: i.e. one day that matches the criteria, since we don't
+-- currently implement sub-day scheduling.
+--
+-- This is cheeky:  The incrementing by a day is done outside the call, so we either return the
+-- empty set (if the input date fails our filters) or we return a set containing the input date.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION daily_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL AND NOT date_part('month',after) = ANY ( rrule.bymonth ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byweekno IS NOT NULL AND NOT date_part('week',after) = ANY ( rrule.byweekno ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byyearday IS NOT NULL AND NOT date_part('doy',after) = ANY ( rrule.byyearday ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.bymonthday IS NOT NULL AND NOT date_part('day',after) = ANY ( rrule.bymonthday ) THEN
+    RETURN;
+  END IF;
+
+  IF rrule.byday IS NOT NULL AND NOT substring( to_char( after, 'DY') for 2 from 1) = ANY ( rrule.byday ) THEN
+    RETURN;
+  END IF;
+
+  -- Since we don't do BYHOUR, BYMINUTE or BYSECOND yet this becomes a trivial
+  RETURN NEXT after;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another week's worth of events
+--
+-- Doesn't handle truly obscure and unlikely stuff like BYWEEKNO=5;BYMONTH=1;BYDAY=WE,TH,FR;BYSETPOS=-2
+-- Imagine that.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION weekly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  rowvar RECORD;
+  curse REFCURSOR;
+  weekno INT;
+  i INT;
+BEGIN
+
+  IF rrule.byweekno IS NOT NULL THEN
+    weekno := date_part('week',after);
+    IF NOT weekno = ANY ( rrule.byweekno ) THEN
+      RETURN;
+    END IF;
+  END IF;
+
+  OPEN curse FOR SELECT r FROM rrule_week_byday_set(after, rrule.byday ) r;
+  FOR rowvar IN SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d LOOP
+    RETURN NEXT rowvar.d;
+  END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another month's worth of events
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION monthly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  rowvar RECORD;
+  curse REFCURSOR;
+  setpos INT;
+  i INT;
+BEGIN
+
+  /**
+  * Need to investigate whether it is legal to set both of these, and whether
+  * we are correct to UNION the results, or whether we should INTERSECT them.
+  * So at this point, we refer to the specification, which grants us this
+  * wonderfully enlightening vision:
+  *
+  *     If multiple BYxxx rule parts are specified, then after evaluating the
+  *     specified FREQ and INTERVAL rule parts, the BYxxx rule parts are
+  *     applied to the current set of evaluated occurrences in the following
+  *     order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR,
+  *     BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.
+  *
+  * My guess is that this means 'INTERSECT'
+  */
+  IF rrule.byday IS NOT NULL AND rrule.bymonthday IS NOT NULL THEN
+    OPEN curse FOR SELECT r FROM rrule_month_byday_set(after, rrule.byday ) r
+                INTERSECT SELECT r FROM rrule_month_bymonthday_set(after, rrule.bymonthday ) r
+                    ORDER BY 1;
+  ELSIF rrule.bymonthday IS NOT NULL THEN
+    OPEN curse FOR SELECT r FROM rrule_month_bymonthday_set(after, rrule.bymonthday ) r ORDER BY 1;
+  ELSE
+    OPEN curse FOR SELECT r FROM rrule_month_byday_set(after, rrule.byday ) r ORDER BY 1;
+  END IF;
+
+  FOR rowvar IN SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d LOOP
+    RETURN NEXT rowvar.d;
+  END LOOP;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- If this is YEARLY;BYMONTH, abuse MONTHLY;BYMONTH for everything except the BYSETPOS
+-- FIXME: it is wrong to abuse MONTHLY;BYMONTH in this way. We should write YEARLY;BYMONTH properly.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_yearly_bymonth_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  current_base TIMESTAMP WITH TIME ZONE;
+  rowvar RECORD;
+  rr rrule_parts;
+  i INT;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL THEN
+    -- Ensure we don't pass BYSETPOS down
+    rr := rrule;
+    rr.bysetpos := NULL;
+    FOR i IN 1..12 LOOP
+      EXIT WHEN rr.bymonth[i] IS NULL;
+      current_base := date_trunc( 'year', after ) + ((rr.bymonth[i] - 1)::text || ' months')::interval + (after::time)::interval;
+      FOR rowvar IN SELECT d FROM monthly_set(current_base,rr) d LOOP
+        RETURN NEXT rowvar.d;
+      END LOOP;
+    END LOOP;
+  ELSE
+    -- We don't yet implement byweekno, byblah
+    RETURN NEXT after;
+  END IF;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Return another year's worth of events
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION yearly_set( TIMESTAMP WITH TIME ZONE, rrule_parts ) RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  after ALIAS FOR $1;
+  rrule ALIAS FOR $2;
+  current_base TIMESTAMP WITH TIME ZONE;
+  rowvar RECORD;
+  curse REFCURSOR;
+  curser REFCURSOR;
+  i INT;
+BEGIN
+
+  IF rrule.bymonth IS NOT NULL THEN
+    OPEN curse FOR SELECT r FROM rrule_yearly_bymonth_set(after, rrule ) r;
+    FOR rowvar IN SELECT d FROM rrule_bysetpos_filter(curse,rrule.bysetpos) d LOOP
+      current_base := date_trunc( 'day', rowvar.d ) + (after::time)::interval;
+      RETURN NEXT current_base;
+    END LOOP;
+  ELSE
+    -- We don't yet implement byweekno, byblah
+    RETURN NEXT after;
+  END IF;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- Combine all of that into something which we can use to generate a series from an arbitrary DTSTART/RRULE
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_event_instances_range( TIMESTAMP WITH TIME ZONE, TEXT, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE, INT )
+                                         RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  basedate ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  mindate ALIAS FOR $3;
+  maxdate ALIAS FOR $4;
+  max_count ALIAS FOR $5;
+  loopmax INT;
+  loopcount INT;
+  base_day TIMESTAMP WITH TIME ZONE;
+  current_base TIMESTAMP WITH TIME ZONE;
+  current TIMESTAMP WITH TIME ZONE;
+  rowvar RECORD;
+  rrule rrule_parts%ROWTYPE;
+BEGIN
+  loopcount := 0;
+
+  SELECT * INTO rrule FROM parse_rrule_parts( basedate, repeatrule );
+  IF rrule.count IS NOT NULL THEN
+    loopmax := rrule.count;
+  ELSE
+    loopmax := max_count;
+  END IF;
+
+  current_base := basedate;
+  base_day := date_trunc('day',basedate);
+  WHILE loopcount < loopmax AND current_base <= maxdate LOOP
+    IF rrule.freq = 'DAILY' THEN
+      FOR rowvar IN SELECT d FROM daily_set(current_base,rrule) d WHERE d >= base_day LOOP
+        current := rowvar.d;
+--        IF test_byday_rule(current,rrule.byday) AND test_bymonthday_rule(current,rrule.bymonthday) AND test_bymonth_rule(current,rrule.bymonth) THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+--        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' days')::interval;
+    ELSIF rrule.freq = 'WEEKLY' THEN
+      FOR rowvar IN SELECT d FROM weekly_set(current_base,rrule) d WHERE d >= base_day LOOP
+        current := rowvar.d;
+        IF test_byyearday_rule(current,rrule.byyearday)
+               AND test_bymonthday_rule(current,rrule.bymonthday)
+               AND test_bymonth_rule(current,rrule.bymonth)
+        THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' weeks')::interval;
+    ELSIF rrule.freq = 'MONTHLY' THEN
+      FOR rowvar IN SELECT d FROM monthly_set(current_base,rrule) d WHERE d >= base_day LOOP
+        current := rowvar.d;
+--        IF /* test_byyearday_rule(current,rrule.byyearday)
+--               AND */ test_bymonth_rule(current,rrule.bymonth)
+--        THEN
+          EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+          IF current >= mindate THEN
+            RETURN NEXT current;
+          END IF;
+          loopcount := loopcount + 1;
+          EXIT WHEN loopcount >= loopmax;
+--        END IF;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' months')::interval;
+    ELSIF rrule.freq = 'YEARLY' THEN
+      FOR rowvar IN SELECT d FROM yearly_set(current_base,rrule) d WHERE d >= base_day LOOP
+        current := rowvar.d;
+        EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+        IF current >= mindate THEN
+          RETURN NEXT current;
+        END IF;
+        loopcount := loopcount + 1;
+        EXIT WHEN loopcount >= loopmax;
+      END LOOP;
+      current_base := current_base + (rrule.interval::text || ' years')::interval;
+    ELSE
+      RAISE NOTICE 'A frequency of "%" is not handled', rrule.freq;
+      RETURN;
+    END IF;
+    EXIT WHEN rrule.until IS NOT NULL AND current > rrule.until;
+  END LOOP;
+  -- RETURN QUERY;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- A simplified DTSTART/RRULE only interface which applies some performance assumptions
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION event_instances( TIMESTAMP WITH TIME ZONE, TEXT )
+                                         RETURNS SETOF TIMESTAMP WITH TIME ZONE AS $$
+DECLARE
+  basedate ALIAS FOR $1;
+  repeatrule ALIAS FOR $2;
+  maxdate TIMESTAMP WITH TIME ZONE;
+  rowvar RECORD;
+BEGIN
+  maxdate := current_date + '10 years'::interval;
+  FOR rowvar IN SELECT d FROM rrule_event_instances_range( basedate, repeatrule, basedate, maxdate, 300 ) d LOOP
+    RETURN NEXT rowvar.d;
+  END LOOP;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+------------------------------------------------------------------------------------------------------
+-- In most cases we just want to know if there *is* an event overlapping the range, so we have a
+-- specific function for that.  Note that this is *not* strict, and can be called with NULLs.
+------------------------------------------------------------------------------------------------------
+CREATE or REPLACE FUNCTION rrule_event_overlaps( TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE, TEXT, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH TIME ZONE )
+                                         RETURNS BOOLEAN AS $$
+DECLARE
+  dtstart ALIAS FOR $1;
+  dtend ALIAS FOR $2;
+  repeatrule ALIAS FOR $3;
+  in_mindate ALIAS FOR $4;
+  in_maxdate ALIAS FOR $5;
+  base_date TIMESTAMP WITH TIME ZONE;
+  mindate TIMESTAMP WITH TIME ZONE;
+  maxdate TIMESTAMP WITH TIME ZONE;
+BEGIN
+
+  IF dtstart IS NULL THEN
+    RETURN NULL;
+  END IF;
+  IF dtend IS NULL THEN
+    base_date := dtstart;
+  ELSE
+    base_date := dtend;
+  END IF;
+
+  IF in_mindate IS NULL THEN
+    mindate := current_date - '10 years'::interval;
+  ELSE
+    mindate := in_mindate;
+  END IF;
+
+  IF in_maxdate IS NULL THEN
+    maxdate := current_date + '10 years'::interval;
+  ELSE
+    -- If we add the duration onto the event, then an overlap occurs if dtend <= increased end of range.
+    maxdate := in_maxdate + (base_date - dtstart);
+  END IF;
+
+  IF repeatrule IS NULL THEN
+    RETURN (dtstart <= maxdate AND base_date >= mindate);
+  END IF;
+
+  SELECT d INTO mindate FROM rrule_event_instances_range( base_date, repeatrule, mindate, maxdate, 60 ) d LIMIT 1;
+  RETURN FOUND;
+
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE;
+
+
+CREATE or REPLACE FUNCTION icalendar_interval_to_SQL( TEXT ) RETURNS interval AS $function$
+  SELECT CASE WHEN substring($1,1,1) = '-' THEN -1 ELSE 1 END * regexp_replace( regexp_replace($1, '[PT-]', '', 'g'), '([A-Z])', E'\\1 ', 'g')::interval;
+$function$ LANGUAGE 'SQL' IMMUTABLE STRICT;
Index: contrib/davical/dba/supported_locales.sql
===================================================================
--- contrib/davical/dba/supported_locales.sql	(revision 3733)
+++ contrib/davical/dba/supported_locales.sql	(revision 3733)
@@ -0,0 +1,24 @@
+TRUNCATE supported_locales;
+
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'en_EN', 'English', 'English' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'de_DE', 'German',  'Deutsch' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'es_ES', 'Spanish', 'EspaÃ±ol' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'fr_FR', 'French',  'FranÃ§ais' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'ru_RU', 'Russian',  'Ð ÑÑÑÐºÐžÐ¹' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'nl_NL', 'Netherlands',  'Nederlands' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'pl_PL', 'Polish',  'Polski' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'hu_HU', 'Hungarian',  'Magyar' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'ja_JP', 'Japanese',  'æ¥æ¬èª' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'it_IT', 'Italian',  'Italiano' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'sv_SE', 'Swedish',  'Svenska' );
Index: contrib/davical/dba/update-davical-database
===================================================================
--- contrib/davical/dba/update-davical-database	(revision 3733)
+++ contrib/davical/dba/update-davical-database	(revision 3733)
@@ -0,0 +1,460 @@
+#!/usr/bin/env perl
+#
+# Update the DAViCal database by repeatedly applying patches to it
+# in the correct order.
+#
+
+use warnings;
+use strict;
+
+use DBI;
+use POSIX qw(floor);
+use Getopt::Long qw(:config permute);  # allow mixed args.
+
+# Options variables
+my $debug  = 0;
+my $dbname = "davical";
+my $dbport = 5432;
+my $dbuser = "davical_dba";
+my $dbpass = "";
+my $dbhost = "";
+my $appuser = "davical_app";
+my $helpmeplease = 0;
+my $apply_patches = 1;
+my $revoke_list = "";
+my $force_owner = "";
+my $config_file = "config/administration.yml";
+
+my $dbadir = $0;
+$dbadir =~ s#/[^/]*$##;
+my $patchdir = $dbadir . "/patches";
+
+#
+# We look in a few places for the config file.  First relative to
+# where we are, then relative to the code we are running, then we
+# start to look in absolute locations.  Then we give up :-)
+if ( ! -f $config_file ) {
+  $config_file = $0;
+  $config_file =~ s{[^/]+/update-[a-z]+-database}{config/administration.yml};
+}
+if ( ! -f $config_file ) {
+  $config_file = "/etc/davical/administration.yml";
+}
+if ( -f $config_file ) {
+  use YAML qw( LoadFile );
+
+  my ($ycfg) = LoadFile($config_file);
+  $dbuser  = $ycfg->{'admin_db_user'} if ( defined($ycfg->{'admin_db_user'}));
+  $dbpass  = $ycfg->{'admin_db_pass'} if ( defined($ycfg->{'admin_db_pass'}));
+  $dbhost  = $ycfg->{'admin_db_host'} if ( defined($ycfg->{'admin_db_host'}));
+  $dbname  = $ycfg->{'admin_db_name'} if ( defined($ycfg->{'admin_db_name'}));
+  $dbport  = $ycfg->{'admin_db_port'} if ( defined($ycfg->{'admin_db_port'}));
+  $appuser = $ycfg->{'app_db_user'}   if ( defined($ycfg->{'app_db_user'}));
+}
+
+GetOptions ('debug!'    => \$debug,
+            'dbname=s'  => \$dbname,
+            'dbuser=s'  => \$dbuser,
+            'dbpass=s'  => \$dbpass,
+            'dbport=s'  => \$dbport,
+            'dbhost=s'  => \$dbhost,
+            'appuser=s' => \$appuser,
+            'patch!'    => \$apply_patches,
+            'owner=s'   => \$force_owner,
+            'revoke=s'  => \$revoke_list,
+            'help'      => \$helpmeplease  );
+
+show_usage() if ( $helpmeplease );
+
+$revoke_list = ", ". $revoke_list if ( $revoke_list ne "" );
+
+
+############################################################
+# Open database connection. Note that the standard PostgreSQL
+# environment variables will also work with DBD::Pg.
+############################################################
+my $dsn = "dbi:Pg:dbname=$dbname";
+$dsn .= ";host=$dbhost" if ( "$dbhost" ne "" );
+$dsn .= ";port=$dbport" if ( $dbport != 5432 );
+
+print "Using database: $dbuser".'%'.$dbpass.'@'.$dsn."\n" if ( $debug );
+
+my $pg_version = get_postgresql_version();
+my $current_revision;
+my $last_results = '';  # Will hold the last SQL result from applying a patch
+
+if ( $apply_patches ) {
+  $current_revision = get_current_revision();
+  printf( "The database is version %.1lf currently at revision %d.%d.%d.\n", $pg_version, $current_revision->{'schema_major'}, $current_revision->{'schema_minor'}, $current_revision->{'schema_patch'} );
+
+  opendir( PATCHDIR, $patchdir ) or die "Can't open patch directory $patchdir";
+  my @patches = grep { /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)\.sql$/ } readdir(PATCHDIR);
+  closedir(PATCHDIR);
+
+  @patches = sort { compare_revisions(revision_hash($a),revision_hash($b), 1); } @patches;
+
+  my $applied = 0;
+
+  for ( my $i=0; $i <= $#patches;  $i++ ) {
+    printf( "Looking at patches[%d] (%s)\n", $i, $patches[$i]) if ( $debug );
+    if ( compare_revisions(revision_hash($patches[$i]),$current_revision) > 0 ) {
+      print "Applying patch $patches[$i] ... ";
+      if ( !apply_patch( $patches[$i] ) ) {
+        # Skip to the end unless the next patch is an alternate for the same version.
+        if ( defined($patches[$i+1]) && compare_revisions(revision_hash($patches[$i]),revision_hash($patches[$i+1])) == 0 ) {
+          print "failed.  Attempting next alternative.\n";
+          $applied--;
+        }
+        else {
+          print "failed!\n$last_results  ==> No further patches will be attempted!\n";
+          last;
+        }
+      }
+      else {
+        print "succeeded.\n";
+      }
+      $applied++;
+    }
+    else {
+      print "Patch $patches[$i] has already been applied.\n" if ( $debug );
+    }
+  }
+
+  if ( $applied ) {
+    print "Successfully applied $applied patches.\n";
+  }
+  else {
+    print "No patches were applied.\n";
+  }
+}
+
+# Ensure the locales data is up to date
+apply_sql_file( $dbadir, "supported_locales.sql" );
+print "Supported locales updated.\n";
+
+# update any views
+apply_sql_folder( $dbadir, 'views', "Updated view: " );
+
+# Ensure the functions are up to date
+apply_sql_file( $dbadir, "caldav_functions.sql" );
+print "CalDAV functions updated.\n";
+
+if ( $pg_version >= 8.3 ) {
+  apply_sql_file( $dbadir, "rrule_functions.sql" );
+}
+else {
+  apply_sql_file( $dbadir, "rrule_functions-8.1.sql",  );
+}
+print "RRULE functions updated.\n";
+
+# Ensure the permissions are up to date
+apply_permissions( $dbadir, "appuser_permissions.txt" );
+print "Database permissions updated.\n";
+
+# The End!
+exit 0;
+
+
+
+
+############################################################
+# Revision Hash - we either have a single parameter,
+# which is of the form "1.2.3" or we have three parameters.
+############################################################
+sub revision_hash {
+  my $rev = +{ 'schema_major', => 0, 'schema_minor' => 0, 'schema_patch' => 0, 'alternative' => '0' };
+  my $first = shift;
+  return $rev unless ( defined($first) );
+  if ( $first =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)([a-z]?)([^0-9]|$)/ ) {
+    $rev->{'schema_major'} = $1;
+    $rev->{'schema_minor'} = $2;
+    $rev->{'schema_patch'} = $3;
+    $rev->{'alternative'} = $4;
+  }
+  else {
+    $rev->{'schema_major'} = $first;
+    $rev->{'schema_minor'} = shift;
+    $rev->{'schema_patch'} = shift;
+    $rev->{'alternative'} = '0';
+  }
+  return $rev;
+}
+
+
+############################################################
+# Compare revisions
+############################################################
+sub compare_revisions {
+  my $a = shift;
+  my $b = shift;
+  my $test_alt = shift;
+
+  return -1 if ( $a->{'schema_major'} < $b->{'schema_major'} );
+  return  1 if ( $a->{'schema_major'} > $b->{'schema_major'} );
+
+  return -1 if ( $a->{'schema_minor'} < $b->{'schema_minor'} );
+  return  1 if ( $a->{'schema_minor'} > $b->{'schema_minor'} );
+
+  return -1 if ( $a->{'schema_patch'} < $b->{'schema_patch'} );
+  return  1 if ( $a->{'schema_patch'} > $b->{'schema_patch'} );
+
+  if ( defined($test_alt) ) {
+    return -1 if ( $a->{'alternative'} lt $b->{'alternative'} );
+    return  1 if ( $a->{'alternative'} gt $b->{'alternative'} );
+  }
+
+  return 0;
+
+}
+
+
+
+############################################################
+=item folder_ordering()
+Function to allow us to sort folders which may have a number
+prefix.
+=cut
+############################################################
+sub folder_ordering {
+  my $a = shift;
+  my $b = shift;
+
+  my $numeric_a = 999999;
+  my $numeric_b = 999999;
+  if ( $a =~ m{^(\d+)-} ) { $numeric_a = $1; }
+  if ( $b =~ m{^(\d+)-} ) { $numeric_b = $1; }
+
+  return -1 if ( $numeric_a < $numeric_b );
+  return  1 if ( $numeric_a > $numeric_b );
+
+  # Fall back on alphanumeric comparison
+  return -1 if ( $a lt $b );
+  return  1 if ( $a lt $b );
+
+  return 0;
+}
+
+
+
+############################################################
+# Get the current version of PostgreSQL
+############################################################
+sub get_postgresql_version {
+
+  my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 0 } ) or die "Can't connect to database $dbname";
+
+  my $current_version = $dbh->prepare( <<EOQ  ) or die $dbh->errstr;
+  SELECT regexp_replace( split_part( version(), ' ', 2), E'\.[0-9]\$', '')
+EOQ
+
+  if ( $current_version->execute() ) {
+    my $version = $current_version->fetchrow_arrayref();
+    undef $current_version;
+    $dbh->disconnect;
+    $version->[0] =~ s/\D+$//;  # It seems the regex in SQL is broken in 8.1, at least on CentOS 5.3
+    return $version->[0];
+  }
+  else {
+    die "ERROR: Cannot read current revision from database.";
+  }
+
+}
+
+
+############################################################
+# Get the current revision
+############################################################
+sub get_current_revision {
+
+  my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 0 } ) or die "Can't connect to database $dbname";
+
+  my $current_revision = $dbh->prepare( <<EOQ  ) or die $dbh->errstr;
+  SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1
+EOQ
+
+  if ( $current_revision->execute() ) {
+    my $revision = $current_revision->fetchrow_hashref();
+    undef $current_revision;
+    $dbh->disconnect;
+    return $revision;
+  }
+  else {
+    die "ERROR: Cannot read current revision from database.";
+  }
+
+}
+
+
+
+############################################################
+# Apply a DB Patch File
+############################################################
+sub apply_patch {
+
+  my $patch = shift;
+
+  apply_sql_file( $patchdir, $patch );
+
+  $current_revision = get_current_revision();
+  if ( compare_revisions($current_revision,revision_hash($patch)) != 0 ) {
+    printf( "Failed to apply revision %s to the database!\n", $patch ) if ( $debug );
+    return 0;
+  }
+  return 1;  # Success
+}
+
+
+
+############################################################
+# Apply SQL File
+#  Note that this stuffs the password into an environment
+#  variable, which isn't ideal.  If you use a .pgpass you
+#  can bypass that issue, but you still need it on the command
+#  line for this program until I get a patch from someone.
+############################################################
+sub apply_sql_file {
+
+  my $sqldir  = shift;
+  my $sqlfile = shift;
+
+  my @psql_opts = ( "psql", "-q", "-f", $sqldir."/".$sqlfile );
+  push @psql_opts, "-h", $dbhost if ( $dbhost ne "" );
+  push @psql_opts, "-p", "$dbport" if ( $dbport != 5432 );
+  push @psql_opts, "-U", $dbuser if ( $dbuser ne "" );
+  push @psql_opts, $dbname;    # It seems that FreeBSD has a poorer argument parsing library so non-option arguments must be last
+  $ENV{'PGPASS'} = $dbpass if ( $dbpass ne "" );
+
+  my $command = join ' ', @psql_opts;
+  $last_results = `$command 2>&1 1>/dev/null`;
+
+  $last_results =~ s/^.*WARNING:  there is no transaction in progress\s$//m;
+  $last_results =~ s/^.*NOTICE: //m;
+}
+
+
+############################################################
+=item apply_sql_folder
+Applies the SQL files in a folder in order, with some magic to apply
+specifically versioned ones by preference.
+=cut
+sub apply_sql_folder {
+  my $dbadir = shift;
+  my $folder_name = shift;
+  my $announce_prefix = shift;
+
+  my $folder = $dbadir . '/' . $folder_name;
+  opendir( FOLDER, $folder ) or die "Can't open SQL directory $folder";
+  my @sql_files = grep { /^[^.].*\.sql$/ } readdir(FOLDER);
+  closedir(FOLDER);
+  @sql_files = grep( !/-\d+.\d+\.sql$/, @sql_files);
+  @sql_files = sort { folder_ordering($a,$b); } @sql_files;
+
+  for ( my $i=0; $i <= $#sql_files;  $i++ ) {
+    my $apply_file = $sql_files[$i];
+    my $testfile = $folder . '/' . $apply_file;
+    $testfile =~ s{\.sql$}{-$pg_version.sql};
+    $apply_file = $testfile if ( -f $testfile );
+
+    apply_sql_file( $folder, $apply_file );
+    print $announce_prefix, $apply_file, " applied.\n";
+  }
+
+}
+
+
+############################################################
+# Apply database permissions from file
+############################################################
+sub apply_permissions {
+
+  my $sqldir  = shift;
+  my $permsfile = shift;
+
+  open PERMS, '<', $sqldir."/".$permsfile;
+  my $dbh = DBI->connect($dsn, $dbuser, $dbpass, { AutoCommit => 1 } ) or die "Can't connect to database $dbname";
+
+  my $sql;
+  my $current_grant;
+
+  while( <PERMS> ) {
+    next if ( /^\s*(#|--)/ );
+
+    /^\s*GRANT\s+(\S.*)\s*$/i && do {
+      $current_grant = $1;
+    };
+
+    /^\s*ON\s+(\S.*)\s*$/i && do {
+      defined($current_grant) or die "No GRANT before ON in $permsfile\n";
+      my $doohickey = $1;
+
+      if ( $revoke_list ne "" ) {
+        # TODO: we should really loop through the revoke_list so that a single non-existent
+        # user doesn't cause this whole statement to fail.
+        $sql = sprintf( "REVOKE ALL ON %s FROM %s %s", $doohickey, $appuser, $revoke_list );
+        print $sql, "\n" if ( $debug );
+        $dbh->do($sql);
+      }
+
+      $sql = sprintf( "GRANT %s on %s to %s", $current_grant, $doohickey, $appuser );
+      print $sql, "\n" if ( $debug );
+      $dbh->do($sql);
+
+      if ( $force_owner ne "" ) {
+        if ( $doohickey =~ /_seq$/ ) {
+          $sql = sprintf( "GRANT ALL on %s to %s", $doohickey, $force_owner );
+        }
+        else {
+          $sql = sprintf( "ALTER TABLE %s OWNER to %s", $doohickey, $force_owner );
+        }
+        print $sql, "\n" if ( $debug );
+        $dbh->do($sql);
+      }
+    };
+
+  }
+  close(PERMS);
+  $dbh->disconnect;
+}
+
+
+
+############################################################
+# Tell the nice user how we do things.  Short and sweet.
+############################################################
+sub show_usage {
+    print <<OPTHELP;
+
+update-davical-database [options]
+
+Options are:
+    --debug           Turn on debugging
+    --dbname  name    The database to dig into
+    --dbuser  name    Connect to the database as this user.
+    --dbport  5432    Connect to the database on this port.
+    --dbhost  name    Connect to the database on this host.
+    --appuser name    The database username which the application uses for it's
+                      database connection.
+    --owner name      The database username which is used for administrative
+                      access to the database.  This option forces the tables
+                      to be owned by this user (default: not present).
+    --nopatch         Don't try and apply any patches
+    --revoke  name    Revoke permissions from this user
+
+The program will apply any patches to the database which have
+not yet been applied, run any desired data patch scripts and set
+the correct minimum permissions for the web application user.
+
+Rather than providing a password on the command-line it is recommended
+that you use a .pgpass file in your home directory to hold the database
+password.  This file must be mode 600 to work and should have lines
+like:
+
+hostname:port:database:username:password
+
+Each bit can be replaced by an asterisk, e.g:
+*:*:davical:davical_dba:53cr3t
+
+OPTHELP
+    exit 0;
+}
+
Index: contrib/davical/dba/sample-data.sql
===================================================================
--- contrib/davical/dba/sample-data.sql	(revision 3733)
+++ contrib/davical/dba/sample-data.sql	(revision 3733)
@@ -0,0 +1,118 @@
+-- Some sample data to prime the database...
+-- base-data.sql should be processed before this
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 2, TRUE, current_date, current_date, 'andrew', '**x', 'Andrew McMillan', 'andrew@catalyst.net.nz' );
+INSERT INTO role_member (user_no, role_no) VALUES( 2, 1);
+
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 10, TRUE, current_date, current_date, 'user1', '**user1', 'User 1', 'user1@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 11, TRUE, current_date, current_date, 'user2', '**user2', 'User 2', 'user2@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 12, TRUE, current_date, current_date, 'user3', '**user3', 'User 3', 'user3@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 13, TRUE, current_date, current_date, 'user4', '**user4', 'User 4', 'user4@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 14, TRUE, current_date, current_date, 'user5', '**user5', 'User 5', 'user5@example.net' );
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 15, TRUE, current_date, current_date, 'User Six', '**user6', 'User 6', 'user6@example.net' );
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 20, TRUE, current_date, current_date, 'manager1', '**manager1', 'Manager 1', 'manager1@example.net' );
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 30, TRUE, current_date, current_date, 'assistant1', '**assistant1', 'Assistant 1', 'assistant1@example.net' );
+
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 100, TRUE, current_date, current_date, 'resource1', '*salt*unpossible', 'Resource 1', 'resource1@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 100, 4);
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 101, TRUE, current_date, current_date, 'resource2', '*salt*unpossible', 'Resource 2', 'resource2@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 101, 4);
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 200, TRUE, current_date, current_date, 'resmgr1', '*salt*unpossible', 'Resource Managers', 'resource-managers@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 200, 2);
+
+INSERT INTO usr ( user_no, active, email_ok, updated, username, password, fullname, email )
+    VALUES( 300, TRUE, current_date, current_date, 'teamclient1', '*salt*unpossible', 'Team for Client1', 'team-client1@example.net' );
+INSERT INTO role_member (user_no, role_no) VALUES( 300, 2);
+
+SELECT setval('usr_user_no_seq', 1000);
+
+UPDATE usr SET joined = '2009-06-01', updated = '2009-06-02';
+
+INSERT INTO collection (user_no, parent_container, dav_name, dav_etag,
+                 dav_displayname, is_calendar, created, modified,
+                 public_events_only, publicly_readable, collection_id, resourcetypes )
+    SELECT user_no, '/' || username || '/',  '/' || username || '/home/', md5(username),
+           username || ' home', TRUE, '2009-06-03', '2009-06-04',
+           FALSE, FALSE, user_no, '<DAV::collection/><urn:ietf:params:xml:ns:caldav:calendar/>'
+      FROM usr;
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 1, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+                 WHERE NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no)
+                   AND NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no)
+                   AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no);
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 2, user_no, fullname, privilege_to_bits(ARRAY['read','schedule-send','schedule-deliver']) FROM usr
+                 WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no)
+                   AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no);
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 3, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+                 WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no)
+                   AND NOT EXISTS(SELECT 1 FROM principal WHERE principal.user_no = usr.user_no);
+
+SELECT setval('dav_id_seq', 1000);
+
+-- Set the insert sequence to the next number, with a minimum of 1000
+SELECT setval('relationship_type_rt_id_seq', (SELECT 10 UNION SELECT rt_id FROM relationship_type ORDER BY 1 DESC LIMIT 1) );
+
+-- The resources for meetings
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 100, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 200, 101, 1 );
+
+-- The people who administer meetings
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 200, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 11, 200, 1 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 200, 1 );
+
+-- Between a PA and their Manager
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30,  20, 2 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30,  10, 2 );
+
+
+-- Between a team
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 20, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 30, 300, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 20, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 10, 3 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 300, 30, 3 );
+
+-- Granting explicit free/busy permission
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 11,  10, 4 );
+INSERT INTO relationship ( from_user, to_user, rt_id ) VALUES( 10,  11, 4 );
+
+
+UPDATE relationship r SET confers = (SELECT bit_confers FROM relationship_type rt WHERE rt.rt_id=r.rt_id);
+
+INSERT INTO group_member ( group_id, member_id)
+              SELECT g.principal_id, m.principal_id
+                FROM relationship JOIN principal g ON(to_user=g.user_no AND g.type_id = 3)    -- Group
+                                  JOIN principal m ON(from_user=m.user_no AND m.type_id IN (1,2)); -- Person | Resource
+
+INSERT INTO grants ( by_principal, to_principal, privileges, is_group )
+   SELECT pby.principal_id AS by_principal, pto.principal_id AS to_principal,
+                                  confers AS privileges, pto.type_id > 2 AS is_group
+     FROM relationship r JOIN usr f ON(f.user_no=r.from_user)
+                         JOIN usr t ON(t.user_no=r.to_user)
+                         JOIN principal pby ON(t.user_no=pby.user_no)
+                         JOIN principal pto ON(pto.user_no=f.user_no)
+     WHERE rt_id < 4 AND pby.type_id < 3;
Index: contrib/davical/dba/patches/1.1.12a.sql
===================================================================
--- contrib/davical/dba/patches/1.1.12a.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.12a.sql	(revision 3733)
@@ -0,0 +1,74 @@
+
+-- Add a numeric foreign key link between caldav_data and calendar_item to
+-- provide more efficient linking when the db has been initialised with a
+-- non POSIX collation.
+
+-- This alternative patch file is the same in/out revision as 1.1.12 but it works where
+-- for some reason some caldav_data rows do not have related calendar_item rows.
+
+BEGIN;
+SELECT check_db_revision(1,1,11);
+
+-- Add a column to the collection table to allow us to mark collections
+-- as publicly readable
+ALTER TABLE collection ADD COLUMN publicly_readable BOOLEAN DEFAULT FALSE;
+
+-- Add a numeric dav_id to link the caldav_data and calendar_item tables
+ALTER TABLE caldav_data ADD COLUMN dav_id INT8;
+ALTER TABLE calendar_item ADD COLUMN dav_id INT8;
+CREATE SEQUENCE caldav_data_dav_id_seq;
+GRANT SELECT,UPDATE ON caldav_data_dav_id_seq TO general;
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
+  DECLARE
+  BEGIN
+
+    IF TG_OP = ''DELETE'' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval(''caldav_data_dav_id_seq'');
+    END IF;
+
+    IF TG_OP = ''UPDATE'' THEN
+      IF OLD.dav_id = NEW.dav_id THEN
+        -- Nothing to do
+        RETURN NEW;
+      END IF;
+    END IF;
+
+    IF TG_RELNAME = ''caldav_data'' THEN
+      UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    ELSE
+      UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    END IF;
+
+    RETURN NEW;
+
+  END
+' LANGUAGE 'plpgsql';
+
+CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+-- Now, using the trigger, magically assign dav_id to all rows in caldav_data and calendar_item
+UPDATE caldav_data SET dav_id = dav_id;
+UPDATE caldav_data set dav_id = nextval('caldav_data_dav_id_seq') WHERE dav_id IS NULL;
+UPDATE calendar_item set dav_id = nextval('caldav_data_dav_id_seq') WHERE dav_id IS NULL;
+
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('caldav_data_dav_id_seq');
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET NOT NULL;
+ALTER TABLE caldav_data ADD CONSTRAINT caldav_data_dav_id_key UNIQUE (dav_id);
+
+ALTER TABLE calendar_item ADD CONSTRAINT calendar_item_dav_id_key UNIQUE (dav_id);
+
+SELECT new_db_revision(1,1,12, 'December' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.2.sql
===================================================================
--- contrib/davical/dba/patches/1.1.2.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.2.sql	(revision 3733)
@@ -0,0 +1,16 @@
+
+-- Bleah.  Missed another permission.
+
+BEGIN;
+SELECT check_db_revision(1,1,1);
+
+GRANT DELETE ON
+    tmp_password
+  , role_member
+  TO general;
+
+SELECT new_db_revision(1,1,2, 'February' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.1.sql
===================================================================
--- contrib/davical/dba/patches/1.2.1.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.1.sql	(revision 3733)
@@ -0,0 +1,91 @@
+
+-- This database update provides new tables for the Principal, for
+-- a consistent dav_resource which a principal, collection or calendar_item
+-- all inherit from.
+
+BEGIN;
+SELECT check_db_revision(1,1,12);
+
+-- Rename the caldav_data_dav_id_seq to dav_id_seq because we will use it
+-- for more tables than just caldav_data
+CREATE SEQUENCE dav_id_seq;
+SELECT setval('dav_id_seq', nextval('caldav_data_dav_id_seq'));
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE calendar_item ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
+  DECLARE
+  BEGIN
+
+    IF TG_OP = ''DELETE'' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval(''dav_id_seq'');
+    END IF;
+
+    IF TG_OP = ''UPDATE'' THEN
+      IF OLD.dav_id = NEW.dav_id THEN
+        -- Nothing to do
+        RETURN NEW;
+      END IF;
+    END IF;
+
+    IF TG_RELNAME = ''caldav_data'' THEN
+      UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    ELSE
+      UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    END IF;
+
+    RETURN NEW;
+
+  END
+' LANGUAGE 'plpgsql';
+
+-- CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+-- CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+
+-- Add a numeric collection_id to collection
+ALTER TABLE collection ADD COLUMN collection_id INT8;
+UPDATE collection SET collection_id = nextval('dav_id_seq');
+ALTER TABLE collection ALTER COLUMN collection_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE collection DROP CONSTRAINT collection_pkey CASCADE;
+ALTER TABLE collection ADD UNIQUE (user_no,dav_name);
+ALTER TABLE collection ADD CONSTRAINT collection_pkey PRIMARY KEY (collection_id);
+
+ALTER TABLE calendar_item ADD COLUMN collection_id INT8;
+INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified)
+  SELECT user_no, '/'||username||'/', '/'||username||'/home/', md5(user_no::text||'/'||username||'/home/'),
+         fullname, TRUE, current_timestamp, current_timestamp
+    FROM usr
+   WHERE NOT EXISTS (SELECT 1 FROM collection WHERE dav_name ~ ('^/'||username||'/'));
+
+UPDATE caldav_data SET dav_name = (select collection.dav_name FROM collection WHERE collection.user_no = caldav_data.user_no limit 1)
+                                  || regexp_replace( caldav_data.dav_name, '^.*/([^/]+)$', E'ex-\\1')
+            WHERE dav_name ~ '^/[^/]+/[^/]+$';
+UPDATE calendar_item SET collection_id = collection.collection_id
+          FROM collection WHERE collection.dav_name = regexp_replace( calendar_item.dav_name, '/[^/]+$', '/');
+ALTER TABLE calendar_item ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE calendar_item ADD CONSTRAINT
+       calendar_item_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX calendar_item_collection_id_fkey ON calendar_item(collection_id);
+
+ALTER TABLE caldav_data ADD COLUMN collection_id INT8;
+UPDATE caldav_data SET collection_id = collection.collection_id
+          FROM collection WHERE collection.dav_name = regexp_replace( caldav_data.dav_name, '/[^/]+$', '/');
+ALTER TABLE caldav_data ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE caldav_data ADD CONSTRAINT
+       caldav_data_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX caldav_data_collection_id_fkey ON caldav_data(collection_id);
+
+SELECT new_db_revision(1,2,1, 'Janvier' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.2.sql
===================================================================
--- contrib/davical/dba/patches/1.2.2.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.2.sql	(revision 3733)
@@ -0,0 +1,70 @@
+
+-- This database update provides new tables for the Principal, for
+-- a consistent dav_resource which a principal, collection or calendar_item
+-- all inherit from.
+
+BEGIN;
+SELECT check_db_revision(1,2,1);
+
+-- Only needs SELECT access by website.
+CREATE TABLE principal_type (
+  principal_type_id SERIAL PRIMARY KEY,
+  principal_type_desc TEXT
+);
+
+-- web needs SELECT,INSERT,UPDATE,DELETE
+CREATE TABLE principal (
+  principal_id SERIAL PRIMARY KEY,
+  type_id INT8 NOT NULL REFERENCES principal_type(principal_type_id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE,
+  user_no INT8 NULL REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  displayname TEXT,
+  active BOOLEAN
+);
+
+-- Allowing identification of group members.
+CREATE TABLE group_member (
+  group_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  member_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
+);
+CREATE UNIQUE INDEX group_member_pk ON group_member(group_id,member_id);
+CREATE INDEX group_member_sk ON group_member(member_id);
+
+
+-- Only needs SELECT access by website. dav_resource_type will be 'principal', 'collection', 'CalDAV:calendar' and so forth.
+CREATE TABLE dav_resource_type (
+  resource_type_id SERIAL PRIMARY KEY,
+  dav_resource_type TEXT,
+  resource_type_desc TEXT
+);
+
+CREATE TABLE dav_resource (
+  dav_id INT8 PRIMARY KEY DEFAULT nextval('dav_id_seq'),
+  dav_name TEXT,
+  resource_type_id INT8 REFERENCES dav_resource_type(resource_type_id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE,
+  owner_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE
+);
+
+
+CREATE TABLE privilege (
+  granted_to_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  resource_id INT8 REFERENCES dav_resource(dav_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  granted_by_id INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE,
+  can_read BOOLEAN,
+  can_write BOOLEAN,
+  can_write_properties BOOLEAN,
+  can_write_content BOOLEAN,
+  can_unlock BOOLEAN,
+  can_read_acl BOOLEAN,
+  can_read_current_user_privilege_set BOOLEAN,
+  can_write_acl BOOLEAN,
+  can_bind BOOLEAN,
+  can_unbind BOOLEAN,
+  can_read_free_busy BOOLEAN,
+  PRIMARY KEY (granted_to_id, resource_id)
+);
+
+SELECT new_db_revision(1,2,2, 'Fevrier' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.3.sql
===================================================================
--- contrib/davical/dba/patches/1.1.3.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.3.sql	(revision 3733)
@@ -0,0 +1,13 @@
+
+-- Bleah.  Missed another permission.
+
+BEGIN;
+SELECT check_db_revision(1,1,2);
+
+UPDATE relationship_type SET rt_isgroup = TRUE WHERE rt_id = 3 AND NOT rt_isgroup;
+
+SELECT new_db_revision(1,1,3, 'March' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.1a.sql
===================================================================
--- contrib/davical/dba/patches/1.2.1a.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.1a.sql	(revision 3733)
@@ -0,0 +1,91 @@
+
+-- This database update provides new tables for the Principal, for
+-- a consistent dav_resource which a principal, collection or calendar_item
+-- all inherit from.
+
+BEGIN;
+SELECT check_db_revision(1,1,12);
+
+-- Rename the caldav_data_dav_id_seq to dav_id_seq because we will use it
+-- for more tables than just caldav_data
+CREATE SEQUENCE dav_id_seq;
+SELECT setval('dav_id_seq', nextval('caldav_data_dav_id_seq'));
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE calendar_item ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
+  DECLARE
+  BEGIN
+
+    IF TG_OP = ''DELETE'' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval(''dav_id_seq'');
+    END IF;
+
+    IF TG_OP = ''UPDATE'' THEN
+      IF OLD.dav_id = NEW.dav_id THEN
+        -- Nothing to do
+        RETURN NEW;
+      END IF;
+    END IF;
+
+    IF TG_RELNAME = ''caldav_data'' THEN
+      UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    ELSE
+      UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    END IF;
+
+    RETURN NEW;
+
+  END
+' LANGUAGE 'plpgsql';
+
+-- CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+-- CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+
+-- Add a numeric collection_id to collection
+ALTER TABLE collection ADD COLUMN collection_id INT8;
+UPDATE collection SET collection_id = nextval('dav_id_seq');
+ALTER TABLE collection ALTER COLUMN collection_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE collection DROP CONSTRAINT collection_pkey CASCADE;
+ALTER TABLE collection ADD UNIQUE (user_no,dav_name);
+ALTER TABLE collection ADD CONSTRAINT collection_pkey PRIMARY KEY (collection_id);
+
+ALTER TABLE calendar_item ADD COLUMN collection_id INT8;
+INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified)
+  SELECT user_no, '/'||username||'/', '/'||username||'/home/', md5(user_no::text||'/'||username||'/home/'),
+         fullname, TRUE, current_timestamp, current_timestamp
+    FROM usr
+   WHERE NOT EXISTS (SELECT 1 FROM collection WHERE dav_name ~ ('^/'||username||'/'));
+
+UPDATE caldav_data SET dav_name = (select collection.dav_name FROM collection WHERE collection.user_no = caldav_data.user_no limit 1)
+                                  || regexp_replace( caldav_data.dav_name, '^.*/([^/]+)$', 'ex-\\1')
+            WHERE dav_name ~ '^/[^/]+/[^/]+$';
+UPDATE calendar_item SET collection_id = collection.collection_id
+          FROM collection WHERE collection.dav_name = regexp_replace( calendar_item.dav_name, '/[^/]+$', '/');
+ALTER TABLE calendar_item ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE calendar_item ADD CONSTRAINT
+       calendar_item_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX calendar_item_collection_id_fkey ON calendar_item(collection_id);
+
+ALTER TABLE caldav_data ADD COLUMN collection_id INT8;
+UPDATE caldav_data SET collection_id = collection.collection_id
+          FROM collection WHERE collection.dav_name = regexp_replace( caldav_data.dav_name, '/[^/]+$', '/');
+ALTER TABLE caldav_data ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE caldav_data ADD CONSTRAINT
+       caldav_data_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX caldav_data_collection_id_fkey ON caldav_data(collection_id);
+
+SELECT new_db_revision(1,2,1, 'Janvier' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.3.sql
===================================================================
--- contrib/davical/dba/patches/1.2.3.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.3.sql	(revision 3733)
@@ -0,0 +1,62 @@
+
+-- Add some more cascading updates and deletes.
+-- For databases created before PostgreSQL 8.0.
+
+-- I screwed up with version 0.9.6.1 and 0.9.6.2 in that the davical.sql
+-- file specified them as version 1.2.2 when in reality they were version
+-- 1.2.4 so we are going to clean that up here...
+
+CREATE TEMP TABLE db_version_check AS SELECT * FROM awl_db_revision;
+DELETE FROM db_version_check WHERE ((schema_major * 1000000) + (schema_minor * 1000) + schema_patch) < 1002002;
+INSERT INTO db_version_check (schema_id, schema_major, schema_minor, schema_patch, schema_name, applied_on )
+   SELECT (SELECT max(schema_id) + 1 FROM awl_db_revision WHERE ((schema_major * 1000000) + (schema_minor * 1000) + schema_patch) <= 1002002),
+            1, 2, 4, 'Avril', current_timestamp FROM pg_class JOIN pg_attribute ON (pg_class.oid = pg_attribute.attrelid)
+      WHERE pg_class.relname = 'calendar_item' AND attname = 'completed';
+DELETE FROM db_version_check
+       WHERE EXISTS( SELECT 1 FROM db_version_check
+                             WHERE schema_major < db_version_check.schema_major
+                                  OR (schema_major = db_version_check.schema_major AND schema_minor < db_version_check.schema_minor )
+                                  OR (schema_major = db_version_check.schema_major AND schema_minor = db_version_check.schema_minor AND schema_patch < db_version_check.schema_patch) );
+
+INSERT INTO awl_db_revision
+    SELECT * FROM db_version_check WHERE schema_major=1 AND schema_minor=2 AND schema_patch=4;
+
+BEGIN;
+SELECT check_db_revision(1,2,2);
+
+ALTER TABLE role_member DROP CONSTRAINT "$1";
+ALTER TABLE role_member ADD CONSTRAINT "role_member_role_no_fkey" FOREIGN KEY (role_no) REFERENCES roles(role_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE role_member DROP CONSTRAINT "$2";
+ALTER TABLE role_member ADD CONSTRAINT "role_member_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE session DROP CONSTRAINT "$1";
+ALTER TABLE session ADD CONSTRAINT "session_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE relationship DROP CONSTRAINT "$1";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_from_user_fkey" FOREIGN KEY (from_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE relationship DROP CONSTRAINT "$2";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_to_user_fkey" FOREIGN KEY (to_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE usr_setting DROP CONSTRAINT "$1";
+ALTER TABLE usr_setting ADD CONSTRAINT "usr_setting_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE tmp_password DROP CONSTRAINT "$1";
+ALTER TABLE tmp_password ADD CONSTRAINT "tmp_password_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE caldav_data DROP CONSTRAINT "$1";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE caldav_data DROP CONSTRAINT "$2";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_logged_user_fkey" FOREIGN KEY (logged_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE property DROP CONSTRAINT "$1";
+ALTER TABLE property ADD CONSTRAINT "property_changed_by_fkey" FOREIGN KEY (changed_by) REFERENCES usr(user_no) ON UPDATE CASCADE;
+
+ALTER TABLE calendar_item DROP CONSTRAINT "$1";
+ALTER TABLE calendar_item ADD CONSTRAINT "calendar_item_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE calendar_item DROP CONSTRAINT "$2";
+ALTER TABLE calendar_item ADD CONSTRAINT "calendar_item_tz_id_fkey" FOREIGN KEY (tz_id) REFERENCES time_zone(tz_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+SELECT new_db_revision(1,2,3, 'Mars' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.4.sql
===================================================================
--- contrib/davical/dba/patches/1.1.4.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.4.sql	(revision 3733)
@@ -0,0 +1,33 @@
+
+-- Starting to add internationalisation support
+
+BEGIN;
+SELECT check_db_revision(1,1,3);
+
+CREATE TABLE supported_locales (
+  locale TEXT PRIMARY KEY,
+  locale_name_en TEXT,
+  locale_name_locale TEXT
+);
+GRANT SELECT ON
+    supported_locales
+  TO general;
+
+
+ALTER TABLE usr ADD COLUMN locale TEXT;
+
+-- I should be able to find people to translate into these base locales
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'en', 'English', 'English' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'de', 'German',  'Deutsch' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'es', 'Spanish', 'EspaÃ±ol' );
+INSERT INTO supported_locales ( locale, locale_name_en, locale_name_locale )
+    VALUES( 'fr', 'French',  'FranÃ§ais' );
+
+SELECT new_db_revision(1,1,4, 'April' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.1b.sql
===================================================================
--- contrib/davical/dba/patches/1.2.1b.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.1b.sql	(revision 3733)
@@ -0,0 +1,88 @@
+
+-- This database update provides new tables for the Principal, for
+-- a consistent dav_resource which a principal, collection or calendar_item
+-- all inherit from.
+
+BEGIN;
+SELECT check_db_revision(1,1,12);
+
+-- Rename the caldav_data_dav_id_seq to dav_id_seq because we will use it
+-- for more tables than just caldav_data
+CREATE SEQUENCE dav_id_seq;
+SELECT setval('dav_id_seq', nextval('caldav_data_dav_id_seq'));
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE calendar_item ALTER COLUMN dav_id SET DEFAULT nextval('dav_id_seq');
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
+  DECLARE
+  BEGIN
+
+    IF TG_OP = ''DELETE'' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval(''dav_id_seq'');
+    END IF;
+
+    IF TG_OP = ''UPDATE'' THEN
+      IF OLD.dav_id = NEW.dav_id THEN
+        -- Nothing to do
+        RETURN NEW;
+      END IF;
+    END IF;
+
+    IF TG_RELNAME = ''caldav_data'' THEN
+      UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    ELSE
+      UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    END IF;
+
+    RETURN NEW;
+
+  END
+' LANGUAGE 'plpgsql';
+
+-- CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+-- CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
+--     FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+
+-- Add a numeric collection_id to collection
+ALTER TABLE collection ADD COLUMN collection_id INT8;
+UPDATE collection SET collection_id = nextval('dav_id_seq');
+ALTER TABLE collection ALTER COLUMN collection_id SET DEFAULT nextval('dav_id_seq');
+ALTER TABLE collection DROP CONSTRAINT collection_pkey CASCADE;
+ALTER TABLE collection ADD UNIQUE (user_no,dav_name);
+ALTER TABLE collection ADD CONSTRAINT collection_pkey PRIMARY KEY (collection_id);
+
+ALTER TABLE calendar_item ADD COLUMN collection_id INT8;
+INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified)
+  SELECT user_no, '/'||username||'/', '/'||username||'/home/', md5(user_no::text||'/'||username||'/home/'),
+         fullname, TRUE, current_timestamp, current_timestamp
+    FROM usr
+   WHERE NOT EXISTS (SELECT 1 FROM collection WHERE dav_name ~ ('^/'||username||'/'));
+
+UPDATE calendar_item SET collection_id = collection.collection_id
+          FROM collection WHERE calendar_item.dav_name LIKE (collection.dav_name || '%');
+ALTER TABLE calendar_item ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE calendar_item ADD CONSTRAINT
+       calendar_item_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX calendar_item_collection_id_fkey ON calendar_item(collection_id);
+
+ALTER TABLE caldav_data ADD COLUMN collection_id INT8;
+UPDATE caldav_data SET collection_id = collection.collection_id
+          FROM collection WHERE caldav_data.dav_name LIKE (collection.dav_name || '%');
+ALTER TABLE caldav_data ALTER COLUMN collection_id SET NOT NULL;
+ALTER TABLE caldav_data ADD CONSTRAINT
+       caldav_data_collection_id_fkey FOREIGN KEY (collection_id) REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+CREATE INDEX caldav_data_collection_id_fkey ON caldav_data(collection_id);
+
+SELECT new_db_revision(1,2,1, 'Janvier' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.4.sql
===================================================================
--- contrib/davical/dba/patches/1.2.4.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.4.sql	(revision 3733)
@@ -0,0 +1,16 @@
+
+-- This database update provides new tables for the Principal, for
+-- a consistent dav_resource which a principal, collection or calendar_item
+-- all inherit from.
+
+BEGIN;
+SELECT check_db_revision(1,2,3);
+
+-- Add a column to hold the 'COMPLETED' property from the caldav_data
+ALTER TABLE calendar_item ADD COLUMN completed TIMESTAMP WITH TIME ZONE;
+
+SELECT new_db_revision(1,2,4, 'Avril' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.3a.sql
===================================================================
--- contrib/davical/dba/patches/1.2.3a.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.3a.sql	(revision 3733)
@@ -0,0 +1,43 @@
+
+-- Add some more cascading updates and deletes.
+-- For databases created on or after PostgreSQL 8.0.
+
+BEGIN;
+SELECT check_db_revision(1,2,2);
+
+ALTER TABLE role_member DROP CONSTRAINT "role_member_role_no_fkey";
+ALTER TABLE role_member ADD CONSTRAINT "role_member_role_no_fkey" FOREIGN KEY (role_no) REFERENCES roles(role_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE role_member DROP CONSTRAINT "role_member_user_no_fkey";
+ALTER TABLE role_member ADD CONSTRAINT "role_member_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ 
+ALTER TABLE session DROP CONSTRAINT "session_user_no_fkey";
+ALTER TABLE session ADD CONSTRAINT "session_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE relationship DROP CONSTRAINT "relationship_from_user_fkey";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_from_user_fkey" FOREIGN KEY (from_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE relationship DROP CONSTRAINT "relationship_to_user_fkey";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_to_user_fkey" FOREIGN KEY (to_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE usr_setting DROP CONSTRAINT "usr_setting_user_no_fkey";
+ALTER TABLE usr_setting ADD CONSTRAINT "usr_setting_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE tmp_password DROP CONSTRAINT "tmp_password_user_no_fkey";
+ALTER TABLE tmp_password ADD CONSTRAINT "tmp_password_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE caldav_data DROP CONSTRAINT "caldav_data_user_no_fkey";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE caldav_data DROP CONSTRAINT "caldav_data_logged_user_fkey";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_logged_user_fkey" FOREIGN KEY (logged_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE property DROP CONSTRAINT "property_changed_by_fkey";
+ALTER TABLE property ADD CONSTRAINT "property_changed_by_fkey" FOREIGN KEY (changed_by) REFERENCES usr(user_no) ON UPDATE CASCADE;
+
+ALTER TABLE calendar_item DROP CONSTRAINT "calendar_item_user_no_fkey";
+ALTER TABLE calendar_item ADD CONSTRAINT "calendar_item_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE calendar_item DROP CONSTRAINT "calendar_item_tz_id_fkey";
+ALTER TABLE calendar_item ADD CONSTRAINT "calendar_item_tz_id_fkey" FOREIGN KEY (tz_id) REFERENCES time_zone(tz_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+SELECT new_db_revision(1,2,3, 'Mars' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.5.sql
===================================================================
--- contrib/davical/dba/patches/1.1.5.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.5.sql	(revision 3733)
@@ -0,0 +1,13 @@
+
+-- Starting to add internationalisation support
+
+BEGIN;
+SELECT check_db_revision(1,1,4);
+
+UPDATE relationship_type SET rt_name = 'is Assistant to' WHERE rt_id = 2 AND rt_name = 'Is Assisted by';
+
+SELECT new_db_revision(1,1,5, 'May' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.5.sql
===================================================================
--- contrib/davical/dba/patches/1.2.5.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.5.sql	(revision 3733)
@@ -0,0 +1,33 @@
+
+-- This database update refines the constraint on usr in order to try and be
+-- able to actually DELETE FROM usr WHERE user_no = x; and have the database
+-- do the right thing...
+
+BEGIN;
+SELECT check_db_revision(1,2,4);
+
+ALTER TABLE calendar_item DROP CONSTRAINT "calendar_item_user_no_fkey";
+ALTER TABLE calendar_item ADD CONSTRAINT "calendar_item_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no)
+    ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE caldav_data DROP CONSTRAINT "caldav_data_logged_user_fkey";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_logged_user_fkey" FOREIGN KEY (logged_user) REFERENCES usr(user_no)
+    ON UPDATE CASCADE ON DELETE SET DEFAULT DEFERRABLE;
+
+ALTER TABLE relationship DROP CONSTRAINT "relationship_from_user_fkey";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_from_user_fkey" FOREIGN KEY (from_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE relationship DROP CONSTRAINT "relationship_to_user_fkey";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_to_user_fkey" FOREIGN KEY (to_user) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ALTER TABLE relationship DROP CONSTRAINT "relationship_rt_id_fkey";
+ALTER TABLE relationship ADD CONSTRAINT "relationship_rt_id_fkey" FOREIGN KEY (rt_id) REFERENCES relationship_type(rt_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE property DROP CONSTRAINT "property_changed_by_fkey";
+ALTER TABLE property ADD CONSTRAINT "property_changed_by_fkey" FOREIGN KEY (changed_by) REFERENCES usr(user_no)
+    ON UPDATE CASCADE ON DELETE SET DEFAULT DEFERRABLE;
+
+
+SELECT new_db_revision(1,2,5, 'Mai' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.6.sql
===================================================================
--- contrib/davical/dba/patches/1.1.6.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.6.sql	(revision 3733)
@@ -0,0 +1,44 @@
+
+-- Adding lock support
+
+BEGIN;
+SELECT check_db_revision(1,1,5);
+
+CREATE TABLE locks (
+  dav_name TEXT,
+  opaquelocktoken TEXT UNIQUE NOT NULL,
+  type TEXT,
+  scope TEXT,
+  depth INT,
+  owner TEXT,
+  timeout INTERVAL,
+  start TIMESTAMP DEFAULT current_timestamp
+);
+
+CREATE INDEX locks_dav_name_idx ON locks(dav_name);
+GRANT SELECT,INSERT,UPDATE,DELETE ON locks TO general;
+
+CREATE TABLE property (
+  dav_name TEXT,
+  property_name TEXT,
+  property_value TEXT,
+  changed_on TIMESTAMP DEFAULT current_timestamp,
+  changed_by INT REFERENCES usr ( user_no ),
+  PRIMARY KEY ( dav_name, property_name )
+);
+
+CREATE INDEX properties_dav_name_idx ON property(dav_name);
+GRANT SELECT,INSERT,UPDATE,DELETE ON property TO general;
+
+UPDATE relationship_type SET confers = 'A' WHERE rt_id = 1;
+UPDATE relationship_type SET confers = 'RW' WHERE rt_id = 2;
+UPDATE relationship_type SET confers = 'R' WHERE rt_id = 3;
+UPDATE relationship_type SET confers = 'A' WHERE rt_id = 4;
+
+INSERT INTO relationship_type ( rt_id, rt_name, rt_isgroup, confers, prefix_match )
+    VALUES( 5, 'Can see free/busy time of', FALSE, 'F', '' );
+
+SELECT new_db_revision(1,1,6, 'June' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.6.sql
===================================================================
--- contrib/davical/dba/patches/1.2.6.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.6.sql	(revision 3733)
@@ -0,0 +1,573 @@
+
+-- This database update converts the permissions into a bitmap stored
+-- as an integer to make calculation of merged permissions simpler
+-- through simple binary 'AND'
+
+CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_priv ALIAS FOR $1;
+  out_bits BIT(24);
+BEGIN
+  out_bits := 0::BIT(24);
+  IF in_priv ~* 'A' THEN
+    out_bits = ~ out_bits;
+    RETURN out_bits;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'R' THEN
+    out_bits := out_bits | 4609::BIT(24);
+  END IF;
+
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind
+  --  128 DAV:unbind
+  IF in_priv ~* 'W' THEN
+    out_bits := out_bits |   198::BIT(24);
+  END IF;
+
+  --   64 DAV:bind
+  IF in_priv ~* 'B' THEN
+    out_bits := out_bits | 64::BIT(24);
+  END IF;
+
+  --  128 DAV:unbind
+  IF in_priv ~* 'U' THEN
+    out_bits := out_bits | 128::BIT(24);
+  END IF;
+
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'F' THEN
+    out_bits := out_bits | 4608::BIT(24);
+  END IF;
+
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+--
+-- NOTE: Round-trip through this and then back through legacy_privilege_to_bits
+--       function is lossy!  Through legacy_privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_legacy_privilege( BIT(24) ) RETURNS TEXT AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT;
+BEGIN
+  out_priv := '';
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv = 'A';
+    RETURN out_priv;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF (in_bits & 4609::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := 'R';
+    ELSE
+      out_priv := 'F';
+    END IF;
+  END IF;
+
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind
+  --  128 DAV:unbind
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 6::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || 'W';
+    ELSE
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'B';
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'U';
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN out_priv;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+CREATE or REPLACE FUNCTION get_permissions( INT, INT ) RETURNS TEXT AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_to   ALIAS FOR $2;
+  out_confers TEXT;
+  bit_confers BIT(24);
+  group_role_no INT;
+  tmp_txt TEXT;
+  dbg TEXT DEFAULT '';
+  r RECORD;
+  counter INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN 'A';
+  END IF;
+
+  -- dbg := 'S-';
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1
+                    WHERE r1.from_user = in_from AND r1.to_user = in_to AND NOT usr_is_role(r1.to_user,'Group');
+  IF FOUND THEN
+    RETURN dbg || out_confers;
+  END IF;
+  -- RAISE NOTICE 'No simple relationships between % and %', in_from, in_to;
+
+  SELECT bit_or(r1.confers & r2.confers) INTO bit_confers
+              FROM relationship r1
+              JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM roles LEFT JOIN role_member USING(role_no) WHERE role_name='Group');
+  IF bit_confers != 0::BIT(24) THEN
+    RETURN dbg || bits_to_legacy_privilege(bit_confers);
+  END IF;
+
+  RETURN '';
+  -- RAISE NOTICE 'No complex relationships between % and %', in_from, in_to;
+
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1 LEFT OUTER JOIN relationship r2 ON(r1.to_user = r2.to_user)
+       WHERE r1.from_user = in_from AND r2.from_user = in_to AND r1.from_user != r2.from_user
+         AND NOT EXISTS( SELECT 1 FROM relationship r3 WHERE r3.from_user = r1.to_user ) ;
+
+  IF FOUND THEN
+    -- dbg := 'H-';
+    -- RAISE NOTICE 'Permissions to shared group % ', out_confers;
+    RETURN dbg || out_confers;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN '';
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION get_group_role_no() RETURNS INT AS $$
+  SELECT role_no FROM roles WHERE role_name = 'Group'
+$$ LANGUAGE 'SQL' IMMUTABLE;
+
+CREATE or REPLACE FUNCTION has_legacy_privilege( INT, TEXT, INT ) RETURNS BOOLEAN AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_legacy_privilege ALIAS FOR $2;
+  in_to   ALIAS FOR $3;
+  in_confers BIT(24);
+  group_role_no INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN TRUE;
+  END IF;
+
+  SELECT get_group_role_no() INTO group_role_no;
+  SELECT legacy_privilege_to_bits(in_legacy_privilege) INTO in_confers;
+
+  IF EXISTS(SELECT 1 FROM relationship WHERE from_user = in_from AND to_user = in_to
+                      AND (in_confers & confers) = in_confers
+                      AND NOT EXISTS(SELECT 1 FROM role_member WHERE to_user = user_no AND role_no = group_role_no) ) THEN
+    -- A direct relationship from A to B that grants sufficient
+    -- RAISE NOTICE 'Permissions directly granted';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers
+           AND r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no) ) THEN
+    -- An indirect relationship from A to B via group G that grants sufficient
+    -- RAISE NOTICE 'Permissions mediated via group';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.to_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers
+           AND r1.from_user=in_from AND r2.from_user=in_to
+           AND r2.to_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no)
+           AND NOT EXISTS(SELECT 1 FROM relationship WHERE from_user=r2.to_user) ) THEN
+    -- An indirect reflexive relationship from both A & B to group G which grants sufficient
+    -- RAISE NOTICE 'Permissions to shared group';
+    RETURN TRUE;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN FALSE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Given a verbose DAV: or CalDAV: privilege name return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_priv ALIAS FOR $1;
+  in_priv TEXT;
+BEGIN
+  in_priv := trim(lower(regexp_replace(raw_priv, '^.*:', '')));
+  IF in_priv = 'all' THEN
+    RETURN ~ 0::BIT(24);
+  END IF;
+
+  RETURN (CASE
+            WHEN in_priv = 'read'                            THEN     1
+            WHEN in_priv = 'write'                           THEN   198 -- 2 + 4 + 64 + 128
+            WHEN in_priv = 'write-properties'                THEN     2
+            WHEN in_priv = 'write-content'                   THEN     4
+            WHEN in_priv = 'unlock'                          THEN     8
+            WHEN in_priv = 'read-acl'                        THEN    16
+            WHEN in_priv = 'read-current-user-privilege-set' THEN    32
+            WHEN in_priv = 'bind'                            THEN    64
+            WHEN in_priv = 'unbind'                          THEN   128
+            WHEN in_priv = 'write-acl'                       THEN   256
+            WHEN in_priv = 'read-free-busy'                  THEN   512
+            WHEN in_priv = 'schedule-deliver'                THEN  7168 -- 1024 + 2048 + 4096
+            WHEN in_priv = 'schedule-deliver-invite'         THEN  1024
+            WHEN in_priv = 'schedule-deliver-reply'          THEN  2048
+            WHEN in_priv = 'schedule-query-freebusy'         THEN  4096
+            WHEN in_priv = 'schedule-send'                   THEN 57344 -- 8192 + 16384 + 32768
+            WHEN in_priv = 'schedule-send-invite'            THEN  8192
+            WHEN in_priv = 'schedule-send-reply'             THEN 16384
+            WHEN in_priv = 'schedule-send-freebusy'          THEN 32768
+          ELSE 0 END)::BIT(24);
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- Given an array of verbose DAV: or CalDAV: privilege names return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT[] ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_privs ALIAS FOR $1;
+  in_priv TEXT;
+  out_bits BIT(24);
+  i INT;
+  all BIT(24);
+  start INT;
+  finish INT;
+BEGIN
+  out_bits := 0::BIT(24);
+  all := ~ out_bits;
+  SELECT array_lower(raw_privs,1) INTO start;
+  SELECT array_upper(raw_privs,1) INTO finish;
+  FOR i IN start .. finish  LOOP
+    SELECT out_bits | privilege_to_bits(raw_privs[i]) INTO out_bits;
+    IF out_bits = all THEN
+      RETURN all;
+    END IF;
+  END LOOP;
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+--
+-- NOTE: Round-trip through this and then back through privilege_to_bits
+--       function is lossy!  Through privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_privilege( BIT(24) ) RETURNS TEXT[] AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT[];
+BEGIN
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv := out_priv || ARRAY['DAV:all'];
+  END IF;
+
+  IF (in_bits & 513::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:read'];
+    END IF;
+    IF (in_bits & 512::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:read-free-busy'];
+    END IF;
+  END IF;
+
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 198::BIT(24)) = 198::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:write'];
+    ELSE
+      IF (in_bits & 2::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-properties'];
+      END IF;
+      IF (in_bits & 4::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-content'];
+      END IF;
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:bind'];
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:unbind'];
+      END IF;
+    END IF;
+  END IF;
+
+  IF (in_bits & 8::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:unlock'];
+  END IF;
+
+  IF (in_bits & 16::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-acl'];
+  END IF;
+
+  IF (in_bits & 32::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-current-user-privilege-set'];
+  END IF;
+
+  IF (in_bits & 256::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:write-acl'];
+  END IF;
+
+  IF (in_bits & 7168::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 7168::BIT(24)) = 7168::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-deliver'];
+    ELSE
+      IF (in_bits & 1024::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-invite'];
+      END IF;
+      IF (in_bits & 2048::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-reply'];
+      END IF;
+      IF (in_bits & 4096::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-query-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+
+  IF (in_bits & 57344::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 57344::BIT(24)) = 57344::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-send'];
+    ELSE
+      IF (in_bits & 8192::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-invite'];
+      END IF;
+      IF (in_bits & 16384::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-reply'];
+      END IF;
+      IF (in_bits & 32768::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN out_priv;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+
+
+
+
+
+
+
+
+
+BEGIN;
+SELECT check_db_revision(1,2,5);
+
+
+-- DAV Privileges implementation
+--
+-- RFC 3744 - DAV ACLs
+--      1 DAV:read
+--   DAV:write (aggregate = 198 = write-properties & write-content & bind & unbind)
+--      2 DAV:write-properties
+--      4 DAV:write-content
+--      8 DAV:unlock
+--     16 DAV:read-acl
+--     32 DAV:read-current-user-privilege-set
+--     64 DAV:bind
+--    128 DAV:unbind
+--    256 DAV:write-acl
+
+-- RFC 4791 - CalDAV
+--    512 CalDAV:read-free-busy
+
+-- RFC ???? - Scheduling Extensions for CalDAV
+-- CALDAV:schedule-deliver (aggregate) => 7168
+--   1024 CALDAV:schedule-deliver-invite
+--   2048 CALDAV:schedule-deliver-reply
+--   4096 CALDAV:schedule-query-freebusy
+-- CALDAV:schedule-send (aggregate) => 57344
+--   8192 CALDAV:schedule-send-invite
+--  16384 CALDAV:schedule-send-reply
+--  32768 CALDAV:schedule-send-freebusy
+
+-- RFC 3744 - DAV ACLs
+-- DAV:all => all of the above and any new ones someone might invent!
+
+-- DAV:read-acl MUST NOT contain DAV:read, DAV:write, DAV:write-acl, DAV:write-properties, DAV:write-content, or DAV:read-current-user-privilege-set.
+-- DAV:write-acl MUST NOT contain DAV:write, DAV:read, DAV:read-acl, DAV:read-current-user-privilege-set.
+-- DAV:read-current-user-privilege-set MUST NOT contain DAV:write, DAV:read, DAV:read-acl, or DAV:write-acl.
+-- DAV:write MUST NOT contain DAV:read, DAV:read-acl, or DAV:read-current-user-privilege-set.
+-- DAV:read MUST NOT contain DAV:write, DAV:write-acl, DAV:write-properties, or DAV:write-content.
+-- DAV:write-acl COULD contain DAV:write-properties DAV:write-content DAV:unlock DAV:bind DAV:unbind BUT why would it?
+
+-- DAV:write => DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+
+-- RFC 4791 - CalDAV
+-- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+
+-- RFC ???? - Scheduling Extensions for CalDAV
+--  DAV:all MUST contain CALDAV:schedule-send and CALDAV:schedule-deliver
+--  CALDAV:schedule-send MUST contain CALDAV:schedule-send-invite, CALDAV:schedule-send-reply, and CALDAV:schedule-send-freebusy;
+--  CALDAV:schedule-deliver MUST contain CALDAV:schedule-deliver-invite, CALDAV:schedule-deliver-reply, and CALDAV:schedule-query-freebusy.
+
+
+-- Me!!!
+-- CalDAV:read-free-busy privilege SHOULD contain CALDAV:schedule-query-freebusy
+-- => DAV:read privilege SHOULD contain CALDAV:schedule-query-freebusy
+-- We do this outside of these privileges though.
+
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_priv ALIAS FOR $1;
+  out_bits BIT(24);
+BEGIN
+  out_bits := 0::BIT(24);
+  IF in_priv ~* 'A' THEN
+    out_bits = ~ out_bits;
+    RETURN out_bits;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'R' THEN
+    out_bits := out_bits | 4609::BIT(24);
+  END IF;
+
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind
+  --  128 DAV:unbind
+  IF in_priv ~* 'W' THEN
+    out_bits := out_bits |   198::BIT(24);
+  END IF;
+
+  --   64 DAV:bind
+  IF in_priv ~* 'B' THEN
+    out_bits := out_bits | 64::BIT(24);
+  END IF;
+
+  --  128 DAV:unbind
+  IF in_priv ~* 'U' THEN
+    out_bits := out_bits | 128::BIT(24);
+  END IF;
+
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'F' THEN
+    out_bits := out_bits | 4608::BIT(24);
+  END IF;
+
+  RETURN out_bits;
+END
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+ALTER TABLE relationship_type ADD COLUMN bit_confers BIT(24) DEFAULT privilege_to_bits(ARRAY['DAV::read','DAV::write']);
+UPDATE relationship_type SET bit_confers = legacy_privilege_to_bits(confers);
+
+ALTER TABLE relationship ADD COLUMN confers BIT(24) DEFAULT privilege_to_bits('caldav:read-free-busy');
+UPDATE relationship SET confers = (SELECT bit_confers FROM relationship_type AS rt WHERE rt.rt_id=relationship.rt_id);
+
+ALTER TABLE collection ADD COLUMN default_privileges BIT(24);
+
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 1, 'Person' );
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 2, 'Resource' );
+INSERT INTO principal_type (principal_type_id, principal_type_desc) VALUES( 3, 'Group' );
+
+-- web needs SELECT,INSERT,UPDATE,DELETE
+DROP TABLE principal CASCADE;
+CREATE TABLE principal (
+  principal_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
+  type_id INT8 NOT NULL REFERENCES principal_type(principal_type_id) ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE,
+  user_no INT8 NULL REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  displayname TEXT,
+  default_privileges BIT(24)
+);
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 1, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+                 WHERE NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no)
+                   AND NOT EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no) ;
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 2, user_no, fullname, privilege_to_bits(ARRAY['read','schedule-send','schedule-deliver']) FROM usr
+                 WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Resource' AND role_member.user_no = usr.user_no);
+
+INSERT INTO principal (type_id, user_no, displayname, default_privileges)
+         SELECT 3, user_no, fullname, privilege_to_bits(ARRAY['read-free-busy','schedule-send','schedule-deliver']) FROM usr
+                 WHERE EXISTS(SELECT 1 FROM role_member JOIN roles USING(role_no) WHERE role_name = 'Group' AND role_member.user_no = usr.user_no);
+
+UPDATE collection SET default_privileges = CASE
+                        WHEN publicly_readable THEN privilege_to_bits(ARRAY['read'])
+                        ELSE NULL
+                  END;
+
+INSERT INTO group_member ( group_id, member_id)
+              SELECT g.principal_id, m.principal_id
+                FROM relationship JOIN principal g ON(to_user=g.user_no AND g.type_id = 3)    -- Group
+                                  JOIN principal m ON(from_user=m.user_no AND m.type_id IN (1, 2) ); -- Person or Resource
+
+DROP TABLE dav_resource_type CASCADE;
+DROP TABLE dav_resource CASCADE;
+DROP TABLE privilege CASCADE;
+
+CREATE TABLE grants (
+  by_principal INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  by_collection INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  to_principal INT8 REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE,
+  privileges BIT(24),
+  is_group BOOLEAN
+) WITHOUT OIDS;
+CREATE UNIQUE INDEX grants_pk1 ON grants(by_principal,to_principal);
+CREATE UNIQUE INDEX grants_pk2 ON grants(by_collection,to_principal);
+
+
+INSERT INTO grants ( by_principal, to_principal, privileges, is_group )
+   SELECT pby.principal_id AS by_principal, pto.principal_id AS to_principal,
+                                  confers AS privileges, pto.type_id > 2 AS is_group
+     FROM relationship r JOIN usr f ON(f.user_no=r.from_user)
+                         JOIN usr t ON(t.user_no=r.to_user)
+                         JOIN principal pby ON(t.user_no=pby.user_no)
+                         JOIN principal pto ON(pto.user_no=f.user_no)
+     WHERE rt_id < 4 AND pby.type_id < 3;
+
+-- It's always safe to kill these collections, so they will be recreated with the correct resourcetype
+DELETE FROM collection WHERE dav_name ~ E'/\.(in|out)/$';
+
+SELECT new_db_revision(1,2,6, 'Juin' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.7.sql
===================================================================
--- contrib/davical/dba/patches/1.1.7.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.7.sql	(revision 3733)
@@ -0,0 +1,34 @@
+
+-- Adding lock support
+
+BEGIN;
+SELECT check_db_revision(1,1,6);
+
+ALTER TABLE relationship_type DROP COLUMN rt_inverse;
+ALTER TABLE relationship_type DROP COLUMN prefix_match;
+ALTER TABLE relationship_type DROP COLUMN rt_isgroup;
+
+UPDATE relationship_type SET rt_name ='Administers', confers = 'A' WHERE rt_id = 1;
+UPDATE relationship_type SET rt_name ='is Assistant to', confers = 'RW' WHERE rt_id = 2;
+UPDATE relationship_type SET rt_name ='Can read from', confers = 'R' WHERE rt_id = 3;
+UPDATE relationship_type SET rt_name ='Can see free/busy time of', confers = 'F' WHERE rt_id = 4;
+
+UPDATE relationship SET rt_id=1 WHERE rt_id=4;
+UPDATE relationship SET rt_id=4 WHERE rt_id=5;
+
+DELETE FROM relationship_type WHERE rt_id = 5;
+
+-- Add a 'status' column to calendar_item which will contain the parsed value of the STATUS property
+ALTER TABLE calendar_item ADD COLUMN status TEXT;
+UPDATE calendar_item SET status = 'CONFIRMED';
+UPDATE calendar_item SET status = 'CANCELLED'    WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*CANCELLED');
+UPDATE calendar_item SET status = 'TENTATIVE'    WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*TENTATIVE');
+UPDATE calendar_item SET status = 'NEEDS-ACTION' WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*NEEDS-ACTION');
+UPDATE calendar_item SET status = 'IN-PROCESS'   WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*IN-PROCESS');
+UPDATE calendar_item SET status = 'DRAFT'        WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*DRAFT');
+UPDATE calendar_item SET status = 'FINAL'        WHERE calendar_item.dav_name IN (SELECT dav_name FROM caldav_data WHERE caldav_data.caldav_data ~ 'STATUS.*:.*FINAL');
+
+SELECT new_db_revision(1,1,7, 'July' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.8.sql
===================================================================
--- contrib/davical/dba/patches/1.1.8.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.8.sql	(revision 3733)
@@ -0,0 +1,18 @@
+
+-- Adding freebusy tickets
+
+BEGIN;
+SELECT check_db_revision(1,1,7);
+
+CREATE TABLE freebusy_ticket (
+  ticket_id TEXT NOT NULL PRIMARY KEY,
+  user_no integer NOT NULL REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE,
+  created timestamp with time zone DEFAULT current_timestamp NOT NULL
+);
+
+GRANT INSERT,SELECT,UPDATE,DELETE ON TABLE freebusy_ticket TO general;
+
+SELECT new_db_revision(1,1,8, 'August' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.7.sql
===================================================================
--- contrib/davical/dba/patches/1.2.7.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.7.sql	(revision 3733)
@@ -0,0 +1,79 @@
+
+-- This database update adds support for the draft webdav-sync specification
+-- as well as some initial support for addressbook collections which will
+-- be needed to support carddav.
+
+BEGIN;
+SELECT check_db_revision(1,2,6);
+
+CREATE TABLE sync_tokens (
+  sync_token SERIAL PRIMARY KEY,
+  collection_id INT8 REFERENCES collection(collection_id) ON DELETE CASCADE ON UPDATE CASCADE,
+  modification_time TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp
+);
+
+CREATE TABLE sync_changes (
+  sync_time TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp,
+  collection_id INT8 REFERENCES collection(collection_id) ON DELETE CASCADE ON UPDATE CASCADE,
+  sync_status INT,
+  dav_id INT8, -- can't REFERENCES calendar_item(dav_id) ON DELETE SET NULL ON UPDATE RESTRICT
+  dav_name TEXT
+);
+CREATE INDEX sync_processing_index ON sync_changes( collection_id, dav_id, sync_time );
+
+ALTER TABLE collection ADD COLUMN is_addressbook BOOLEAN DEFAULT FALSE;
+ALTER TABLE collection ADD COLUMN resourcetypes TEXT DEFAULT '<DAV::collection/>';
+ALTER TABLE collection ADD COLUMN in_freebusy_set BOOLEAN DEFAULT TRUE;
+ALTER TABLE collection ADD COLUMN schedule_transp TEXT DEFAULT 'opaque';
+ALTER TABLE collection ADD COLUMN timezone TEXT REFERENCES time_zone(tz_id) ON DELETE SET NULL ON UPDATE CASCADE;
+ALTER TABLE collection ADD COLUMN description TEXT DEFAULT '';
+
+UPDATE collection SET resourcetypes = '<DAV::collection/><urn:ietf:params:xml:ns:caldav:calendar/>' WHERE is_calendar;
+
+SELECT new_db_revision(1,2,7, 'Juillet' );
+
+COMMIT;
+ROLLBACK;
+
+
+CREATE or REPLACE FUNCTION write_sync_change( INT8, INT, TEXT ) RETURNS BOOLEAN AS $$
+DECLARE
+  in_collection_id ALIAS FOR $1;
+  in_status ALIAS FOR $2;
+  in_dav_name ALIAS FOR $3;
+  tmp_int INT8;
+BEGIN
+  SELECT 1 INTO tmp_int FROM sync_tokens
+           WHERE collection_id = in_collection_id
+           LIMIT 1;
+  IF NOT FOUND THEN
+    RETURN FALSE;
+  END IF;
+  SELECT dav_id INTO tmp_int FROM calendar_item WHERE dav_name = in_dav_name;
+  INSERT INTO sync_changes ( collection_id, sync_status, dav_id, dav_name)
+                     VALUES( in_collection_id, in_status, tmp_int, in_dav_name);
+  RETURN TRUE;
+END
+$$ LANGUAGE 'PlPgSQL' VOLATILE STRICT;
+
+
+CREATE or REPLACE FUNCTION new_sync_token( INT8, INT8 ) RETURNS INT8 AS $$
+DECLARE
+  in_old_sync_token ALIAS FOR $1;
+  in_collection_id ALIAS FOR $2;
+  tmp_int INT8;
+BEGIN
+  IF in_old_sync_token > 0 THEN
+    SELECT 1 INTO tmp_int FROM sync_changes
+            WHERE collection_id = in_collection_id
+              AND sync_time > (SELECT modification_time FROM sync_tokens WHERE sync_token = in_old_sync_token)
+            LIMIT 1;
+    IF NOT FOUND THEN
+      RETURN in_old_sync_token;
+    END IF;
+  END IF;
+  SELECT nextval('sync_tokens_sync_token_seq') INTO tmp_int;
+  INSERT INTO sync_tokens(collection_id, sync_token) VALUES( in_collection_id, tmp_int );
+  RETURN tmp_int;
+END
+$$ LANGUAGE 'PlPgSQL' STRICT;
Index: contrib/davical/dba/patches/1.1.9.sql
===================================================================
--- contrib/davical/dba/patches/1.1.9.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.9.sql	(revision 3733)
@@ -0,0 +1,13 @@
+
+-- Adding a primary key to the calendar_item table
+
+BEGIN;
+SELECT check_db_revision(1,1,8);
+
+ALTER TABLE calendar_item ADD PRIMARY KEY (user_no, dav_name );
+
+SELECT new_db_revision(1,1,9, 'September' );
+COMMIT;
+ROLLBACK;
+
+VACUUM FULL ANALYZE;
Index: contrib/davical/dba/patches/1.2.8.sql
===================================================================
--- contrib/davical/dba/patches/1.2.8.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.8.sql	(revision 3733)
@@ -0,0 +1,95 @@
+
+-- This database update adds support for tickets to be handed out to grant
+-- specific access to a collection or individual resource, as read-only or
+-- read-write.  A table is also added to manage WebDAV binding, in line
+-- with http://tools.ietf.org/html/draft-ietf-webdav-bind.
+
+BEGIN;
+SELECT check_db_revision(1,2,7);
+
+ALTER TABLE caldav_data ADD COLUMN weak_etag TEXT DEFAULT NULL;
+ALTER TABLE collection DROP COLUMN in_freebusy_set;
+
+CREATE TABLE access_ticket (
+  ticket_id TEXT PRIMARY KEY,
+  dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  privileges BIT(24),
+  target_collection_id INT8 NOT NULL REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  target_resource_id INT8 REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  expires TIMESTAMP
+);
+
+
+-- At this point we only support binding collections
+CREATE TABLE dav_binding (
+  bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
+  bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
+  dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  parent_container TEXT NOT NULL,
+  dav_name TEXT UNIQUE NOT NULL,
+  dav_displayname TEXT
+);
+
+
+CREATE TABLE addressbook_resource (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE PRIMARY KEY,
+  version TEXT,
+  uid TEXT,
+  nickname TEXT,
+  fn TEXT, -- fullname
+  n TEXT, -- Name Surname;First names
+  note TEXT,
+  org TEXT,
+  url TEXT
+);
+
+CREATE TABLE addressbook_address_adr (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  adr TEXT,
+  property TEXT -- The full text of the property
+);
+
+CREATE TABLE addressbook_address_tel (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  tel TEXT,
+  property TEXT -- The full text of the property
+);
+
+CREATE TABLE addressbook_address_email (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  email TEXT,
+  property TEXT -- The full text of the property
+);
+
+
+CREATE TABLE calendar_alarm (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  action TEXT,
+  trigger TEXT,
+  summary TEXT,
+  description TEXT,
+  next_trigger TIMESTAMP WITH TIME ZONE,
+  component TEXT -- The full text of the component
+);
+
+CREATE TABLE calendar_attendee (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  status TEXT,
+  partstat TEXT,
+  cn TEXT,
+  attendee TEXT,
+  role TEXT,
+  rsvp BOOLEAN,
+  property TEXT, -- The full text of the property
+  PRIMARY KEY ( dav_id, attendee )
+);
+
+SELECT new_db_revision(1,2,8, 'AoÃ»t' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.2.9.sql
===================================================================
--- contrib/davical/dba/patches/1.2.9.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.2.9.sql	(revision 3733)
@@ -0,0 +1,40 @@
+
+-- This database update adds support for tickets to be handed out to grant
+-- specific access to a collection or individual resource, as read-only or
+-- read-write.  A table is also added to manage WebDAV binding, in line
+-- with http://tools.ietf.org/html/draft-ietf-webdav-bind.
+
+BEGIN;
+SELECT check_db_revision(1,2,8);
+
+-- Kind of important to have these as first-class citizens
+ALTER TABLE addressbook_resource ADD COLUMN fburl TEXT DEFAULT NULL;
+ALTER TABLE addressbook_resource ADD COLUMN caluri TEXT DEFAULT NULL;
+ALTER TABLE addressbook_resource ADD COLUMN caladruri TEXT DEFAULT NULL;
+
+DROP TABLE addressbook_address_adr CASCADE;
+CREATE TABLE addressbook_address_adr (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  box_no TEXT,
+  unit_no TEXT,
+  street_address TEXT,
+  locality TEXT,
+  region TEXT,
+  postcode TEXT,
+  country TEXT,
+  property TEXT -- The full text of the property
+);
+
+-- 'N' => 'New/Needs setting', 'A' = 'Active', 'O' = 'Old'
+ALTER TABLE calendar_alarm ADD COLUMN trigger_state CHAR DEFAULT 'N';
+
+-- Internal DAViCal calendar state
+ALTER TABLE calendar_attendee ADD COLUMN attendee_state TEXT;
+ALTER TABLE calendar_attendee ADD COLUMN weak_etag TEXT;
+
+SELECT new_db_revision(1,2,9, 'Septembre' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.10.sql
===================================================================
--- contrib/davical/dba/patches/1.1.10.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.10.sql	(revision 3733)
@@ -0,0 +1,20 @@
+
+-- Sort out accessing calendar entries.
+
+BEGIN;
+SELECT check_db_revision(1,1,9);
+
+-- Make sure that class is set to something, by default PUBLIC.
+-- According to RFC2445, 4.8.1.3.
+UPDATE calendar_item SET class = 'PUBLIC' WHERE class IS NULL;
+
+-- Allow forcing all events in a calendar to be public
+ALTER TABLE collection ADD COLUMN public_events_only BOOLEAN;
+UPDATE collection SET public_events_only = FALSE;
+ALTER TABLE collection ALTER public_events_only SET NOT NULL;
+ALTER TABLE collection ALTER public_events_only SET DEFAULT FALSE;
+
+SELECT new_db_revision(1,1,10, 'October' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.11.sql
===================================================================
--- contrib/davical/dba/patches/1.1.11.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.11.sql	(revision 3733)
@@ -0,0 +1,16 @@
+
+-- Sort out accessing calendar entries.
+
+BEGIN;
+SELECT check_db_revision(1,1,10);
+
+ALTER TABLE caldav_data DROP CONSTRAINT "$1";
+ALTER TABLE caldav_data ADD CONSTRAINT "$1" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+ 
+ALTER TABLE collection DROP CONSTRAINT "$1";
+ALTER TABLE collection ADD CONSTRAINT "$1" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+SELECT new_db_revision(1,1,11, 'November' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.12.sql
===================================================================
--- contrib/davical/dba/patches/1.1.12.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.12.sql	(revision 3733)
@@ -0,0 +1,70 @@
+
+-- Add a numeric foreign key link between caldav_data and calendar_item to
+-- provide more efficient linking when the db has been initialised with a
+-- non POSIX collation.
+
+
+BEGIN;
+SELECT check_db_revision(1,1,11);
+
+-- Add a column to the collection table to allow us to mark collections
+-- as publicly readable
+ALTER TABLE collection ADD COLUMN publicly_readable BOOLEAN DEFAULT FALSE;
+
+-- Add a numeric dav_id to link the caldav_data and calendar_item tables
+ALTER TABLE caldav_data ADD COLUMN dav_id INT8;
+ALTER TABLE calendar_item ADD COLUMN dav_id INT8;
+CREATE SEQUENCE caldav_data_dav_id_seq;
+GRANT SELECT,UPDATE ON caldav_data_dav_id_seq TO general;
+
+CREATE or REPLACE FUNCTION sync_dav_id ( ) RETURNS TRIGGER AS '
+  DECLARE
+  BEGIN
+
+    IF TG_OP = ''DELETE'' THEN
+      -- Just let the ON DELETE CASCADE handle this case
+      RETURN OLD;
+    END IF;
+
+    IF NEW.dav_id IS NULL THEN
+      NEW.dav_id = nextval(''caldav_data_dav_id_seq'');
+    END IF;
+
+    IF TG_OP = ''UPDATE'' THEN
+      IF OLD.dav_id = NEW.dav_id THEN
+        -- Nothing to do
+        RETURN NEW;
+      END IF;
+    END IF;
+
+    IF TG_RELNAME = ''caldav_data'' THEN
+      UPDATE calendar_item SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    ELSE
+      UPDATE caldav_data SET dav_id = NEW.dav_id WHERE user_no = NEW.user_no AND dav_name = NEW.dav_name;
+    END IF;
+
+    RETURN NEW;
+
+  END
+' LANGUAGE 'plpgsql';
+
+CREATE TRIGGER caldav_data_sync_dav_id AFTER INSERT OR UPDATE ON caldav_data
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+CREATE TRIGGER calendar_item_sync_dav_id AFTER INSERT OR UPDATE ON calendar_item
+    FOR EACH ROW EXECUTE PROCEDURE sync_dav_id();
+
+-- Now, using the trigger, magically assign dav_id to all rows in caldav_data and calendar_item
+UPDATE caldav_data SET dav_id = dav_id;
+
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET DEFAULT nextval('caldav_data_dav_id_seq');
+ALTER TABLE caldav_data ALTER COLUMN dav_id SET NOT NULL;
+ALTER TABLE caldav_data ADD CONSTRAINT caldav_data_dav_id_key UNIQUE (dav_id);
+
+ALTER TABLE calendar_item ADD CONSTRAINT calendar_item_dav_id_key UNIQUE (dav_id);
+
+SELECT new_db_revision(1,1,12, 'December' );
+
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/patches/1.1.11a.sql
===================================================================
--- contrib/davical/dba/patches/1.1.11a.sql	(revision 3733)
+++ contrib/davical/dba/patches/1.1.11a.sql	(revision 3733)
@@ -0,0 +1,17 @@
+
+-- Sort out accessing calendar entries.
+-- This alternative patch file is the same in/out revision as 1.1.11 but it works with newer databases (8.x)
+
+BEGIN;
+SELECT check_db_revision(1,1,10);
+
+ALTER TABLE caldav_data DROP CONSTRAINT "caldav_data_user_no_fkey";
+ALTER TABLE caldav_data ADD CONSTRAINT "caldav_data_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+ALTER TABLE collection DROP CONSTRAINT "collection_user_no_fkey";
+ALTER TABLE collection ADD CONSTRAINT "collection_user_no_fkey" FOREIGN KEY (user_no) REFERENCES usr(user_no) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE;
+
+SELECT new_db_revision(1,1,11, 'November' );
+COMMIT;
+ROLLBACK;
+
Index: contrib/davical/dba/appuser_permissions.txt
===================================================================
--- contrib/davical/dba/appuser_permissions.txt	(revision 3733)
+++ contrib/davical/dba/appuser_permissions.txt	(revision 3733)
@@ -0,0 +1,65 @@
+#
+# This file is used by update-rscds-database to set the correct
+# permissions for the application user.  In newer installations
+# the application user will probably be called app_davical (and
+# the administrative user will be called dba_davical) but in
+# older installations the application user was called 'general'
+# and the administrative user was probably 'postgres'.
+#
+# See the wiki topic 'Database/Users' for more discussion.
+#
+# This file includes lines like:
+#   GRANT SELECT,...
+# which define what gets granted to the following lines like:
+#   ON table1, table2, sequence1, function 3, view4, ...
+# no user-serviceable parts inside, all whitespace is ignored,
+# your mileage should not vary :-)
+#
+
+GRANT SELECT,INSERT,UPDATE,DELETE
+  ON collection
+  ON caldav_data
+  ON calendar_item
+  ON relationship
+  ON locks
+  ON property
+  ON freebusy_ticket
+  ON usr
+  ON usr_setting
+  ON roles
+  ON role_member
+  ON session
+  ON tmp_password
+  ON group_member
+  ON principal
+  ON relationship_type
+  ON sync_tokens
+  ON sync_changes
+  ON grants
+  ON dav_principal
+  ON access_ticket
+  ON dav_binding
+  ON calendar_alarm
+  ON calendar_attendee
+  ON addressbook_resource
+  ON addressbook_address_adr
+  ON addressbook_address_tel
+  ON addressbook_address_email
+
+GRANT SELECT,UPDATE
+  ON relationship_type_rt_id_seq
+  ON dav_id_seq
+  ON usr_user_no_seq
+  ON roles_role_no_seq
+  ON session_session_id_seq
+  ON principal_type_principal_type_id_seq
+  ON sync_tokens_sync_token_seq
+
+GRANT SELECT,INSERT
+  ON time_zone
+
+GRANT SELECT
+  ON supported_locales
+  ON awl_db_revision
+  ON principal_type
+
Index: contrib/davical/dba/better_perms.sql
===================================================================
--- contrib/davical/dba/better_perms.sql	(revision 3733)
+++ contrib/davical/dba/better_perms.sql	(revision 3733)
@@ -0,0 +1,371 @@
+CREATE or REPLACE FUNCTION legacy_privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  in_priv ALIAS FOR $1;
+  out_bits BIT(24);
+BEGIN
+  out_bits := 0::BIT(24);
+  IF in_priv ~* 'A' THEN
+    out_bits = ~ out_bits;
+    RETURN out_bits;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'R' THEN
+    out_bits := out_bits | 4609::BIT(24);
+  END IF;
+  
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind 
+  --  128 DAV:unbind 
+  IF in_priv ~* 'W' THEN
+    out_bits := out_bits |   198::BIT(24);
+  END IF;
+  
+  --   64 DAV:bind 
+  IF in_priv ~* 'B' THEN
+    out_bits := out_bits | 64::BIT(24);
+  END IF;
+  
+  --  128 DAV:unbind 
+  IF in_priv ~* 'U' THEN
+    out_bits := out_bits | 128::BIT(24);
+  END IF;
+
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF in_priv ~* 'F' THEN
+    out_bits := out_bits | 4608::BIT(24);
+  END IF;
+  
+  RETURN out_bits;
+END 
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+--
+-- NOTE: Round-trip through this and then back through legacy_privilege_to_bits
+--       function is lossy!  Through legacy_privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_legacy_privilege( BIT(24) ) RETURNS TEXT AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT;
+BEGIN
+  out_priv := '';
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv = 'A';
+    RETURN out_priv;
+  END IF;
+
+  -- The CALDAV:read-free-busy privilege MUST be aggregated in the DAV:read privilege.
+  --    1 DAV:read
+  --  512 CalDAV:read-free-busy
+  -- 4096 CALDAV:schedule-query-freebusy
+  IF (in_bits & 4609::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := 'R';
+    ELSE
+      out_priv := 'F';
+    END IF;
+  END IF;
+  
+  -- DAV:write => DAV:write MUST contain DAV:bind, DAV:unbind, DAV:write-properties and DAV:write-content
+  --    2 DAV:write-properties
+  --    4 DAV:write-content
+  --   64 DAV:bind 
+  --  128 DAV:unbind 
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 6::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || 'W';
+    ELSE
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'B';
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || 'U';
+      END IF;
+    END IF;
+  END IF;
+  
+  RETURN out_priv;
+END 
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+CREATE or REPLACE FUNCTION get_permissions( INT, INT ) RETURNS TEXT AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_to   ALIAS FOR $2;
+  out_confers TEXT;
+  bit_confers BIT(24);
+  group_role_no INT;
+  tmp_txt TEXT;
+  dbg TEXT DEFAULT '';
+  r RECORD;
+  counter INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN 'A';
+  END IF;
+
+  -- dbg := 'S-';
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1
+                    WHERE r1.from_user = in_from AND r1.to_user = in_to AND NOT usr_is_role(r1.to_user,'Group');
+  IF FOUND THEN
+    RETURN dbg || out_confers;
+  END IF;
+  -- RAISE NOTICE 'No simple relationships between % and %', in_from, in_to;
+
+  SELECT bit_or(r1.confers & r2.confers) INTO bit_confers
+              FROM relationship r1
+              JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM roles LEFT JOIN role_member USING(role_no) WHERE role_name='Group');
+  IF bit_confers != 0::BIT(24) THEN
+    RETURN dbg || bits_to_legacy_privilege(bit_confers);
+  END IF;
+
+  RETURN '';
+  -- RAISE NOTICE 'No complex relationships between % and %', in_from, in_to;
+
+  SELECT bits_to_legacy_privilege(r1.confers) INTO out_confers FROM relationship r1 LEFT OUTER JOIN relationship r2 ON(r1.to_user = r2.to_user) 
+       WHERE r1.from_user = in_from AND r2.from_user = in_to AND r1.from_user != r2.from_user
+         AND NOT EXISTS( SELECT 1 FROM relationship r3 WHERE r3.from_user = r1.to_user ) ;
+
+  IF FOUND THEN
+    -- dbg := 'H-';
+    -- RAISE NOTICE 'Permissions to shared group % ', out_confers;
+    RETURN dbg || out_confers;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN '';
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+CREATE or REPLACE FUNCTION get_group_role_no() RETURNS INT AS $$
+  SELECT role_no FROM roles WHERE role_name = 'Group'
+$$ LANGUAGE 'SQL' IMMUTABLE; 
+
+CREATE or REPLACE FUNCTION has_legacy_privilege( INT, TEXT, INT ) RETURNS BOOLEAN AS $$
+DECLARE
+  in_from ALIAS FOR $1;
+  in_legacy_privilege ALIAS FOR $2;
+  in_to   ALIAS FOR $3;
+  in_confers BIT(24);
+  group_role_no INT;
+BEGIN
+  -- Self can always have full access
+  IF in_from = in_to THEN
+    RETURN TRUE;
+  END IF;
+  
+  SELECT get_group_role_no() INTO group_role_no;
+  SELECT legacy_privilege_to_bits(in_legacy_privilege) INTO in_confers;
+
+  IF EXISTS(SELECT 1 FROM relationship WHERE from_user = in_from AND to_user = in_to
+                      AND (in_confers & confers) = in_confers  
+                      AND NOT EXISTS(SELECT 1 FROM role_member WHERE to_user = user_no AND role_no = group_role_no) ) THEN
+    -- A direct relationship from A to B that grants sufficient
+    -- RAISE NOTICE 'Permissions directly granted';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.from_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers  
+           AND r1.from_user=in_from AND r2.to_user=in_to
+           AND r2.from_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no) ) THEN
+    -- An indirect relationship from A to B via group G that grants sufficient
+    -- RAISE NOTICE 'Permissions mediated via group';
+    RETURN TRUE;
+  END IF;
+
+  IF EXISTS( SELECT 1 FROM relationship r1 JOIN relationship r2 ON r1.to_user=r2.to_user
+         WHERE (in_confers & r1.confers & r2.confers) = in_confers  
+           AND r1.from_user=in_from AND r2.from_user=in_to
+           AND r2.to_user IN (SELECT user_no FROM role_member WHERE role_no=group_role_no)
+           AND NOT EXISTS(SELECT 1 FROM relationship WHERE from_user=r2.to_user) ) THEN
+    -- An indirect reflexive relationship from both A & B to group G which grants sufficient
+    -- RAISE NOTICE 'Permissions to shared group';
+    RETURN TRUE;
+  END IF;
+
+  -- RAISE NOTICE 'No common group relationships between % and %', in_from, in_to;
+
+  RETURN FALSE;
+END;
+$$ LANGUAGE 'plpgsql' IMMUTABLE STRICT;
+
+
+-- Given a verbose DAV: or CalDAV: privilege name return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_priv ALIAS FOR $1;
+  in_priv TEXT;
+BEGIN
+  in_priv := trim(lower(regexp_replace(raw_priv, '^.*:', '')));
+  IF in_priv = 'all' THEN
+    RETURN ~ 0::BIT(24);
+  END IF;
+
+  RETURN (CASE
+            WHEN in_priv = 'read'                            THEN  4609 -- 1 + 512 + 4096
+            WHEN in_priv = 'write'                           THEN   198 -- 2 + 4 + 64 + 128
+            WHEN in_priv = 'write-properties'                THEN     2
+            WHEN in_priv = 'write-content'                   THEN     4
+            WHEN in_priv = 'unlock'                          THEN     8
+            WHEN in_priv = 'read-acl'                        THEN    16
+            WHEN in_priv = 'read-current-user-privilege-set' THEN    32
+            WHEN in_priv = 'bind'                            THEN    64
+            WHEN in_priv = 'unbind'                          THEN   128
+            WHEN in_priv = 'write-acl'                       THEN   256
+            WHEN in_priv = 'read-free-busy'                  THEN  4608 --  512 + 4096
+            WHEN in_priv = 'schedule-deliver'                THEN  7168 -- 1024 + 2048 + 4096
+            WHEN in_priv = 'schedule-deliver-invite'         THEN  1024
+            WHEN in_priv = 'schedule-deliver-reply'          THEN  2048
+            WHEN in_priv = 'schedule-query-freebusy'         THEN  4096
+            WHEN in_priv = 'schedule-send'                   THEN 57344 -- 8192 + 16384 + 32768 
+            WHEN in_priv = 'schedule-send-invite'            THEN  8192 
+            WHEN in_priv = 'schedule-send-reply'             THEN 16384 
+            WHEN in_priv = 'schedule-send-freebusy'          THEN 32768 
+          ELSE 0 END)::BIT(24);
+END 
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- Given an array of verbose DAV: or CalDAV: privilege names return the bitmask
+CREATE or REPLACE FUNCTION privilege_to_bits( TEXT[] ) RETURNS BIT(24) AS $$
+DECLARE
+  raw_privs ALIAS FOR $1;
+  in_priv TEXT;
+  out_bits BIT(24);
+  i INT;
+  all BIT(24);
+  start INT;
+  finish INT;
+BEGIN
+  out_bits := 0::BIT(24);
+  all := ~ out_bits;
+  SELECT array_lower(raw_privs,1) INTO start;
+  SELECT array_upper(raw_privs,1) INTO finish; 
+  FOR i IN start .. finish  LOOP
+    SELECT out_bits | privilege_to_bits(raw_privs[i]) INTO out_bits;   
+    IF out_bits = all THEN
+      RETURN all;
+    END IF;
+  END LOOP;
+  RETURN out_bits;
+END 
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
+
+
+-- This legacy conversion function will eventually be removed, once all logic
+-- has been converted to use bitmaps, or to use the bits_to_priv() output.
+--
+-- NOTE: Round-trip through this and then back through privilege_to_bits
+--       function is lossy!  Through privilege_to_bits() and back through
+--       this one is not.
+--
+CREATE or REPLACE FUNCTION bits_to_privilege( BIT(24) ) RETURNS TEXT[] AS $$
+DECLARE
+  in_bits ALIAS FOR $1;
+  out_priv TEXT[];
+BEGIN
+  out_priv := ARRAY[]::text[];
+  IF in_bits = (~ 0::BIT(24)) THEN
+    out_priv := out_priv || ARRAY['DAV:all'];
+  END IF;
+
+  IF (in_bits & 513::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 1::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:read'];
+    END IF;
+    IF (in_bits & 512::BIT(24)) != 0::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:read-free-busy'];
+    END IF;
+  END IF;
+
+  IF (in_bits & 198::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 198::BIT(24)) = 198::BIT(24) THEN
+      out_priv := out_priv || ARRAY['DAV:write'];
+    ELSE
+      IF (in_bits & 2::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-properties'];
+      END IF;
+      IF (in_bits & 4::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:write-content'];
+      END IF;
+      IF (in_bits & 64::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:bind'];
+      END IF;
+      IF (in_bits & 128::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['DAV:unbind'];
+      END IF;
+    END IF;
+  END IF;
+
+  IF (in_bits & 8::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:unlock'];
+  END IF;
+
+  IF (in_bits & 16::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-acl'];
+  END IF;
+
+  IF (in_bits & 32::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:read-current-user-privilege-set'];
+  END IF;
+
+  IF (in_bits & 256::BIT(24)) != 0::BIT(24) THEN
+    out_priv := out_priv || ARRAY['DAV:write-acl'];
+  END IF;
+
+  IF (in_bits & 7168::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 7168::BIT(24)) = 7168::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-deliver'];
+    ELSE
+      IF (in_bits & 1024::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-invite'];
+      END IF;
+      IF (in_bits & 2048::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-deliver-reply'];
+      END IF;
+      IF (in_bits & 4096::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-query-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+  
+  IF (in_bits & 57344::BIT(24)) != 0::BIT(24) THEN
+    IF (in_bits & 57344::BIT(24)) = 57344::BIT(24) THEN
+      out_priv := out_priv || ARRAY['caldav:schedule-send'];
+    ELSE
+      IF (in_bits & 8192::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-invite'];
+      END IF;
+      IF (in_bits & 16384::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-reply'];
+      END IF;
+      IF (in_bits & 32768::BIT(24)) != 0::BIT(24) THEN
+        out_priv := out_priv || ARRAY['caldav:schedule-send-freebusy'];
+      END IF;
+    END IF;
+  END IF;
+
+  RETURN out_priv;
+END 
+$$
+LANGUAGE 'PlPgSQL' IMMUTABLE STRICT;
Index: contrib/davical/dba/rrule_tests.sql
===================================================================
--- contrib/davical/dba/rrule_tests.sql	(revision 3733)
+++ contrib/davical/dba/rrule_tests.sql	(revision 3733)
@@ -0,0 +1,458 @@
+-- --------------------------------------------------------------------
+-- This file is a set of tests for the RRULE sql functions taken from
+-- the examples in RFC2445 section 4.8.5.4
+-- --------------------------------------------------------------------
+-- --------------------------------------------------------------------
+--    Daily for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=DAILY;COUNT=10
+--
+--      ==> (1997 9:00 AM EDT)September 2-11
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=DAILY;COUNT=10' );
+--
+-- --------------------------------------------------------------------
+--    Daily until December 24, 1997:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
+--
+--      ==> (1997 9:00 AM EDT)September 2-30;October 1-25
+--          (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=DAILY;UNTIL=19971224T000000' );
+--
+-- --------------------------------------------------------------------
+--    Every other day - forever:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=DAILY;INTERVAL=2
+--      ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;
+--           October 2,4,6...20,22,24
+--          (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;
+--           Dec 1,3,...
+--
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=DAILY;INTERVAL=2' ) LIMIT 50;
+-- --------------------------------------------------------------------
+--    Every 10 days, 5 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
+--
+--      ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12
+--
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=DAILY;INTERVAL=10;COUNT=5' );
+-- --------------------------------------------------------------------
+--    Everyday in January, for 3 years:
+--
+--      DTSTART;TZID=US-Eastern:19980101T090000
+--      RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
+--       BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
+--      or
+--      RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1
+--
+--      ==> (1998 9:00 AM EDT)January 1-31
+--          (1999 9:00 AM EDT)January 1-31
+--          (2000 9:00 AM EDT)January 1-31
+--
+--- ? No instance on January 31, since 9:00am EDT is after 2000-01-31 09:00:00 UTC.  I think that is correct.
+--- ? SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19980101T090000', 'FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA' );
+--- ? No instance on January 31, since 9:00am EDT is after 2000-01-31 09:00:00 UTC.  I think that is correct.
+--- ? SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19980101T090000', 'FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1' );
+-- --------------------------------------------------------------------
+--    Weekly for 10 occurrences
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=WEEKLY;COUNT=10
+--
+--      ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+--          (1997 9:00 AM EST)October 28;November 4
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;COUNT=10' );
+--
+-- --------------------------------------------------------------------
+--    Weekly until December 24, 1997
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
+--
+--      ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
+--          (1997 9:00 AM EST)October 28;November 4,11,18,25;
+--                            December 2,9,16,23
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;UNTIL=19971224T000000Z' );
+--
+-- --------------------------------------------------------------------
+--    Every other week - forever:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
+--
+--      ==> (1997 9:00 AM EDT)September 2,16,30;October 14
+--          (1997 9:00 AM EST)October 28;November 11,25;December 9,23
+--          (1998 9:00 AM EST)January 6,20;February 3, 17
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;INTERVAL=2;WKST=SU' ) LIMIT 13;
+--
+-- --------------------------------------------------------------------
+--    Weekly on Tuesday and Thursday for 5 weeks:
+--
+--     DTSTART;TZID=US-Eastern:19970902T090000
+--     RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
+--     or
+--     RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
+--
+--     ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH' );
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH' );
+--
+-- --------------------------------------------------------------------
+--    Every other week on Monday, Wednesday and Friday until December 24,
+--    1997, but starting on Tuesday, September 2, 1997:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
+--       BYDAY=MO,WE,FR
+--      ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
+--      1,3,13,15,17
+--          (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
+--                            December 8,10,12,22
+--  !!! This is challenging, because it starts on DTSTART even though DTSTART fails the repeat criteria.  In fact no known provide a UI that allows data like this to be created.
+--- !!! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR' );
+--
+-- --------------------------------------------------------------------
+--    Every other week on Tuesday and Thursday, for 8 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
+--
+--      ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH' );
+--
+-- --------------------------------------------------------------------
+--    Monthly on the 1st Friday for ten occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970905T090000
+--      RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
+--
+--      ==> (1997 9:00 AM EDT)September 5;October 3
+--          (1997 9:00 AM EST)November 7;Dec 5
+--          (1998 9:00 AM EST)January 2;February 6;March 6;April 3
+--          (1998 9:00 AM EDT)May 1;June 5
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970905T090000', 'FREQ=MONTHLY;COUNT=10;BYDAY=1FR' );
+--
+-- --------------------------------------------------------------------
+--    Monthly on the 1st Friday until December 24, 1997:
+--
+--      DTSTART;TZID=US-Eastern:19970905T090000
+--      RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
+--
+--      ==> (1997 9:00 AM EDT)September 5;October 3
+--          (1997 9:00 AM EST)November 7;December 5
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970905T090000', 'FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR' );
+--
+-- --------------------------------------------------------------------
+--    Every other month on the 1st and last Sunday of the month for 10
+--    occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970907T090000
+--      RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+--
+--      ==> (1997 9:00 AM EDT)September 7,28
+--          (1997 9:00 AM EST)November 2,30
+--          (1998 9:00 AM EST)January 4,25;March 1,29
+--          (1998 9:00 AM EDT)May 3,31
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970907T090000', 'FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU' );
+--
+-- --------------------------------------------------------------------
+--    Monthly on the second to last Monday of the month for 6 months:
+--
+--      DTSTART;TZID=US-Eastern:19970922T090000
+--      RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
+--
+--      ==> (1997 9:00 AM EDT)September 22;October 20
+--          (1997 9:00 AM EST)November 17;December 22
+--          (1998 9:00 AM EST)January 19;February 16
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=MONTHLY;COUNT=6;BYDAY=-2MO' );
+--
+-- --------------------------------------------------------------------
+--    Monthly on the third to the last day of the month, forever:
+--
+--      DTSTART;TZID=US-Eastern:19970928T090000
+--      RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
+--
+--      ==> (1997 9:00 AM EDT)September 28
+--          (1997 9:00 AM EST)October 29;November 28;December 29
+--          (1998 9:00 AM EST)January 29;February 26
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=MONTHLY;BYMONTHDAY=-3' ) LIMIT 6;
+--
+-- --------------------------------------------------------------------
+--    Monthly on the 2nd and 15th of the month for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
+--
+--      ==> (1997 9:00 AM EDT)September 2,15;October 2,15
+--          (1997 9:00 AM EST)November 2,15;December 2,15
+--          (1998 9:00 AM EST)January 2,15
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15' );
+--
+-- --------------------------------------------------------------------
+--    Monthly on the first and last day of the month for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970930T090000
+--      RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
+--
+--      ==> (1997 9:00 AM EDT)September 30;October 1
+--          (1997 9:00 AM EST)October 31;November 1,30;December 1,31
+--          (1998 9:00 AM EST)January 1,31;February 1
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970930T090000', 'FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1' );
+--
+-- --------------------------------------------------------------------
+--    Every 18 months on the 10th thru 15th of the month for 10
+--    occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970910T090000
+--      RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
+--       15
+--
+--      ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
+--          (1999 9:00 AM EST)March 10,11,12,13
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970910T090000', 'FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15' );
+--
+-- --------------------------------------------------------------------
+--    Every Tuesday, every other month:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
+--
+--      ==> (1997 9:00 AM EDT)September 2,9,16,23,30
+--          (1997 9:00 AM EST)November 4,11,18,25
+--          (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=MONTHLY;INTERVAL=2;BYDAY=TU' ) LIMIT 18;
+--
+-- --------------------------------------------------------------------
+--    Yearly in June and July for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970610T090000
+--      RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
+--      ==> (1997 9:00 AM EDT)June 10;July 10
+--          (1998 9:00 AM EDT)June 10;July 10
+--          (1999 9:00 AM EDT)June 10;July 10
+--          (2000 9:00 AM EDT)June 10;July 10
+--          (2001 9:00 AM EDT)June 10;July 10
+--      Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
+--      are specified, the day is gotten from DTSTART
+--- !!! We miss the first instance in June.  Busted.
+--- !!! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970610T090000', 'FREQ=YEARLY;COUNT=10;BYMONTH=6,7' );
+--
+-- --------------------------------------------------------------------
+--    Every other year on January, February, and March for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970310T090000
+--      RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
+--
+--      ==> (1997 9:00 AM EST)March 10
+--          (1999 9:00 AM EST)January 10;February 10;March 10
+--          (2001 9:00 AM EST)January 10;February 10;March 10
+--          (2003 9:00 AM EST)January 10;February 10;March 10
+--- !!! We miss the first instance in March.  Busted.
+--- !!! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970310T090000', 'FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3' );
+--
+-- --------------------------------------------------------------------
+--    Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970101T090000
+--      RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200
+--
+--      ==> (1997 9:00 AM EST)January 1
+--          (1997 9:00 AM EDT)April 10;July 19
+--          (2000 9:00 AM EST)January 1
+--          (2000 9:00 AM EDT)April 9;July 18
+--          (2003 9:00 AM EST)January 1
+--          (2003 9:00 AM EDT)April 10;July 19
+--          (2006 9:00 AM EST)January 1
+--- ! We don't support BYYEARDAY yet.
+--- ! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970101T090000', 'FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200' );
+--
+-- --------------------------------------------------------------------
+--    Every 20th Monday of the year, forever:
+--      DTSTART;TZID=US-Eastern:19970519T090000
+--      RRULE:FREQ=YEARLY;BYDAY=20MO
+--
+--      ==> (1997 9:00 AM EDT)May 19
+--          (1998 9:00 AM EDT)May 18
+--          (1999 9:00 AM EDT)May 17
+--      ...
+--- ! We don't support BYDAY for yearly events.  We don't support YEARLY at all well, TBH
+--- ! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970519T090000', 'FREQ=YEARLY;BYDAY=20MO' ) LIMIT 3;
+--
+-- --------------------------------------------------------------------
+--    Monday of week number 20 (where the default start of the week is
+--    Monday), forever:
+--
+--      DTSTART;TZID=US-Eastern:19970512T090000
+--      RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO
+--
+--      ==> (1997 9:00 AM EDT)May 12
+--          (1998 9:00 AM EDT)May 11
+--          (1999 9:00 AM EDT)May 17
+--      ...
+--- ! We don't support BYWEEKNO at all yet.
+--- ! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970512T090000', 'FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO' ) LIMIT 3;
+--
+-- --------------------------------------------------------------------
+--    Every Thursday in March, forever:
+--
+--      DTSTART;TZID=US-Eastern:19970313T090000
+--      RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
+--
+--      ==> (1997 9:00 AM EST)March 13,20,27
+--          (1998 9:00 AM EST)March 5,12,19,26
+--          (1999 9:00 AM EST)March 4,11,18,25
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970313T090000', 'FREQ=YEARLY;BYMONTH=3;BYDAY=TH' ) LIMIT 11;
+--
+-- --------------------------------------------------------------------
+--    Every Thursday, but only during June, July, and August, forever:
+--
+--      DTSTART;TZID=US-Eastern:19970605T090000
+--      RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
+--
+--      ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;
+--                        August 7,14,21,28
+--          (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;
+--                        August 6,13,20,27
+--          (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;
+--                        August 5,12,19,26
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970605T090000', 'FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8' ) LIMIT 39;
+--
+-- --------------------------------------------------------------------
+--    Every Friday the 13th, forever:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      EXDATE;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
+--
+--      ==> (1998 9:00 AM EST)February 13;March 13;November 13
+--          (1999 9:00 AM EDT)August 13
+--          (2000 9:00 AM EDT)October 13
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13' ) LIMIT 35;
+--
+-- --------------------------------------------------------------------
+--    The first Saturday that follows the first Sunday of the month,
+--     forever:
+--
+--      DTSTART;TZID=US-Eastern:19970913T090000
+--      RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
+--
+--      ==> (1997 9:00 AM EDT)September 13;October 11
+--          (1997 9:00 AM EST)November 8;December 13
+--          (1998 9:00 AM EST)January 10;February 7;March 7
+--          (1998 9:00 AM EDT)April 11;May 9;June 13...
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970913T090000', 'FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13' ) LIMIT 10;
+--
+-- --------------------------------------------------------------------
+--    Every four years, the first Tuesday after a Monday in November,
+--    forever (U.S. Presidential Election day):
+--
+--      DTSTART;TZID=US-Eastern:19961105T090000
+--      RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,
+--       5,6,7,8
+--
+--      ==> (1996 9:00 AM EST)November 5
+--          (2000 9:00 AM EST)November 7
+--          (2004 9:00 AM EST)November 2
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19961105T090000', 'FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8' ) LIMIT 6;
+--
+-- --------------------------------------------------------------------
+--    The 3rd instance into the month of one of Tuesday, Wednesday or
+--    Thursday, for the next 3 months:
+--
+--      DTSTART;TZID=US-Eastern:19970904T090000
+--      RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3
+--
+--      ==> (1997 9:00 AM EDT)September 4;October 7
+--          (1997 9:00 AM EST)November 6
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970904T090000', 'FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3' );
+--
+-- --------------------------------------------------------------------
+--    The 2nd to last weekday of the month:
+--
+--      DTSTART;TZID=US-Eastern:19970929T090000
+--      RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
+--
+--      ==> (1997 9:00 AM EDT)September 29
+--          (1997 9:00 AM EST)October 30;November 27;December 30
+--          (1998 9:00 AM EST)January 29;February 26;March 30
+--      ...
+--- SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970929T090000', 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2' ) LIMIT 8;
+--
+-- --------------------------------------------------------------------
+--    Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z
+--
+--      ==> (September 2, 1997 EDT)09:00,12:00,15:00
+--- !! FREQ=HOURLY Not implemented
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z' );
+--
+-- --------------------------------------------------------------------
+--    Every 15 minutes for 6 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6
+--
+--      ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
+--- !! FREQ=MINUTELY Not implemented
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('', '' );
+--
+-- --------------------------------------------------------------------
+--    Every hour and a half for 4 occurrences:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4
+--
+--      ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30
+--- !! FREQ=HOURLY Not implemented
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('', '' );
+--
+-- --------------------------------------------------------------------
+--    Every 20 minutes from 9:00 AM to 4:40 PM every day:
+--
+--      DTSTART;TZID=US-Eastern:19970902T090000
+--      RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40
+--      or
+--      RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16
+--
+--      ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+--                                 ... 16:00,16:20,16:40
+--          (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
+--                                ...16:00,16:20,16:40
+--      ...
+--- !! FREQ=MINUTELY  and/or BYHOUR Not implemented
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970902T090000', 'FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40' ) LIMIT 500;
+--
+-- --------------------------------------------------------------------
+--    An example where the days generated makes a difference because of
+--    WKST:
+--
+--      DTSTART;TZID=US-Eastern:19970805T090000
+--      RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
+--
+--      ==> (1997 EDT)Aug 5,10,19,24
+--
+--      changing only WKST from MO to SU, yields different results...
+--
+--      DTSTART;TZID=US-Eastern:19970805T090000
+--      RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
+--      ==> (1997 EDT)August 5,17,19,31
+--
+--- !! WKST Not implemented.  The second one works fine because WKST == SU in all our code.
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970805T090000', 'FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO' );
+--- !! SET timezone TO 'US/Eastern'; SELECT * FROM event_instances('19970805T090000', 'FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU' );
+--
Index: contrib/davical/dba/windows/schema-management.sql
===================================================================
--- contrib/davical/dba/windows/schema-management.sql	(revision 3733)
+++ contrib/davical/dba/windows/schema-management.sql	(revision 3733)
@@ -0,0 +1,59 @@
+-- SQL file for AWL tables
+
+-- Table for holding the schema version so we can be more structured in future
+CREATE TABLE awl_db_revision (
+   schema_id INT4,
+   schema_major INT4,
+   schema_minor INT4,
+   schema_patch INT4,
+   schema_name TEXT,
+   applied_on TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp
+);
+GRANT SELECT ON awl_db_revision TO general;
+
+CREATE or REPLACE FUNCTION check_db_revision( INT, INT, INT ) RETURNS BOOLEAN AS '
+   DECLARE
+      major ALIAS FOR $1;
+      minor ALIAS FOR $2;
+      patch ALIAS FOR $3;
+      matching INT;
+   BEGIN
+      SELECT COUNT(*) INTO matching FROM awl_db_revision
+                      WHERE schema_major = major AND schema_minor = minor AND schema_patch = patch;
+      IF matching != 1 THEN
+        RAISE EXCEPTION ''Database has not been upgraded to %.%.%'', major, minor, patch;
+        RETURN FALSE;
+      END IF;
+      SELECT COUNT(*) INTO matching FROM awl_db_revision
+             WHERE (schema_major = major AND schema_minor = minor AND schema_patch > patch)
+                OR (schema_major = major AND schema_minor > minor)
+                OR (schema_major > major)
+             ;
+      IF matching >= 1 THEN
+        RAISE EXCEPTION ''Database revisions after %.%.% have already been applied.'', major, minor, patch;
+        RETURN FALSE;
+      END IF;
+      RETURN TRUE;
+   END;
+' LANGUAGE 'plpgsql';
+
+-- The schema_id should always be incremented.  The major / minor / patch level should
+-- be incremented as seems appropriate...
+CREATE or REPLACE FUNCTION new_db_revision( INT, INT, INT, TEXT ) RETURNS BOOLEAN AS '
+   DECLARE
+      major ALIAS FOR $1;
+      minor ALIAS FOR $2;
+      patch ALIAS FOR $3;
+      blurb ALIAS FOR $4;
+      new_id INT;
+   BEGIN
+      SELECT MAX(schema_id) + 1 INTO new_id FROM awl_db_revision;
+      IF NOT FOUND OR new_id IS NULL THEN
+        new_id := 1;
+      END IF;
+      INSERT INTO awl_db_revision (schema_id, schema_major, schema_minor, schema_patch, schema_name)
+                    VALUES( new_id, major, minor, patch, blurb );
+      RETURN TRUE;
+   END;
+' LANGUAGE 'plpgsql';
+SELECT new_db_revision(1,1,0, 'Dawn' );
Index: contrib/davical/dba/windows/awl-tables.sql
===================================================================
--- contrib/davical/dba/windows/awl-tables.sql	(revision 3733)
+++ contrib/davical/dba/windows/awl-tables.sql	(revision 3733)
@@ -0,0 +1,96 @@
+-- Tables needed for AWL Libraries
+
+BEGIN;
+
+CREATE TABLE supported_locales (
+  locale TEXT PRIMARY KEY,
+  locale_name_en TEXT,
+  locale_name_locale TEXT
+);
+
+-- This is the table of users for the system
+CREATE TABLE usr (
+  user_no SERIAL PRIMARY KEY,
+  active BOOLEAN DEFAULT TRUE,
+  email_ok TIMESTAMPTZ,
+  joined TIMESTAMPTZ DEFAULT current_timestamp,
+  updated TIMESTAMPTZ,
+  last_used TIMESTAMPTZ,
+  username TEXT NOT NULL,  -- Note UNIQUE INDEX below constains case-insensitive uniqueness
+  password TEXT,
+  fullname TEXT,
+  email TEXT,
+  config_data TEXT,
+  date_format_type TEXT DEFAULT 'E', -- default to english date format dd/mm/yyyy
+  locale TEXT
+);
+CREATE FUNCTION max_usr() RETURNS INT4 AS 'SELECT max(user_no) FROM usr' LANGUAGE 'sql';
+CREATE UNIQUE INDEX usr_sk1_unique_username ON usr ( lower(username) );
+
+CREATE TABLE usr_setting (
+  user_no INT4 REFERENCES usr ( user_no ),
+  setting_name TEXT,
+  setting_value TEXT,
+  PRIMARY KEY ( user_no, setting_name )
+);
+
+CREATE FUNCTION get_usr_setting(INT4,TEXT)
+    RETURNS TEXT
+    AS 'SELECT setting_value FROM usr_setting
+            WHERE usr_setting.user_no = $1
+            AND usr_setting.setting_name = $2 ' LANGUAGE 'sql';
+
+CREATE TABLE roles (
+    role_no SERIAL PRIMARY KEY,
+    role_name TEXT
+);
+CREATE FUNCTION max_roles() RETURNS INT4 AS 'SELECT max(role_no) FROM roles' LANGUAGE 'sql';
+
+
+CREATE TABLE role_member (
+    role_no INT4 REFERENCES roles ( role_no ),
+    user_no INT4 REFERENCES usr ( user_no )
+);
+
+
+CREATE TABLE session (
+    session_id SERIAL PRIMARY KEY,
+    user_no INT4 REFERENCES usr ( user_no ),
+    session_start TIMESTAMPTZ DEFAULT current_timestamp,
+    session_end TIMESTAMPTZ DEFAULT current_timestamp,
+    session_key TEXT,
+    session_config TEXT
+);
+CREATE FUNCTION max_session() RETURNS INT4 AS 'SELECT max(session_id) FROM session' LANGUAGE 'sql';
+
+CREATE TABLE tmp_password (
+  user_no INT4 REFERENCES usr ( user_no ),
+  password TEXT,
+  valid_until TIMESTAMPTZ DEFAULT (current_timestamp + '1 day'::interval)
+);
+COMMIT;
+
+BEGIN;
+GRANT SELECT,INSERT,UPDATE ON
+    usr
+  , usr_setting
+  , roles
+  , role_member
+  , session
+  , tmp_password
+  TO general;
+GRANT SELECT,UPDATE ON
+    usr_user_no_seq
+  , session_session_id_seq
+  TO general;
+
+GRANT SELECT ON
+    supported_locales
+  TO general;
+
+GRANT DELETE ON
+    tmp_password
+  , role_member
+  TO general;
+
+COMMIT;
Index: contrib/davical/dba/windows/create-database.bat
===================================================================
--- contrib/davical/dba/windows/create-database.bat	(revision 3733)
+++ contrib/davical/dba/windows/create-database.bat	(revision 3733)
@@ -0,0 +1,155 @@
+@echo off
+rem Build the DAViCal database
+
+rem BAT file changes are internal only
+setlocal
+
+if db%1 EQU db (
+    echo Usage: create-database dbnameprefix [adminpassword [pguser]]
+    exit /B 1
+)
+set DBNAME=%1-davical
+set ADMINPW=%2
+
+set DBADIR=%CD%\..
+
+rem Attempt to locate the AWL directory
+set AWLDIR=%DBADIR%\..\..\awl\dba
+echo %AWLDIR%
+if EXIST %AWLDIR%\awl-tables.sql (
+  rem awldir=%AWLDIR%
+) ELSE (
+  echo awl directory not found
+  exit /B 2
+)
+
+rem Set DB user, web user, DB config directory, Windows DB config directory
+set AWL_DBAUSER=davical_dba
+set AWL_APPUSER=davical_app
+set DBA=%AWL_DBAUSER%
+
+rem Need PostgreSQL location
+if DEFINED %PGDIR% (
+    rem Use existing variable
+) ELSE (
+    if EXIST "c:\Program Files\PostgreSQL\8.3\bin\createuser" (
+        set PGDIR="c:\Program Files\PostgreSQL\8.3\bin"
+    )
+)
+echo PGDIR=%PGDIR%
+rem set PGDIR=%PGLOCALEDIR%\..\..\bin
+
+rem Get the major version for PostgreSQL
+rem set DBVERSION="`%PGDIR\psql -qAt -c "SELECT version();" template1 | cut -f2 -d' ' | cut -f1-2 -d'.'`"
+
+rem Show general info
+IF usr%3 NEQ usr ( set USERNAME=%3 )
+echo username=%USERNAME%
+
+rem Create DB user, web user
+%PGDIR%\createuser -U %USERNAME% --no-createdb --no-createrole --no-superuser %AWL_DBAUSER%
+%PGDIR%\createuser -U %USERNAME% --no-createdb --no-createrole --no-superuser %AWL_APPUSER%
+
+echo Creating DB=%DBNAME%
+%PGDIR%\createdb -E UTF8 -T template0  -U %USERNAME% %DBNAME% 
+if %ERRORLEVEL% NEQ 0 ( 
+   echo Unable to create database
+   exit /B 2
+)
+
+rem This will fail if the language already exists, but it should not
+rem because we created from template0.
+%PGDIR%\createlang -U %USERNAME% plpgsql %DBNAME%
+
+rem Test if egrep is available
+rem You can download egrep.exe for Windows e.g. from UnxUtils: http://unxutils.sourceforge.net/):
+egrep 2>NULL
+echo egrep results: %ERRORLEVEL%
+if %ERRORLEVEL% EQU 3 (
+    rem No egrep
+
+    rem Load the AWL base tables and schema management tables
+    echo load windows\awl-tables.sql [no egrep]
+    %PGDIR%\psql -q -f %AWLDIR/awl-tables.sql %DBNAME% %USERNAME% 2>&1 
+
+    echo load windows\schema-management.sql [no egrep]
+    %PGDIR%\psql -q -f %AWLDIR%/schema-management.sql %DBNAME% %USERNAME% 2>&1
+
+    rem Load the DAViCal tables
+    echo load davical [no egrep]
+    %PGDIR%\psql -q -f %DBADIR%\davical.sql %DBNAME% %USERNAME% 2>&1
+
+) ELSE (
+    rem egrep is available
+
+    rem Load the AWL base tables and schema management tables
+    echo load windows\awl-tables [egrep]
+    %PGDIR%\psql -q -f %AWLDIR%/awl-tables.sql %DBNAME% %USERNAME% 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: )"
+    echo load WINDOWS schema-management [egrep]
+    %PGDIR%\psql -q -f %AWLDIR%/schema-management.sql %DBNAME% %USERNAME% 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: |^t$)"
+
+    rem Load the DAViCal tables
+    echo load davical [egrep]
+    %PGDIR%\psql -q -f %DBADIR%/davical.sql %DBNAME% %USERNAME% 2>&1 | egrep -v "(^CREATE |^GRANT|^BEGIN|^COMMIT| NOTICE: |^t$)"
+)
+del NULL
+
+echo load caldav_functions
+%PGDIR%\psql -q -f %DBADIR%/caldav_functions.sql %DBNAME%  %USERNAME%
+
+echo TBD: Set permissions for the application DB user on the database
+rem if EXIST %DBADIR%\update-davical-database (
+rem   %DBADIR%\update-davical-database --dbname %DBNAME% --appuser %AWL_APPUSER% --nopatch --owner %AWL_DBAUSER%
+rem ) ELSE (
+rem   if EXIST %DBADIR%\..\update-davical-database (
+rem     %DBADIR%\..\update-davical-database --dbname %DBNAME% --appuser %AWL_APPUSER% --nopatch --owner %AWL_DBAUSER%
+rem   ) ELSE (
+rem     echo Could not find update-davical-database...ignoring
+rem   )
+rem )
+rem if %ERRORLEVEL% NEQ 0 (
+rem   echo * * * * ERROR * * * *
+rem   echo The database administration utility failed.  This is usually due to the Perl YAML
+rem   echo or the Perl DBD::Pg libraries not being available.
+
+rem   echo See:  http://wiki.davical.org/w/Install_Errors/No_Perl_YAML
+
+rem   exit /B 2
+rem )
+
+rem Load the required base data
+echo load base-data
+%PGDIR%\psql -q -f %DBADIR%/base-data.sql %DBNAME%  %USERNAME%
+
+rem We can override the admin password generation for regression testing predictability
+rem if [ %ADMINPW}" = "" ] ; then
+rem   #
+rem   # Generate a random administrative password.  If pwgen is available we'll use that,
+rem   # otherwise try and hack something up using a few standard utilities
+rem   ADMINPW="`pwgen -Bcny 2>/dev/null | tr \"\\\'\" '^='`"
+rem fi
+rem 
+rem if [ "$ADMINPW" = "" ] ; then
+rem   # OK.  They didn't supply one, and pwgen didn't work, so we hack something
+rem   # together from /dev/random ...
+rem   ADMINPW="`dd if=/dev/urandom bs=512 count=1 2>/dev/null | tr -c -d "[:alnum:]" | cut -c2-9`"
+rem fi
+rem 
+rem   # Right.  We're getting desperate now.  We'll have to use a default password
+rem   # and hope that they change it to something more sensible.
+IF pw%ADMINPW% EQU pw ( set ADMINPW=please change this password )
+rem fi
+
+%PGDIR%\psql -q -c "UPDATE usr SET password = '**%ADMINPW%' WHERE user_no = 1;" %DBNAME%  %USERNAME%
+
+echo The password for the 'admin' user has been set to "%ADMINPW%"
+
+rem The supported locales are in a separate file to make them easier to upgrade
+%PGDIR%\psql -q -f %DBADIR%/supported_locales.sql %DBNAME%  %USERNAME%
+
+echo DONE
+
+:END
+
+endlocal
+
Index: contrib/davical/dba/windows/rscds.sql
===================================================================
--- contrib/davical/dba/windows/rscds.sql	(revision 3733)
+++ contrib/davical/dba/windows/rscds.sql	(revision 3733)
@@ -0,0 +1,135 @@
+-- Really Simple CalDAV Store - Database Schema
+--
+
+-- Use the usr, group and schema management stufffrom libawl-php
+\i awl-tables.sql
+\i schema-management.sql
+
+-- The main event.  Where we store the things the calendar throws at us.
+CREATE TABLE caldav_data (
+  user_no INT references usr(user_no),
+  dav_name TEXT,
+  dav_etag TEXT,
+  created TIMESTAMP WITH TIME ZONE,
+  modified TIMESTAMP WITH TIME ZONE,
+  caldav_data TEXT,
+  caldav_type TEXT,
+  logged_user INT references usr(user_no),
+
+  PRIMARY KEY ( user_no, dav_name )
+);
+
+GRANT SELECT,INSERT,UPDATE,DELETE ON caldav_data TO general;
+
+-- Not particularly needed, perhaps, except as a way to collect
+-- a bunch of valid iCalendar time zone specifications... :-)
+CREATE TABLE time_zone (
+  tz_id TEXT PRIMARY KEY,
+  tz_locn TEXT,
+  tz_spec TEXT
+);
+GRANT SELECT,INSERT ON time_zone TO general;
+
+-- The parsed calendar item.  Here we have pulled those events/todos/journals apart somewhat.
+CREATE TABLE calendar_item (
+  user_no INT references usr(user_no),
+  dav_name TEXT,
+  dav_etag TEXT,
+
+  -- Extracted vEvent/vTodo data
+  uid TEXT,
+  created TIMESTAMP,
+  last_modified TIMESTAMP,
+  dtstamp TIMESTAMP,
+  dtstart TIMESTAMP WITH TIME ZONE,
+  dtend TIMESTAMP WITH TIME ZONE,
+  due TIMESTAMP WITH TIME ZONE,
+  summary TEXT,
+  location TEXT,
+  description TEXT,
+  priority INT,
+  class TEXT,
+  transp TEXT,
+  rrule TEXT,
+  url TEXT,
+  percent_complete NUMERIC(7,2),
+  tz_id TEXT REFERENCES time_zone( tz_id ),
+  status TEXT,
+
+  -- Cascade updates / deletes from the caldav_data table
+  CONSTRAINT caldav_exists FOREIGN KEY ( user_no, dav_name )
+                REFERENCES caldav_data ( user_no, dav_name )
+                MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE
+);
+
+GRANT SELECT,INSERT,UPDATE,DELETE ON calendar_item TO general;
+
+
+-- Something that can look like a filesystem hierarchy where we store stuff
+CREATE TABLE collection (
+  user_no INT references usr(user_no),
+  parent_container TEXT,
+  dav_name TEXT,
+  dav_etag TEXT,
+  dav_displayname TEXT,
+  is_calendar BOOLEAN,
+  created TIMESTAMP WITH TIME ZONE,
+  modified TIMESTAMP WITH TIME ZONE,
+
+  PRIMARY KEY ( user_no, dav_name )
+);
+
+GRANT SELECT,INSERT,UPDATE,DELETE ON collection TO general;
+
+-- Each user can be related to each other user.  This mechanism can also
+-- be used to define groups of users, since some relationships are transitive.
+CREATE TABLE relationship_type (
+  rt_id SERIAL PRIMARY KEY,
+  rt_name TEXT,
+  rt_togroup BOOLEAN,
+  confers TEXT DEFAULT 'RW',
+  rt_fromgroup BOOLEAN
+);
+
+GRANT SELECT,INSERT,UPDATE,DELETE ON relationship_type TO general;
+GRANT SELECT,UPDATE ON relationship_type_rt_id_seq TO general;
+
+CREATE TABLE relationship (
+  from_user INT REFERENCES usr (user_no) ON UPDATE CASCADE,
+  to_user INT REFERENCES usr (user_no) ON UPDATE CASCADE,
+  rt_id INT REFERENCES relationship_type (rt_id) ON UPDATE CASCADE,
+
+  PRIMARY KEY ( from_user, to_user, rt_id )
+);
+
+GRANT SELECT,INSERT,UPDATE,DELETE ON relationship TO general;
+
+
+CREATE TABLE locks (
+  dav_name TEXT,
+  opaquelocktoken TEXT UNIQUE NOT NULL,
+  type TEXT,
+  scope TEXT,
+  depth INT,
+  owner TEXT,
+  timeout INTERVAL,
+  start TIMESTAMP DEFAULT current_timestamp
+);
+
+CREATE INDEX locks_dav_name_idx ON locks(dav_name);
+GRANT SELECT,INSERT,UPDATE,DELETE ON locks TO general;
+
+CREATE TABLE property (
+  dav_name TEXT,
+  property_name TEXT,
+  property_value TEXT,
+  changed_on TIMESTAMP DEFAULT current_timestamp,
+  changed_by INT REFERENCES usr ( user_no ),
+  PRIMARY KEY ( dav_name, property_name )
+);
+
+CREATE INDEX properties_dav_name_idx ON property(dav_name);
+GRANT SELECT,INSERT,UPDATE,DELETE ON property TO general;
+
+
+SELECT new_db_revision(1,1,7, 'July' );
Index: contrib/davical/dba/views/dav_principal.sql
===================================================================
--- contrib/davical/dba/views/dav_principal.sql	(revision 3733)
+++ contrib/davical/dba/views/dav_principal.sql	(revision 3733)
@@ -0,0 +1,77 @@
+-- Define an updateable view for dav_principal which conbines the AWL usr
+-- record 1:1 with the principal table
+
+
+DROP VIEW dav_principal CASCADE;
+CREATE OR REPLACE VIEW dav_principal AS
+  SELECT user_no, usr.active AS user_active, joined AS created, updated AS modified,
+         username, password, fullname, email,
+         email_ok, date_format_type, locale,
+         principal_id, type_id, displayname, default_privileges,
+         TRUE AS is_principal,
+         FALSE AS is_calendar,
+         principal_id AS collection_id,
+         FALSE AS is_addressbook,
+          '/' || username || '/' AS dav_name,
+         '<DAV::collection/><DAV::principal/>'::text AS resourcetypes
+    FROM usr JOIN principal USING(user_no);
+
+CREATE or REPLACE RULE dav_principal_insert AS ON INSERT TO dav_principal
+DO INSTEAD
+(
+  INSERT INTO usr ( user_no, active, joined, updated, username, password, fullname, email, email_ok, date_format_type, locale )
+    VALUES(
+      COALESCE( NEW.user_no, nextval('usr_user_no_seq')),
+      COALESCE( NEW.user_active, TRUE),
+      current_timestamp,
+      current_timestamp,
+      NEW.username, NEW.password,
+      COALESCE( NEW.fullname, NEW.displayname ),
+      NEW.email, NEW.email_ok,
+      COALESCE( NEW.date_format_type, 'E'),
+      NEW.locale
+    );
+  INSERT INTO principal ( user_no, principal_id, type_id, displayname, default_privileges )
+    VALUES(
+      COALESCE( NEW.user_no, currval('usr_user_no_seq')),
+      COALESCE( NEW.principal_id, nextval('dav_id_seq')),
+      NEW.type_id,
+      COALESCE( NEW.displayname, NEW.fullname ),
+      COALESCE( NEW.default_privileges, 0::BIT(24))
+    );
+);
+
+
+CREATE or REPLACE RULE dav_principal_update AS ON UPDATE TO dav_principal
+DO INSTEAD
+(
+  UPDATE usr
+    SET
+      user_no=NEW.user_no,
+      active=NEW.user_active,
+      updated=current_timestamp,
+      username=NEW.username,
+      password=NEW.password,
+      fullname=NEW.fullname,
+      email=NEW.email,
+      email_ok=NEW.email_ok,
+      date_format_type=NEW.date_format_type,
+      locale=NEW.locale
+    WHERE user_no=OLD.user_no;
+
+  UPDATE principal
+    SET
+      principal_id = NEW.principal_id,
+      type_id = NEW.type_id,
+      displayname = NEW.displayname,
+      default_privileges = NEW.default_privileges
+    WHERE principal_id=OLD.principal_id;
+);
+
+CREATE or REPLACE RULE dav_principal_delete AS ON DELETE TO dav_principal
+DO INSTEAD
+(
+  DELETE FROM usr WHERE user_no=OLD.user_no;
+  DELETE FROM principal WHERE principal_id=OLD.principal_id;
+);
+
Index: contrib/davical/dba/create-database.sh
===================================================================
--- contrib/davical/dba/create-database.sh	(revision 3733)
+++ contrib/davical/dba/create-database.sh	(revision 3733)
@@ -0,0 +1,181 @@
+#!/bin/sh
+#
+# Build the DAViCal database
+#
+
+DBNAME="${1:-davical}"
+ADMINPW="${2}"
+
+DBADIR="`dirname \"$0\"`"
+
+INSTALL_NOTE_FN="`mktemp -t tmp.XXXXXXXXXX`"
+
+testawldir() {
+  [ -f "${1}/dba/awl-tables.sql" ]
+}
+
+#
+# Attempt to locate the AWL directory
+AWLDIR="${DBADIR}/../../awl"
+if ! testawldir "${AWLDIR}"; then
+  AWLDIR="/usr/share/awl"
+  if ! testawldir "${AWLDIR}"; then
+    AWLDIR="/usr/local/share/awl"
+    if ! testawldir "${AWLDIR}"; then
+      echo "Unable to find AWL libraries"
+      exit 1
+    fi
+  fi
+fi
+
+export AWL_DBAUSER=davical_dba
+export AWL_APPUSER=davical_app
+
+# Get the major version for PostgreSQL
+export DBVERSION="`psql -qXAt -c "SELECT version();" template1 | cut -f2 -d' ' | cut -f1-2 -d'.'`"
+
+install_note() {
+  cat >>"${INSTALL_NOTE_FN}"
+}
+
+db_users() {
+  psql -qXAt -c "SELECT usename FROM pg_user;" template1
+}
+
+create_db_user() {
+  if ! db_users | grep "^${1}$" >/dev/null ; then
+    psql -qXAt -c "CREATE USER ${1} NOCREATEDB NOCREATEROLE;" template1
+    cat <<EONOTE | install_note
+*  You will need to edit the PostgreSQL pg_hba.conf to allow the
+   '${1}' database user access to the 'davical' database.
+
+EONOTE
+  fi
+}
+
+create_plpgsql_language() {
+  if ! psql ${DBA} -qXAt -c "SELECT lanname FROM pg_language;" "${DBNAME}" | grep "^plpgsql$" >/dev/null; then
+    createlang plpgsql "${DBNAME}"
+  fi
+}
+
+try_db_user() {
+  [ "XtestX`psql -U "${1}" -qXAt -c \"SELECT usename FROM pg_user;\" \"${DBNAME}\" 2>/dev/null`" != "XtestX" ]
+}
+
+# Hide all the annoying NOTICE... messages
+export PGOPTIONS='--client-min-messages=warning'
+
+create_db_user "${AWL_DBAUSER}"
+create_db_user "${AWL_APPUSER}"
+
+# FIXME: Need to check that the database was actually created.
+if ! createdb --encoding UTF8 --template template0 --owner "${AWL_DBAUSER}" "${DBNAME}" ; then
+  echo "Unable to create database"
+  exit 1
+fi
+
+#
+# Try a few alternatives for a database user or give up...
+if try_db_user "${AWL_DBAUSER}" ; then
+  export DBA="-U ${AWL_DBAUSER}"
+else
+  if try_db_user "postgres" ; then
+    export DBA="-U postgres"
+  else
+    if try_db_user "${USER}" ; then
+      export DBA=""
+    else
+      if try_db_user "${PGUSER}" ; then
+        export DBA=""
+      else
+        cat <<EOFAILURE
+* * * * ERROR * * * *
+I cannot find a usable database user to construct the DAViCal database with, but
+may have successfully created the davical_app and davical_dba users (I tried :-).
+
+You should edit your pg_hba.conf file to give permissions to the davical_app and
+davical_dba users to access the database and run this script again.  If you still
+continue to see this message then you will need to make sure you run the script
+as a user with full permissions to access the local PostgreSQL database.
+
+If your PostgreSQL database is non-standard then you will need to set the PGHOST,
+PGPORT and/or PGCLUSTER environment variables before running this script again.
+
+See:  http://wiki.davical.org/w/Install_Errors/No_Database_Rights
+
+EOFAILURE
+        exit 1
+      fi
+    fi
+  fi
+fi
+
+create_plpgsql_language
+
+#
+# Load the AWL base tables and schema management tables
+psql -qXAt ${DBA} -f "${AWLDIR}/dba/awl-tables.sql" "${DBNAME}" 2>&1
+psql -qXAt ${DBA} -f "${AWLDIR}/dba/schema-management.sql" "${DBNAME}" 2>&1
+
+#
+# Load the DAViCal tables
+psql -qXAt ${DBA} -f "${DBADIR}/davical.sql" "${DBNAME}" 2>&1
+
+#
+# Set permissions for the application DB user on the database
+if ! ${DBADIR}/update-davical-database --dbname "${DBNAME}" --appuser "${AWL_APPUSER}" --nopatch --owner "${AWL_DBAUSER}" ; then
+        cat <<EOFAILURE
+* * * * ERROR * * * *
+The database administration utility failed.  This may be due to database
+permissions for the davical_dba user, or because the Perl DBD::Pg or YAML
+libraries are not available.
+
+Check that your pg_hba.conf allows the davical_dba user to connect to the
+database (and make sure you've reloaded PostgreSQL since changing that).
+
+Also see:  http://wiki.davical.org/w/Install_Errors/No_Perl_YAML
+
+EOFAILURE
+  exit 1
+fi
+#
+# Load the required base data
+psql -qXAt ${DBA} -f "${DBADIR}/base-data.sql" "${DBNAME}" | egrep -v '^10'
+
+#
+# We can override the admin password generation for regression testing predictability
+if [ "${ADMINPW}" = "" ] ; then
+  #
+  # Generate a random administrative password.  If pwgen is available we'll use that,
+  # otherwise try and hack something up using a few standard utilities
+  ADMINPW="`pwgen -Bcny 2>/dev/null | tr \"\\\\\'\" '^='`"
+fi
+
+if [ "$ADMINPW" = "" ] ; then
+  # OK.  They didn't supply one, and pwgen didn't work, so we hack something
+  # together from /dev/random ...
+  export LC_ALL=C
+  ADMINPW="`dd if=/dev/urandom bs=512 count=1 2>/dev/null | tr -c -d 'a-km-zA-HJ-NP-Y0-9' | cut -c2-9`"
+fi
+
+if [ "$ADMINPW" = "" ] ; then
+  # Right.  We're getting desperate now.  We'll have to use a default password
+  # and hope that they change it to something more sensible.
+  ADMINPW="please change this password"
+fi
+
+psql -qX -c "UPDATE usr SET password = '**${ADMINPW}' WHERE user_no = 1;" "${DBNAME}"
+
+echo "NOTE"
+echo "===="
+cat "${INSTALL_NOTE_FN}"
+rm "${INSTALL_NOTE_FN}"
+
+cat <<FRIENDLY
+*  The password for the 'admin' user has been set to '${ADMINPW}'"
+
+Thanks for trying DAViCal!  Check in /usr/share/doc/davical/examples/ for
+some configuration examples.  For help, visit #davical on irc.oftc.net.
+
+FRIENDLY
Index: contrib/davical/inc/username
===================================================================
--- contrib/davical/inc/username	(revision 3733)
+++ contrib/davical/inc/username	(revision 3733)
@@ -0,0 +1,9101 @@
+always.php.in:$c->script_start_time = microtime(true);
+always.php.in:// An ultra-simple exception handler to catch errors that occur
+always.php.in:  echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
+always.php.in:  $trace = array_reverse($e->getTrace());
+always.php.in:$c->sysabbr     = 'davical';
+always.php.in:$c->admin_email = 'admin@davical.example.com';
+always.php.in:$c->system_name = 'DAViCal CalDAV Server';
+always.php.in:$c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+always.php.in:$c->save_time_zone_defs = true;
+always.php.in:$c->collections_always_exist = false;
+always.php.in:$c->allow_get_email_visibility = false;
+always.php.in:$c->permission_scan_depth = 2;
+always.php.in:$c->expand_pdo_parameters = true;
+always.php.in:$c->home_calendar_name = 'home';
+always.php.in:$c->enable_row_linking = true;
+always.php.in:$c->enable_scheduling = false;
+always.php.in:$c->http_auth_mode = 'Basic';
+always.php.in:// $c->default_locale = array('es_MX', 'es_AR', 'es', 'pt');  // An array of locales to try, or just a single locale
+always.php.in:// $c->local_tzid = 'Pacific/Auckland';  // Perhaps we should read from /etc/timezone - I wonder how standard that is?
+always.php.in:$c->default_locale = 'en';
+always.php.in:$c->locale_path = '../locale';
+always.php.in:$c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
+always.php.in:$c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
+always.php.in:$c->default_privileges = array('read-free-busy', 'schedule-deliver');
+always.php.in:$c->stylesheets = array( $c->base_url.'/davical.css' );
+always.php.in:$c->images      = $c->base_url . '/images';
+always.php.in:$c->template_usr = array( 'active' => true,
+always.php.in:                          'email_ok' => date('Y-m-d')
+always.php.in:$c->hide_TODO = true;                      // VTODO only visible to collection owner
+always.php.in:$c->readonly_webdav_collections = true;    // WebDAV access is readonly
+always.php.in:$c->total_query_time = 0;
+always.php.in:$c->dbg = array();
+always.php.in:  @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration.  Using IP address meanhwhile..." );
+always.php.in:$c->protocol_server_port = sprintf( '%s://%s%s',
+always.php.in:$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
+always.php.in:if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+always.php.in:  include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+always.php.in:else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+always.php.in:  include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+always.php.in:if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
+always.php.in:else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
+always.php.in:         $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
+always.php.in:putenv("LANG=". $c->default_locale);
+always.php.in:awl_set_locale($c->default_locale);
+always.php.in:init_gettext( 'davical', $c->locale_path );
+always.php.in:$c->code_version = 0;
+always.php.in:$c->version_string = '0.9.8.3'; // The actual version # is replaced into that during the build /release process
+always.php.in:if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
+always.php.in:  $c->code_major = $matches[1];
+always.php.in:  $c->code_minor = $matches[2];
+always.php.in:  $c->code_patch = $matches[3];
+always.php.in:  $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
+always.php.in:  dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
+always.php.in:  header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
+always.php.in:$_SERVER['SERVER_NAME'] = $c->domain_name;
+always.php.in:$c->want_dbversion = array(1,2,8);
+always.php.in:$c->schema_version = 0;
+always.php.in:if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
+always.php.in:  $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
+always.php.in:  $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
+always.php.in:  $c->schema_major = $row->schema_major;
+always.php.in:  $c->schema_minor = $row->schema_minor;
+always.php.in:  $c->schema_patch = $row->schema_patch;
+always.php.in:  if ( $c->schema_version < $c->wanted_version ) {
+always.php.in:    $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
+always.php.in:             $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
+always.php.in:  if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) $qry->QDo('SET TIMEZONE TO \'Pacific/Auckland\'');
+always.php.in:  if ( isset($session->principal_id) ) {
+always.php.in:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+always.php.in:  if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
+always.php.in:    if ( isset($session->principal_id) ) {
+always.php.in:      $_known_users_name[$row->username]      = $row;
+always.php.in:      $_known_users_id[$row->user_no]         = $row;
+always.php.in:      $_known_users_pid[$row->principal_id]   = $row;
+always.php.in:* Return a user record identified by e-mail address, caching it for any subsequent lookup
+always.php.in:    /** We don't actually maintain a cache-access by e-mail, since it's rare */
+always.php.in:      if ( strtolower($email) == strtolower($v->email) ) return $v;
+always.php.in:    case 203:  $ans = 'Non-Authoritative Information';        break;
+always.php.in:    case 207:  $ans = 'Multi-Status';                         break;
+always.php.in:    case 414:  $ans = 'Request-URI Too Long';                 break;
+always.php.in:  if ( ! isset($c->_url_script_path) ) {
+always.php.in:    $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
+always.php.in:    $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
+always.php.in:  $url = $c->_url_script_path;
+always.php.in:  if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
+always.php.in:  if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
+always.php.in:    if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
+always.php.in:      case 'write-properties'                : $out_priv |=     2;  break;
+always.php.in:      case 'write-content'                   : $out_priv |=     4;  break;
+always.php.in:      case 'read-acl'                        : $out_priv |=    16;  break;
+always.php.in:      case 'read-current-user-privilege-set' : $out_priv |=    32;  break;
+always.php.in:      case 'write-acl'                       : $out_priv |=   256;  break;
+always.php.in:      case 'read-free-busy'                  : $out_priv |=   512;  break;
+always.php.in:      case 'schedule-deliver-invite'         : $out_priv |=  1024;  break;
+always.php.in:      case 'schedule-deliver-reply'          : $out_priv |=  2048;  break;
+always.php.in:      case 'schedule-query-freebusy'         : $out_priv |=  4096;  break;
+always.php.in:      case 'schedule-send-invite'            : $out_priv |=  8192;  break;
+always.php.in:      case 'schedule-send-reply'             : $out_priv |= 16384;  break;
+always.php.in:      case 'schedule-send-freebusy'          : $out_priv |= 32768;  break;
+always.php.in:      case 'schedule-deliver'                : $out_priv |=  7168;  break; // 1024 + 2048 + 4096
+always.php.in:      case 'schedule-send'                   : $out_priv |= 57344;  break; // 8192 + 16384 + 32768
+always.php.in:  if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
+always.php.in:  if ( ($raw_bits &  16) != 0 ) $out_priv[] = 'DAV::read-acl';
+always.php.in:  if ( ($raw_bits &  32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
+always.php.in:  if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
+always.php.in:  if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
+always.php.in:    if ( ($raw_bits &   2) != 0 ) $out_priv[] = 'DAV::write-properties';
+always.php.in:    if ( ($raw_bits &   4) != 0 ) $out_priv[] = 'DAV::write-content';
+always.php.in:  if ( $resourcetype == 'schedule-inbox' && ($raw_bits & 7168) != 0 ) {
+always.php.in:    if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
+always.php.in:    if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
+always.php.in:    if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
+always.php.in:    if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
+always.php.in:  if ( $resourcetype == 'schedule-outbox' && ($raw_bits & 57344) != 0 ) {
+always.php.in:    if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
+always.php.in:    if ( ($raw_bits &  8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
+always.php.in:    if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
+always.php.in:    if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
+always.php.in:      $xmldoc->NSElement($privilege,$k);
+always.php.in:      $privilege->NewElement($k);
+auth-functions.php:*   - Accept a username / password
+auth-functions.php:*   - Confirm the username / password are correct
+auth-functions.php:*   - Create (or update) a 'usr' record in our database
+auth-functions.php:*   - Return the 'usr' record as an object
+auth-functions.php:*   - Return === false when authentication fails
+auth-functions.php:*   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+auth-functions.php:*   - This file should be included
+auth-functions.php:*   - $c->authenticate_hook['call'] should be set to the name of the plugin
+auth-functions.php:*   - $c->authenticate_hook['config'] should be set up with any configuration data for the plugin
+auth-functions.php:  if ( ! isset($c->home_calendar_name) || strlen($c->home_calendar_name) == 0 ) return true;
+auth-functions.php:  //$calendar_path = $parent_path . $c->home_calendar_name."/";
+auth-functions.php:  $dav_etag = md5($usr->user_no . $calendar_path);
+auth-functions.php:      ':user_no' => $usr->user_no,
+auth-functions.php:      ':displayname' => $usr->fullname,
+auth-functions.php:  if ( $qry->Exec() ) {
+auth-functions.php:    $c->messages[] = i18n("Home calendar added.");
+auth-functions.php:    $c->messages[] = i18n("There was an error writing to the database.");
+auth-functions.php:  if ( !isset($usr->user_no) || intval($usr->user_no) == 0 ) {
+auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:    $sequence_value = $qry->Fetch(true);  // Fetch as an array
+auth-functions.php:    $usr->user_no = $sequence_value[0];
+auth-functions.php:  $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no) );
+auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+auth-functions.php:    if ( $old = $qry->Fetch() ) {
+auth-functions.php:        if ( $old->{$k} != $v ) {
+auth-functions.php:          dbg_error_log("Login","User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v );
+auth-functions.php:        dbg_error_log("Login","No changes to user record for '%s' - leaving as-is.", $usr->username );
+auth-functions.php:        if ( isset($usr->active) && $usr->active == 'f' ) return false;
+auth-functions.php:        dbg_error_log("Login","Changes to user record for '%s' - updating.", $usr->username );
+auth-functions.php:  if ( $type != 'INSERT' ) $params[':user_no'] = $usr->user_no;
+auth-functions.php:  $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:  if ( isset($usr->active) && ($usr->active === 'f' || $usr->active === false) ) return false;
+auth-functions.php:                          array( ':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username) );
+auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:    CreateHomeCalendar($usr->username);
+auth-functions.php:* require_once('auth-functions.php');
+auth-functions.php:*  $c->authenticate_hook = array(
+auth-functions.php:  $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent'];
+auth-functions.php:  if ( isset($c->authenticate_hook['config']['columns']) )
+auth-functions.php:    $cols = $c->authenticate_hook['config']['columns'];
+auth-functions.php:  if ( isset($c->authenticate_hook['config']['where']) )
+auth-functions.php:    $andwhere = ' AND '.$c->authenticate_hook['config']['where'];
+auth-functions.php:  $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], ($persistent ? array(PDO::ATTR_PERSISTENT => true) : null));
+auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+auth-functions.php:    $usr = $qry->Fetch();
+auth-functions.php:    if ( session_validate_password( $password, $usr->password ) ) {
+auth-functions.php:      if ( isset($usr->active) && $usr->active == 'f' ) return false;
+auth-functions.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username) );
+auth-functions.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+auth-functions.php:        $principal = $qry->Fetch();
+caldav-ACL.php:* CalDAV Server - handle ACL method
+caldav-ACL.php:$request->NeedPrivilege('DAV::write-acl');
+caldav-ACL.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-ACL.php:    fwrite($fh,$request->raw_post);
+caldav-ACL.php:$resource = new DAVResource( $request->path );
+caldav-ACL.php:   (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
+caldav-ACL.php:   that an implementation-specific ACL restriction has been violated.
+caldav-ACL.php:   (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
+caldav-ACL.php:   (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
+caldav-ACL.php:   implementation-dependent.  Implementations MUST either report this
+caldav-ACL.php:   (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
+caldav-ACL.php:   However, ACL-compliant servers MUST support at least one ACE granting
+caldav-ACL.php:   (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
+caldav-ACL.php:   non-inherited grant ACEs.
+caldav-ACL.php:   (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
+caldav-ACL.php:   restrictions of the resource include the DAV:grant-only constraint
+caldav-ACL.php:   (DAV:no-invert):  The ACL request MUST NOT include a DAV:invert
+caldav-ACL.php:   the resource includes the DAV:no-invert constraint (defined in
+caldav-ACL.php:   (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
+caldav-ACL.php:   (DAV:not-supported-privilege): The ACEs submitted in the ACL request
+caldav-ACL.php:   (DAV:missing-required-principal): The result of the ACL request MUST
+caldav-ACL.php:   DAV:required-principal XML element in the ACL semantics of that
+caldav-ACL.php:   (DAV:recognized-principal): Every principal URL in the ACL request
+caldav-ACL.php:   (DAV:allowed-principal): The principals specified in the ACEs
+caldav-ACL.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-ACL.php:$aces = $xmltree->GetPath("/DAV::acl/*");
+caldav-ACL.php:$grantor = new DAVResource($request->path);
+caldav-ACL.php:if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
+caldav-ACL.php:if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id');
+caldav-ACL.php:else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id');
+caldav-ACL.php:else $request->PreconditionFailed(403,'not-supported-privilege','ACLs may only be applied to Principals or Collections');
+caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:  $elements = $ace->GetContent();
+caldav-ACL.php:  if ( $principal->GetTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetTag());
+caldav-ACL.php:  $grant_tag = $grant->GetTag();
+caldav-ACL.php:  if ( $grant_tag == 'DAV::deny' )   $request->PreconditionFailed(403,'grant-only');
+caldav-ACL.php:  if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
+caldav-ACL.php:  if ( $grant->GetTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
+caldav-ACL.php:  $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
+caldav-ACL.php:    $privilege_names[] = $priv->GetTag();
+caldav-ACL.php:  $principal_content = $principal->GetContent();
+caldav-ACL.php:  if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
+caldav-ACL.php:  switch( $principal_content->GetTag() ) {
+caldav-ACL.php:      $principal_property = $principal_content->GetContent();
+caldav-ACL.php:      if ( $principal_property[0]->GetTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
+caldav-ACL.php:        $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
+caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+caldav-ACL.php:      $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) );
+caldav-ACL.php:      if ( ! $principal->Exists() || !$principal->IsPrincipal() )
+caldav-ACL.php:        $request->PreconditionFailed(403,'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
+caldav-ACL.php:      $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_id') );
+caldav-ACL.php:      if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
+caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:      if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
+caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+caldav-ACL.php:      $request->PreconditionFailed(403, 'recognized-principal' );
+caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:$request->DoResponse( 200 );
+caldav-BIND.php:* CalDAV Server - handle BIND method
+caldav-BIND.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-BIND.php:$request->NeedPrivilege('DAV::bind');
+caldav-BIND.php:if ( ! $request->IsCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-into-collection',translate('The BIND Request-URI MUST identify a collection.'));
+caldav-BIND.php:$parent_container = $request->path;
+caldav-BIND.php:if ( ! $parent->Exists() || $parent->IsSchedulingCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::method-not-allowed',translate('The BIND method is not allowed at that location.') );
+caldav-BIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-BIND.php:$segment = $xmltree->GetElements('DAV::segment');
+caldav-BIND.php:$segment = $segment[0]->GetContent();
+caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::name-allowed',translate('That destination name contains invalid characters.') );
+caldav-BIND.php:$href    = $xmltree->GetElements('DAV::href');
+caldav-BIND.php:$href    = $href[0]->GetContent();
+caldav-BIND.php:if ( $destination->Exists() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
+caldav-BIND.php:if ( !$source->Exists() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
+caldav-BIND.php:if ( $source->IsPrincipal() || !$source->IsCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
+caldav-BIND.php:    ':target_id'    => $source->GetProperty('collection_id'),
+caldav-BIND.php:    ':ticket_id'    => (isset($request->ticket) ? $request->ticket->id() : null),
+caldav-BIND.php:    ':parent_container' => $parent->dav_name(),
+caldav-BIND.php:    ':session_principal' => $session->principal_id,
+caldav-BIND.php:    ':displayname'  => $source->GetProperty('displayname')
+caldav-BIND.php:if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
+caldav-BIND.php:  $request->DoResponse(201);
+caldav-BIND.php:  $request->DoResponse(500,translate('Database Error'));
+caldav-client.php:* removed curl - now using fsockopen
+caldav-client.php:* changed 2009 by Andres Obrero - Switzerland andres@obrero.ch
+caldav-client.php:    $this->user = $user;
+caldav-client.php:    $this->pass = $pass;
+caldav-client.php:    $this->calendar = $calendar;
+caldav-client.php:    $this->headers = array();
+caldav-client.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+caldav-client.php:      $this->server = $matches[2];
+caldav-client.php:      $this->base_url = $matches[5];
+caldav-client.php:        $this->protocol = 'ssl';
+caldav-client.php:        $this->port = 443;
+caldav-client.php:        $this->protocol = 'tcp';
+caldav-client.php:        $this->port = 80;
+caldav-client.php:        $this->port = intval($matches[4]);
+caldav-client.php:  * Adds an If-Match or If-None-Match header
+caldav-client.php:    $this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+caldav-client.php:    $this->headers[] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+caldav-client.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+caldav-client.php:    $this->user_agent = $user_agent;
+caldav-client.php:  * Add a Content-type: header.
+caldav-client.php:    $this->headers[] = "Content-type: $type";
+caldav-client.php:        $this->httpResponse = trim($response);
+caldav-client.php:        $this->httpResponse = trim(substr($response, 0, $pos));
+caldav-client.php:        $this->xmlResponse = trim(substr($response, $pos));
+caldav-client.php:      return $this->httpRequest;
+caldav-client.php:      return $this->httpResponse;
+caldav-client.php:      return $this->xmlRequest;
+caldav-client.php:      return $this->xmlResponse;
+caldav-client.php:    $headers[] = $this->requestMethod." ". $this->base_url . $relative_url . " HTTP/1.1";
+caldav-client.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+caldav-client.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+caldav-client.php:    foreach( $this->headers as $ii => $head ) {
+caldav-client.php:    $headers[] = "Content-Length: " . strlen($this->body);
+caldav-client.php:    $headers[] = "User-Agent: " . $this->user_agent;
+caldav-client.php:    $this->httpRequest = join("\r\n",$headers);
+caldav-client.php:    $this->xmlRequest = $this->body;
+caldav-client.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+caldav-client.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+caldav-client.php:    $this->headers = array();  // reset the headers array for our next request
+caldav-client.php:    $this->ParseResponse($rsp);
+caldav-client.php:    $this->requestMethod = "OPTIONS";
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $headers = $this->DoRequest($relative_url);
+caldav-client.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+caldav-client.php:    $this->body = $xml;
+caldav-client.php:    $this->requestMethod = $request_method;
+caldav-client.php:    $this->SetContentType("text/xml");
+caldav-client.php:    return $this->DoRequest($relative_url);
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $this->requestMethod = "GET";
+caldav-client.php:    return $this->DoRequest( $relative_url );
+caldav-client.php:    $this->body = $icalendar;
+caldav-client.php:    $this->requestMethod = "PUT";
+caldav-client.php:      $this->SetMatch( ($etag != '*'), $etag );
+caldav-client.php:    $this->SetContentType("text/icalendar");
+caldav-client.php:    $headers = $this->DoRequest($relative_url);
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $this->requestMethod = "DELETE";
+caldav-client.php:      $this->SetMatch( true, $etag );
+caldav-client.php:    $this->DoRequest($relative_url);
+caldav-client.php:    return $this->resultcode;
+caldav-client.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+caldav-client.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+caldav-client.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client.php:    <C:calendar-data/>
+caldav-client.php:</C:calendar-query>
+caldav-client.php:    $this->DoXMLRequest( 'REPORT', $xml, $relative_url );
+caldav-client.php:    $xml_parser = xml_parser_create_ns('UTF-8');
+caldav-client.php:    $this->xml_tags = array();
+caldav-client.php:    xml_parse_into_struct( $xml_parser, $this->xmlResponse, $this->xml_tags );
+caldav-client.php:    foreach( $this->xml_tags as $k => $v ) {
+caldav-client.php:        case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
+caldav-client.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:      <C:comp-filter name="VEVENT">
+caldav-client.php:      </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:                <C:time-range start="$start" end="$finish"/>
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:          <C:comp-filter name="VTODO">
+caldav-client.php:                <C:prop-filter name="STATUS">
+caldav-client.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+caldav-client.php:                </C:prop-filter>
+caldav-client.php:                <C:prop-filter name="STATUS">
+caldav-client.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+caldav-client.php:                </C:prop-filter>$time_range
+caldav-client.php:          </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:          <C:comp-filter name="VEVENT">
+caldav-client.php:                <C:prop-filter name="UID">
+caldav-client.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+caldav-client.php:                </C:prop-filter>
+caldav-client.php:          </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:    return $this->DoGETRequest( $relative_url . $href );
+caldav-client.php:* $options = $cal->DoOptionsRequest();
+caldav-client.php:*   $cal->SetDepth(1);
+caldav-client.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+caldav-client.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+caldav-client.php:* $options = $cal->DoOptionsRequest();
+caldav-client.php:* <?xml version="1.0" encoding="utf-8" ?>
+caldav-client.php:* //  $cal->SetDepth(1);
+caldav-client.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+caldav-client.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+caldav-client.php:*     print "\n---------------------------------------------\n";
+caldav-client-v2.php:* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
+caldav-client-v2.php:    $this->url = $url;
+caldav-client-v2.php:    $this->displayname = $displayname;
+caldav-client-v2.php:    $this->getctag = $getctag;
+caldav-client-v2.php:    return( '(URL: '.$this->url.'   Ctag: '.$this->getctag.'   Displayname: '.$this->displayname .')'. "\n" );
+caldav-client-v2.php:  * The principal-URL we're using
+caldav-client-v2.php:  * The calendar-URL we're using
+caldav-client-v2.php:  * The calendar-home-set we're using
+caldav-client-v2.php:    $this->user = $user;
+caldav-client-v2.php:    $this->pass = $pass;
+caldav-client-v2.php:    $this->headers = array();
+caldav-client-v2.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+caldav-client-v2.php:      $this->server = $matches[2];
+caldav-client-v2.php:      $this->base_url = $matches[5];
+caldav-client-v2.php:        $this->protocol = 'ssl';
+caldav-client-v2.php:        $this->port = 443;
+caldav-client-v2.php:        $this->protocol = 'tcp';
+caldav-client-v2.php:        $this->port = 80;
+caldav-client-v2.php:        $this->port = intval($matches[4]);
+caldav-client-v2.php:  * Adds an If-Match or If-None-Match header
+caldav-client-v2.php:    $this->headers['match'] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+caldav-client-v2.php:    $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+caldav-client-v2.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+caldav-client-v2.php:    $this->user_agent = $user_agent;
+caldav-client-v2.php:  * Add a Content-type: header.
+caldav-client-v2.php:    $this->headers['content-type'] = "Content-type: $type";
+caldav-client-v2.php:    $this->calendar_url = $url;
+caldav-client-v2.php:      $this->httpResponse = trim($response);
+caldav-client-v2.php:      $this->httpResponse = trim(substr($response, 0, $pos));
+caldav-client-v2.php:      $this->xmlResponse = trim(substr($response, $pos));
+caldav-client-v2.php:      $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
+caldav-client-v2.php:      $parser = xml_parser_create_ns('UTF-8');
+caldav-client-v2.php:      if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
+caldav-client-v2.php:        printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
+caldav-client-v2.php://        echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes );
+caldav-client-v2.php://        echo "\nTags array............................................................\n";  print_r( $this->xmltags );
+caldav-client-v2.php:        printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
+caldav-client-v2.php:      return $this->httpRequest;
+caldav-client-v2.php:      return $this->httpResponseHeaders;
+caldav-client-v2.php:      return $this->httpResponseBody;
+caldav-client-v2.php:      return $this->xmlRequest;
+caldav-client-v2.php:      return $this->xmlResponse;
+caldav-client-v2.php:    if ( !isset($url) ) $url = $this->base_url;
+caldav-client-v2.php:    $this->request_url = $url;
+caldav-client-v2.php:    if ( preg_match( '{[^%?&=+,.-_/a-z0-9]}', $url ) ) {
+caldav-client-v2.php:    $headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
+caldav-client-v2.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+caldav-client-v2.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+caldav-client-v2.php:    if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
+caldav-client-v2.php:    foreach( $this->headers as $ii => $head ) {
+caldav-client-v2.php:    $headers[] = "Content-Length: " . strlen($this->body);
+caldav-client-v2.php:    $headers[] = "User-Agent: " . $this->user_agent;
+caldav-client-v2.php:    $this->httpRequest = join("\r\n",$headers);
+caldav-client-v2.php:    $this->xmlRequest = $this->body;
+caldav-client-v2.php:    $this->httpResponse = '';
+caldav-client-v2.php:    $this->xmlResponse = '';
+caldav-client-v2.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+caldav-client-v2.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+caldav-client-v2.php:    list( $this->httpResponseHeaders, $this->httpResponseBody ) = preg_split( '{\r?\n\r?\n}s', $response, 2 );
+caldav-client-v2.php:    if ( preg_match( '{Transfer-Encoding: chunked}i', $this->httpResponseHeaders ) ) $this->Unchunk();
+caldav-client-v2.php:    $this->headers = array();  // reset the headers array for our next request
+caldav-client-v2.php:    $this->ParseResponse($this->httpResponseBody);
+caldav-client-v2.php:    $chunks = $this->httpResponseBody;
+caldav-client-v2.php:      if ( preg_match('{^((\r\n)?\s*([ 0-9a-fA-F]+)(;[^\n]*)?\r?\n)}', $chunks, $matches ) ) {
+caldav-client-v2.php:          // printf( "---------------------------------\n%s\n---------------------------------\n", substr($chunks,$pos,$bytes) );
+caldav-client-v2.php:    $this->httpResponseBody = $content;
+caldav-client-v2.php:    $this->requestMethod = "OPTIONS";
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $headers = $this->DoRequest($url);
+caldav-client-v2.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+caldav-client-v2.php:    $this->body = $xml;
+caldav-client-v2.php:    $this->requestMethod = $request_method;
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    return $this->DoRequest($url);
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "GET";
+caldav-client-v2.php:    return $this->DoRequest( $url );
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "HEAD";
+caldav-client-v2.php:    return $this->DoRequest( $url );
+caldav-client-v2.php:    $this->body = $icalendar;
+caldav-client-v2.php:    $this->requestMethod = "PUT";
+caldav-client-v2.php:      $this->SetMatch( ($etag != '*'), $etag );
+caldav-client-v2.php:    $this->SetContentType('text/calendar; encoding="utf-8"');
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+caldav-client-v2.php:      printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
+caldav-client-v2.php:      $save_request = $this->httpRequest;
+caldav-client-v2.php:      $save_response_headers = $this->httpResponseHeaders;
+caldav-client-v2.php:      $this->DoHEADRequest( $url );
+caldav-client-v2.php:      if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+caldav-client-v2.php:        printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
+caldav-client-v2.php:      $this->httpRequest = $save_request;
+caldav-client-v2.php:      $this->httpResponseHeaders = $save_response_headers;
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "DELETE";
+caldav-client-v2.php:      $this->SetMatch( true, $etag );
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    return $this->resultcode;
+caldav-client-v2.php:    $this->SetDepth($depth);
+caldav-client-v2.php:      $xml->NSElement($prop,$v);
+caldav-client-v2.php:    $this->body = $xml->Render('propfind',$prop );
+caldav-client-v2.php:    $this->requestMethod = "PROPFIND";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    return $this->GetXmlResponse();
+caldav-client-v2.php:      $this->principal_url = $url;
+caldav-client-v2.php:    return $this->principal_url;
+caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+caldav-client-v2.php:  * @param $url array of string The calendar-home-set URLs to set
+caldav-client-v2.php:      $this->calendar_home_set = $urls;
+caldav-client-v2.php:    return $this->calendar_home_set;
+caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+caldav-client-v2.php:      $this->calendar_urls = $urls;
+caldav-client-v2.php:    return $this->calendar_urls;
+caldav-client-v2.php:    foreach( $this->xmltags[$tagname] AS $k => $v ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
+caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
+caldav-client-v2.php://        printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+caldav-client-v2.php:        if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
+caldav-client-v2.php://      printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+caldav-client-v2.php:      if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+caldav-client-v2.php://        printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']);
+caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' );
+caldav-client-v2.php:        while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
+caldav-client-v2.php:        if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+caldav-client-v2.php:          return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    $level = $this->xmlnodes[$nodenum]['level'];
+caldav-client-v2.php:    while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) {
+caldav-client-v2.php:        if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) {
+caldav-client-v2.php:      elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) {
+caldav-client-v2.php:      elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) {
+caldav-client-v2.php:        $status = $this->xmlnodes[$nodenum]['value'];
+caldav-client-v2.php:        $props[] = $this->xmlnodes[$nodenum];
+caldav-client-v2.php:  * @param string $url The URL to find the principal-URL from
+caldav-client-v2.php:    $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL',
+caldav-client-v2.php:                                  'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
+caldav-client-v2.php:    $principal_url = $this->HrefForProp('DAV::principal');
+caldav-client-v2.php:      foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) {
+caldav-client-v2.php:          $principal_url = $this->HrefValueInside($href);
+caldav-client-v2.php:    return $this->PrincipalURL($principal_url);
+caldav-client-v2.php:  * @param string $url The URL to find the calendar-home-set from
+caldav-client-v2.php:    if ( !isset($this->principal_url) ) {
+caldav-client-v2.php:      $this->FindPrincipal();
+caldav-client-v2.php:      $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0);
+caldav-client-v2.php:    foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$v]['type'] != 'open' ) continue;
+caldav-client-v2.php:      while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
+caldav-client-v2.php://        printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
+caldav-client-v2.php:        if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
+caldav-client-v2.php:          $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
+caldav-client-v2.php:      $calendar_home = $this->FindCalendarHome(true);
+caldav-client-v2.php:    return $this->CalendarHomeSet($calendar_home);
+caldav-client-v2.php:    if ( !isset($this->calendar_home_set[0]) ) {
+caldav-client-v2.php:      $this->FindCalendarHome();
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1);
+caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) {
+caldav-client-v2.php:        $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1;
+caldav-client-v2.php:      foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
+caldav-client-v2.php:        $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+caldav-client-v2.php:        $ok_props = $this->GetOKProps($hnode);
+caldav-client-v2.php:              $calendar->getctag = $v['value'];
+caldav-client-v2.php:              $calendar->displayname = $v['value'];
+caldav-client-v2.php:    return $this->CalendarUrls($calendars);
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
+caldav-client-v2.php:    $hnode = $this->xmltags['DAV::href'][0];
+caldav-client-v2.php:    $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+caldav-client-v2.php:    $ok_props = $this->GetOKProps($hnode);
+caldav-client-v2.php:        $calendar->{$name} = $v['value'];
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
+caldav-client-v2.php:    if ( isset($this->xmltags['DAV::getetag']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
+caldav-client-v2.php:        $href = $this->HrefForProp('DAV::getetag', $k);
+caldav-client-v2.php:        if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) $etags[$href] = $this->xmlnodes[$v]['value'];
+caldav-client-v2.php:  * Get a bunch of events for a calendar with a calendar-multiget report
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->body = <<<EOXML
+caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client-v2.php:<prop><getetag/><C:calendar-data/></prop>
+caldav-client-v2.php:</C:calendar-multiget>
+caldav-client-v2.php:    $this->requestMethod = "REPORT";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
+caldav-client-v2.php:        $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
+caldav-client-v2.php://        echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]);
+caldav-client-v2.php:        $events[$href] = $this->xmlnodes[$v]['value'];
+caldav-client-v2.php:        $this->DoGETRequest($href);
+caldav-client-v2.php:        $events[$href] = $this->httpResponseBody;
+caldav-client-v2.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+caldav-client-v2.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->body = <<<EOXML
+caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client-v2.php:    <C:calendar-data/>
+caldav-client-v2.php:</C:calendar-query>
+caldav-client-v2.php:    $this->requestMethod = "REPORT";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+caldav-client-v2.php:    foreach( $this->xmltags as $k => $v ) {
+caldav-client-v2.php:        case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+caldav-client-v2.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:      <C:comp-filter name="VEVENT">
+caldav-client-v2.php:      </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:                <C:time-range start="$start" end="$finish"/>
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:          <C:comp-filter name="VTODO">
+caldav-client-v2.php:                <C:prop-filter name="STATUS">
+caldav-client-v2.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>
+caldav-client-v2.php:                <C:prop-filter name="STATUS">
+caldav-client-v2.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>$time_range
+caldav-client-v2.php:          </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:          <C:comp-filter name="VEVENT">
+caldav-client-v2.php:                <C:prop-filter name="UID">
+caldav-client-v2.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>
+caldav-client-v2.php:          </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:    return $this->DoGETRequest( $href );
+caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+caldav-client-v2.php:*   $cal->SetDepth(1);
+caldav-client-v2.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+caldav-client-v2.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+caldav-client-v2.php:* <?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:* //  $cal->SetDepth(1);
+caldav-client-v2.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+caldav-client-v2.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+caldav-client-v2.php:*     print "\n---------------------------------------------\n";
+caldav-DELETE.php:* CalDAV Server - handle DELETE method
+caldav-DELETE.php:$dav_resource = new DAVResource($request->path);
+caldav-DELETE.php:$container = $dav_resource->FetchParentContainer();
+caldav-DELETE.php:$container->NeedPrivilege('DAV::unbind');
+caldav-DELETE.php:$lock_opener = $request->FailIfLocked();
+caldav-DELETE.php:$etag = md5($request->raw_post);
+caldav-DELETE.php:$nome = substr($request->username, 0, 9);
+caldav-DELETE.php:  if ( $qry->Exec("DELETE") && $qry->rows() == 1 ){                                                
+caldav-DELETE.php:    $delete_row = $qry->Fetch(); 
+caldav-DELETE.php:    if ($qry->QDo("DELETE FROM phpgw_cal WHERE cal_id = :cal_id AND owner = :nome",array( ':cal_id' => $CALID, ':nome' => $nome))){
+caldav-DELETE.php:         if($qry->QDO("DELETE FROM phpgw_cal_user WHERE cal_login = :nome AND cal_id = :cal_id",array( ':nome' => $nome , ':cal_id' => $CALID))){
+caldav-DELETE.php:           if ( $delete_row->cal_type == 'M' )
+caldav-DELETE.php:                  if($qry->QDO("DELETE FROM phpgw_cal_repeats WHERE cal_id = :cal_id",array( ':cal_id' => $CALID)))
+caldav-DELETE.php:                      @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:           @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:       //$request->DoResponse( 500, translate("Error querying database.") );
+caldav-DELETE.php:  //if ( $qry->Exec('DELETE',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-DELETE.php:  //  while( $row = $qry->Fetch() ) {
+caldav-DELETE.php:  //    delete_collection($row->child_id);
+caldav-DELETE.php:  //if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params )
+caldav-DELETE.php:  //  && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+caldav-DELETE.php:  //  && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+caldav-DELETE.php:  //  && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params )
+caldav-DELETE.php:  //  && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params ) ) {
+caldav-DELETE.php:  //  @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:if ( !$dav_resource->Exists() )$request->DoResponse( 404 );
+caldav-DELETE.php:if ( ! ( $dav_resource->resource_id() > 0 ) ) {
+caldav-DELETE.php:  $request->DoResponse( 403 );
+caldav-DELETE.php:$qry->Begin();
+caldav-DELETE.php:if ( $dav_resource->IsBinding() ) {
+caldav-DELETE.php:  $params = array( ':dav_name' => $dav_resource->dav_name() );
+caldav-DELETE.php:  if ( $qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params )
+caldav-DELETE.php:    && $qry->Commit() ) {
+caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:    $request->DoResponse( 204 );
+caldav-DELETE.php:else if ( $dav_resource->IsCollection() ) {
+caldav-DELETE.php:  if ( delete_collection( $nome,$CALID ) && $qry->Commit() ) {
+caldav-DELETE.php:    $request->DoResponse( 204 );
+caldav-DELETE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag() ) ) {
+caldav-DELETE.php:    $request->DoResponse( 412, translate("Resource has changed on server - not deleted") );
+caldav-DELETE.php:$request->DoResponse( 500 );
+caldav-DELTICKET.php:* CalDAV Server - handle DELTICKET method in line with defunct proposed RFC
+caldav-DELTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+caldav-DELTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-DELTICKET.php:if ( ! $request->HavePrivilegeTo('DAV::unbind') && $request->ticket->owner() != $session->principal_id ) {
+caldav-DELTICKET.php:  $request->NeedPrivilege('DAV::unbind');
+caldav-DELTICKET.php:if ( ! isset($request->ticket) ) {
+caldav-DELTICKET.php:    $r = new DAVResource($request->path);
+caldav-DELTICKET.php:    if ( ! $r->Exists() ) {
+caldav-DELTICKET.php:      $request->PreconditionFailed(404,'not-found');
+caldav-DELTICKET.php:      $request->PreconditionFailed(412,'ticket-does-not-exist','The specified ticket does not exist');
+caldav-DELTICKET.php:    $request->MalformedRequest('No ticket specified');
+caldav-DELTICKET.php:$qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=:ticket_id', array( ':ticket_id'   => $request->ticket->id() ) );
+caldav-DELTICKET.php:if ( $qry->Exec('DELTICKET', __LINE__, __FILE__) ) {
+caldav-DELTICKET.php:  $request->DoResponse( 204 );
+caldav-DELTICKET.php:$request->DoResponse( 500 );
+caldav-GET.php:* CalDAV Server - handle GET method
+caldav-GET.php:$dav_resource = new DAVResource($request->path);
+caldav-GET.php:$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
+caldav-GET.php:if ( ! $dav_resource->Exists() ) {
+caldav-GET.php:  $request->DoResponse( 404, translate("Resource Not Found.") );
+caldav-GET.php:  // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
+caldav-GET.php:  $confidential->SetType($icalendar->GetType());
+caldav-GET.php:  $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-GET.php:  $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTSTART'), 'DTSTART' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('RRULE'), 'RRULE' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DURATION'), 'DURATION' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTEND'), 'DTEND' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('UID'), 'UID' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('CREATED'), 'CREATED' );
+caldav-GET.php:if ( $dav_resource->IsCollection() ) {
+caldav-GET.php:  if ( ! $dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections) ) {
+caldav-GET.php:    $request->DoResponse( 405, translate("GET requests on collections are only supported for calendars.") );
+caldav-GET.php:  if ( isset($c->get_includes_subcollections) && $c->get_includes_subcollections ) {
+caldav-GET.php:    $params = array( ':path_match' => '^'.$request->path );
+caldav-GET.php:    $params = array( ':collection_id' => $dav_resource->resource_id() );
+caldav-GET.php:  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+caldav-GET.php:  if ( !$qry->Exec("GET",__LINE__,__FILE__) ) {
+caldav-GET.php:    $request->DoResponse( 500, translate("Database Error") );
+caldav-GET.php:  $vcal->VCalendar();
+caldav-GET.php:  $displayname = $dav_resource->GetProperty('displayname');
+caldav-GET.php:    $vcal->AddProperty("X-WR-CALNAME", $displayname);
+caldav-GET.php:  while( $event = $qry->Fetch() ) {
+caldav-GET.php:    $ical = new iCalComponent( $event->caldav_data );
+caldav-GET.php:    $event_zones = $ical->GetComponents('VTIMEZONE',true);
+caldav-GET.php:      $tzid = $tz->GetPValue('TZID');
+caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DTSTART', 'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DUE',     'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DTEND',   'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      if ( $dav_resource->HavePrivilegeTo('all',false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user
+caldav-GET.php:            || ( $c->allow_get_email_visibility && $comp->IsAttendee($session->email) ) ) {
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:      if ( $event->class == 'PRIVATE' ) continue;
+caldav-GET.php:      if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL' ) {
+caldav-GET.php:       $vcal->AddComponent(obfuscated_event($comp));
+caldav-GET.php:      elseif ( isset($c->hide_alarm) && $c->hide_alarm ) {
+caldav-GET.php:        $comp->ClearComponents('VALARM');
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:    if ( isset($timezones[$tzid]) ) $vcal->AddComponent($timezones[$tzid]);
+caldav-GET.php:  $response = $vcal->Render();
+caldav-GET.php:  header( 'Content-Length: '.strlen($response) );
+caldav-GET.php:  header( 'Etag: '.$dav_resource->unique_tag() );
+caldav-GET.php:  $request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $response), 'text/calendar; charset="utf-8"' );
+caldav-GET.php:$resource = $dav_resource->resource();
+caldav-GET.php:$ic = new iCalComponent( $resource->caldav_data );
+caldav-GET.php:if ( $dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $resource->user_no || $session->user_no == $resource->logged_user
+caldav-GET.php:      || ( $c->allow_get_email_visibility && $ic->IsAttendee($session->email) ) ) {
+caldav-GET.php:else if ( $resource->class != 'PRIVATE' ) {
+caldav-GET.php:  if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || ( $resource->class == 'CONFIDENTIAL' && ! $request->HavePrivilegeTo('DAV::write-content') ) ) {
+caldav-GET.php:    $ical = new iCalComponent( $resource->caldav_data );
+caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+caldav-GET.php:    $ical->SetComponents( array($confidential), $resource->caldav_type );
+caldav-GET.php:    $resource->caldav_data = $ical->Render();
+caldav-GET.php:// else $resource->class == 'PRIVATE' and this person may not see it.
+caldav-GET.php:  $request->DoResponse( 403, translate("Forbidden") );
+caldav-GET.php:header( 'Etag: "'.$resource->dav_etag.'"' );
+caldav-GET.php:header( 'Content-Length: '.strlen($resource->caldav_data) );
+caldav-GET.php:switch( $resource->caldav_type ) {
+caldav-GET.php:    $contenttype = 'text/x-vcard';
+caldav-GET.php:$request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $resource->caldav_data), $contenttype.'; charset="utf-8"' );
+caldav-LOCK.php:if ( ! $request->AllowedTo('write') ) {
+caldav-LOCK.php:  $request->NeedPrivilege( 'write', $request->path );
+caldav-LOCK.php:if ( ! isset($request->xml_tags) ) {
+caldav-LOCK.php:  if ( isset($request->lock_token) ) {
+caldav-LOCK.php:    $request->xml_tags = array();
+caldav-LOCK.php:    $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-LOCK.php:foreach( $request->xml_tags AS $k => $v ) {
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:$request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff.
+caldav-LOCK.php:$lock_opener = $request->FailIfLocked();
+caldav-LOCK.php:if ( $request->method == "LOCK" ) {
+caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to lock resource '%s'", $request->path);
+caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+caldav-LOCK.php:        ':dav_name'      => $request->path,
+caldav-LOCK.php:        ':request_depth' => $request->depth,
+caldav-LOCK.php:        ':timeout'       => $request->timeout.' seconds'
+caldav-LOCK.php:    header( "Lock-Token: <opaquelocktoken:$lock_token>" );
+caldav-LOCK.php:  $qry->Exec("LOCK",__LINE__,__FILE__);
+caldav-LOCK.php:  $lock_row = $request->GetLockRow($lock_token);
+caldav-LOCK.php:      new XMLElement( 'locktype',  new XMLElement( $lock_row->type )),
+caldav-LOCK.php:      new XMLElement( 'lockscope', new XMLElement( $lock_row->scope )),
+caldav-LOCK.php:      new XMLElement( 'depth',     $request->GetDepthName() ),
+caldav-LOCK.php:      new XMLElement( 'owner',     new XMLElement( 'href', $lock_row->owner )),
+caldav-LOCK.php:      new XMLElement( 'timeout',   'Second-'.$request->timeout),
+caldav-LOCK.php:elseif (  $request->method == "UNLOCK" ) {
+caldav-LOCK.php:  * @TODO: respond with preconditionfailed(409,'lock-token-matches-request-uri') if
+caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to unlock resource '%s'", $request->path);
+caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+caldav-LOCK.php:    $qry->Exec("LOCK",__LINE__,__FILE__);
+caldav-LOCK.php:  $request->DoResponse( 204 );
+caldav-LOCK.php:$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+caldav-LOCK.php:$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-MKCOL.php:* CalDAV Server - handle MKCOL and MKCALENDAR method
+caldav-MKCOL.php:* @copyright Catalyst IT Ltd, Morphoss Ltd - http://www.morphoss.com/
+caldav-MKCOL.php:$request->NeedPrivilege('DAV::bind');
+caldav-MKCOL.php:$displayname = $request->path;
+caldav-MKCOL.php:if ( ! preg_match( '#/$#', $request->path ) ) {
+caldav-MKCOL.php:  dbg_error_log( 'MKCOL', 'Add trailling "/" to "%s"', $request->path);
+caldav-MKCOL.php:  $request->path .= '/';
+caldav-MKCOL.php:if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
+caldav-MKCOL.php:if ( $parent->IsSchedulingCollection( 'inbox' ) ) {
+caldav-MKCOL.php:  $request->PreconditionFailed(403, 'urn:ietf:params:xml:ns:caldav:no-mkcol-in-inbox' );
+caldav-MKCOL.php:$request_type = $request->method;
+caldav-MKCOL.php:if ( isset($request->xml_tags) ) {
+caldav-MKCOL.php:  $xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-MKCOL.php:  if ( $xmltree->GetTag() == 'DAV::mkcol' ) $request_type = 'extended-mkcol';
+caldav-MKCOL.php:  if ( $xmltree->GetTag() != 'urn:ietf:params:xml:ns:caldav:mkcalendar' && $request_type != 'extended-mkcol' ) {
+caldav-MKCOL.php:    $request->DoResponse( 406, sprintf('The XML is not a "DAV::mkcol" or "urn:ietf:params:xml:ns:caldav:mkcalendar" document (%s)', $xmltree->GetTag()) );
+caldav-MKCOL.php:  $setprops = $xmltree->GetContent();   // <set>
+caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // <prop>
+caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // the array of properties.
+caldav-MKCOL.php:    $tag = $setting->GetTag();
+caldav-MKCOL.php:    $content = $setting->RenderContent();
+caldav-MKCOL.php:        $is_addressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+caldav-MKCOL.php:        $is_calendar    = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+caldav-MKCOL.php:          $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-MKCOL.php:              new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+caldav-MKCOL.php:          $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+caldav-MKCOL.php:          dbg_error_log( 'MKCOL', 'Displayname is "/" to "%s"', $request->path);
+caldav-MKCOL.php:          $parent_container = $request->path;
+caldav-MKCOL.php:          $request->path .= $content . '/';
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':  /** Ignored, since we will support all component types */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-resource-size':  /** Ignored, since we will support arbitrary size */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:min-date-time':  /** Ignored, since we will support arbitrary time */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-date-time':  /** Ignored, since we will support arbitrary time */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-instances':  /** Ignored, since we will support arbitrary instances */
+caldav-MKCOL.php:      * The following properties are read-only, so they will cause the request to fail
+caldav-MKCOL.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-MKCOL.php:            new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+caldav-MKCOL.php:            new XMLElement('responsedescription', translate('Property is read-only') )
+caldav-MKCOL.php:      $props[] = new XMLElement($reply->Tag($tag));
+caldav-MKCOL.php:    if ( $request_type == 'extended-mkcol' ) {
+caldav-MKCOL.php:      $request->DoResponse( $failure_code, $reply->Render('mkcol-response', array_merge( $status, $failure ), 'text/xml; charset="utf-8"' ) );
+caldav-MKCOL.php:      array_unshift( $failure, $reply->href( ConstructURL($request->path) ) );
+caldav-MKCOL.php:      $request->DoResponse( 207, $reply->Render('multistatus', new XMLElement( 'response', $failure )), 'text/xml; charset="utf-8"' );
+caldav-MKCOL.php:$qry = new AwlQuery( $sql, array( ':dav_name' => $request->path) );
+caldav-MKCOL.php:if ( ! $qry->Exec('MKCOL',__LINE__,__FILE__) ) {
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error querying database.') );
+caldav-MKCOL.php:if ( $qry->rows() != 0 ) {
+caldav-MKCOL.php:  $request->DoResponse( 405, translate('A collection already exists at that location.') );
+caldav-MKCOL.php:$qry->Begin();
+caldav-MKCOL.php:if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
+caldav-MKCOL.php:              ':user_no'          => $request->user_no,
+caldav-MKCOL.php:              ':dav_name'         => $request->path,
+caldav-MKCOL.php:              ':dav_etag'         => md5($request->user_no. $request->path),
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+caldav-MKCOL.php:  if ( ! $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text )',
+caldav-MKCOL.php:             array( ':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v) ) ) {
+caldav-MKCOL.php:    $request->DoResponse( 500, translate('Error writing calendar properties to database.') );
+caldav-MKCOL.php:if ( !$qry->Commit() ) {
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+caldav-MKCOL.php:dbg_error_log( 'MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
+caldav-MKCOL.php:header('Cache-Control: no-cache'); /** RFC4791 mandates this at 5.3.1 */
+caldav-MKCOL.php:$request->DoResponse( 201, '' );
+caldav-MKCOL.php:*   <?xml version="1.0" encoding="utf-8" ?>
+caldav-MKCOL.php:*         <C:calendar-description xml:lang="en">Calendar restricted to events.</C:calendar-description>
+caldav-MKCOL.php:*         <C:supported-calendar-component-set>
+caldav-MKCOL.php:*         </C:supported-calendar-component-set>
+caldav-MKCOL.php:*         <C:calendar-timezone><![CDATA[BEGIN:VCALENDAR
+caldav-MKCOL.php:*   PRODID:-//Example Corp.//CalDAV Client//EN
+caldav-MKCOL.php:*   TZID:US-Eastern
+caldav-MKCOL.php:*   LAST-MODIFIED:19870101T000000Z
+caldav-MKCOL.php:*   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+caldav-MKCOL.php:*   TZOFFSETFROM:-0400
+caldav-MKCOL.php:*   TZOFFSETTO:-0500
+caldav-MKCOL.php:*   TZOFFSETFROM:-0500
+caldav-MKCOL.php:*   TZOFFSETTO:-0400
+caldav-MKCOL.php:*   ]]></C:calendar-timezone>
+caldav-MKTICKET.php:* CalDAV Server - handle MKTICKET method in line with defunct proposed RFC
+caldav-MKTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+caldav-MKTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-MKTICKET.php:$request->NeedPrivilege('DAV::bind');
+caldav-MKTICKET.php:$target = new DAVResource( $request->path );
+caldav-MKTICKET.php:if ( ! $target->Exists() ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 404, new XMLElement( 'error', new XMLElement('resource-must-not-be-null'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:if ( ! isset($request->xml_tags) ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-MKTICKET.php:if ( $xmltree->GetTag() != 'http://www.xythos.com/namespaces/StorageServer:ticketinfo' &&
+caldav-MKTICKET.php:     $xmltree->GetTag() != 'DAV::ticketinfo' ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:$ticket_timeout = 'Seconds-3600';
+caldav-MKTICKET.php:$ticket_privs_array = array('read-free-busy');
+caldav-MKTICKET.php:foreach( $xmltree->GetContent() AS $k => $v ) {
+caldav-MKTICKET.php:  switch( $v->GetTag() ) {
+caldav-MKTICKET.php:      $ticket_timeout = $v->GetContent();
+caldav-MKTICKET.php:      $ticket_privs_array = $v->GetElements(); // Ensure we always get an array back
+caldav-MKTICKET.php:        $ticket_privileges |= privilege_to_bits( $v1->GetTag() );
+caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read') )           $ticket_privileges |= privilege_to_bits( array('read-free-busy', 'read-current-user-privilege-set') );
+caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read-free-busy') ) $ticket_privileges |= privilege_to_bits( 'schedule-query-freebusy');
+caldav-MKTICKET.php:else if ( preg_match( '{^([a-z]+)-(\d+)$}i', $ticket_timeout, $matches ) ) {
+caldav-MKTICKET.php:  /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */
+caldav-MKTICKET.php:$collection_id = $target->GetProperty('collection_id');
+caldav-MKTICKET.php:$resource_id   = $target->GetProperty('dav_id');
+caldav-MKTICKET.php:      ':owner'       => $session->principal_id,
+caldav-MKTICKET.php:  $result = $qry->Exec('MKTICKET', __LINE__, __FILE__);
+caldav-MKTICKET.php:  $reply->NSElement($privs, $v);
+caldav-MKTICKET.php:      new XMLElement( 'owner', $reply->href( ConstructURL('/'.$session->username.'/') ) ),
+caldav-MKTICKET.php:$prop = new XMLElement( "prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray() );
+caldav-MKTICKET.php:$request->XMLResponse( 200, $prop );
+caldav-MOVE.php:* CalDAV Server - handle MOVE method
+caldav-MOVE.php:$request->NeedPrivilege('DAV::unbind');
+caldav-MOVE.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['move']) && $c->dbg['move'])) ) {
+caldav-MOVE.php:    fwrite($fh,$request->raw_post);
+caldav-MOVE.php:$lock_opener = $request->FailIfLocked();
+caldav-MOVE.php:$dest = new DAVResource($request->destination);
+caldav-MOVE.php:if ( $dest->dav_name() == '/' || $dest->IsPrincipal() ) {
+caldav-MOVE.php:  $dest->NeedPrivilege('DAV::bind');
+caldav-MOVE.php:if ( ! $dest->ContainerExists() ) {
+caldav-MOVE.php:  $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-MOVE.php:if ( ! $request->overwrite && $dest->Exists() ) {
+caldav-MOVE.php:  $request->DoResponse( 412, translate('Not overwriting existing destination resource') );
+caldav-MOVE.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' ) {
+caldav-MOVE.php:  $request->DoResponse( 412 );  /** request to move, but only if there is no source? WTF! */
+caldav-MOVE.php:$src  = new DAVResource($request->path);
+caldav-MOVE.php:if ( ! $src->Exists() ) {
+caldav-MOVE.php:  $request->DoResponse( 412, translate('Source resource does not exist.') );
+caldav-MOVE.php:if ( $src->IsCollection() ) {
+caldav-MOVE.php:  switch( $dest->ContainerType() ) {
+caldav-MOVE.php:    case 'schedule-inbox':
+caldav-MOVE.php:    case 'schedule-outbox':
+caldav-MOVE.php:      $request->DoResponse( 412, translate('Special collections may not contain a calendar or other special collection.') );
+caldav-MOVE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != '' )
+caldav-MOVE.php:        || ( isset($request->etag_none_match) && $request->etag_none_match != '') ) {
+caldav-MOVE.php:    * (without the If-None-Match header) on that resource, or if "*" is
+caldav-MOVE.php:    if ( isset($request->etag_if_match) && $request->etag_if_match != $src->unique_tag() ) {
+caldav-MOVE.php:      $error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
+caldav-MOVE.php:    else if ( isset($request->etag_none_match) && $request->etag_none_match != '' && $request->etag_none_match == $src->unique_tag() ) {
+caldav-MOVE.php:      $error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
+caldav-MOVE.php:    if ( $error != '' ) $request->DoResponse( 412, $error );
+caldav-MOVE.php:$src->NeedPrivilege('DAV::unbind');
+caldav-MOVE.php:$dest->NeedPrivilege('DAV::write-content');
+caldav-MOVE.php:if ( ! $dest->Exists() ) $dest->NeedPrivilege('DAV::bind');
+caldav-MOVE.php:  $qry->Exec('move'); // Just in case
+caldav-MOVE.php:  $request->DoResponse( $response_code );
+caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:$src_name = $src->dav_name();
+caldav-MOVE.php:$dst_name = $dest->dav_name();
+caldav-MOVE.php:$src_collection = $src->GetProperty('collection_id');
+caldav-MOVE.php:$dst_collection = $dest->GetProperty('collection_id');
+caldav-MOVE.php:$src_user_no = $src->GetProperty('user_no');
+caldav-MOVE.php:$dst_user_no = $dest->GetProperty('user_no');
+caldav-MOVE.php:if ( $src->IsCollection()  ) {
+caldav-MOVE.php:  if ( $dest->Exists() ) {
+caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( $dest->Exists() ) {
+caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:    log_caldav_action( 'DELETE', $src->GetProperty('uid'), $src_user_no, $src_collection, $src_name );
+caldav-MOVE.php:    ':sync_type' => ( $dest->Exists() ? 200 : 201 )
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:    log_caldav_action( ( $dest->Exists() ? 'UPDATE' : 'INSERT' ), $src->GetProperty('uid'), $dst_user_no, $dst_collection, $dst_name );
+caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:$request->DoResponse( 200 );
+caldav-OPTIONS.php:* CalDAV Server - handle OPTIONS method
+caldav-OPTIONS.php:$resource = new DAVResource($request->path);
+caldav-OPTIONS.php://$resource->NeedPrivilege( 'DAV::read', true );
+caldav-OPTIONS.php:if ( !$resource->Exists() ) {
+caldav-OPTIONS.php:  $request->DoResponse( 404, translate("No collection found at that location.") );
+caldav-OPTIONS.php:$allowed = implode( ', ', array_keys($resource->FetchSupportedMethods()) );
+caldav-OPTIONS.php:$request->DoResponse( 200, "" );
+caldav-POST.php:* CalDAV Server - handle PUT method
+caldav-POST.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-POST.php:include_once('caldav-PUT-functions.php');
+caldav-POST.php:include_once('freebusy-functions.php');
+caldav-POST.php:if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
+caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-invite")
+caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
+caldav-POST.php:  // $request->DoResponse(403);
+caldav-POST.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['post'])) ) {
+caldav-POST.php:    fwrite($fh,$request->raw_post);
+caldav-POST.php:  $fbq_start = $ic->GetPValue('DTSTART');
+caldav-POST.php:  $fbq_end   = $ic->GetPValue('DTEND');
+caldav-POST.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
+caldav-POST.php:  $attendees = $ic->GetProperties('ATTENDEE');
+caldav-POST.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-POST.php:    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-POST.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-POST.php:    //$params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email );
+caldav-POST.php:    //if ( !$qry->Exec('POST',__LINE__,__FILE__) ) $request->DoResponse( 501, 'Database error');
+caldav-POST.php:    //if ( $qry->rows() > 1 ) {
+caldav-POST.php:    //  if ( !$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params ) )
+caldav-POST.php:    //    $request->DoResponse( 501, 'Database error');
+caldav-POST.php:    //  if ( $qry->rows() == 0 ) {
+caldav-POST.php:        /** Sigh... Go back to the original case-insensitive match */
+caldav-POST.php:    //    $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
+caldav-POST.php:    $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
+caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+caldav-POST.php:    //if ( $qry->rows() == 0 ) {
+caldav-POST.php:    //  $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
+caldav-POST.php:    //  $reply->CalDAVElement($response, "calendar-data" );
+caldav-POST.php:    //if ( ! $attendee_usr = $qry->Fetch() ) $request->DoResponse( 501, 'Database error');
+caldav-POST.php:    //if ( (privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0 ) {
+caldav-POST.php:    //  $reply->CalDAVElement($response, "request-status", "3.8;No authority" );
+caldav-POST.php:    //  $reply->CalDAVElement($response, "calendar-data" );
+caldav-POST.php:    $attendee_path_match = '^/'.$attendee_usr->username.'/';
+caldav-POST.php:    $fb = get_freebusy( $attendee_path_match, $attendee_email,$range_start, $range_end, bindec($attendee_usr->p) );
+caldav-POST.php:    $fb->AddProperty( 'UID',       $ic->GetPValue('UID') );
+caldav-POST.php:    $fb->SetProperties( $ic->GetProperties('ORGANIZER'), 'ORGANIZER');
+caldav-POST.php:    $fb->AddProperty( $attendee );
+caldav-POST.php:    $vcal->VCalendar( array('METHOD' => 'REPLY') );
+caldav-POST.php:    $vcal->AddComponent( $fb );
+caldav-POST.php:    $response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+caldav-POST.php:    $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+caldav-POST.php:    $reply->CalDAVElement($response, "calendar-data", $vcal->Render() );
+caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:  $request->XMLResponse( 200, $response );
+caldav-POST.php:  $responses[] = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:  $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray() );
+caldav-POST.php:  $request->XMLResponse( 200, $response );
+caldav-POST.php:$ical = new iCalComponent( $request->raw_post );
+caldav-POST.php:$method =  $ical->GetPValue('METHOD');
+caldav-POST.php:$resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REQUEST" method with "%s" component.', $method, $first->GetType() );
+caldav-POST.php:    if ( $first->GetType() == 'VFREEBUSY' )
+caldav-POST.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-POST.php:      dbg_error_log('POST', 'Ignoring iTIP "REQUEST" with "%s" component.', $first->GetType() );
+caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REPLY" with "%s" component.', $first->GetType() );
+CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+CalDAVPrincipal.php:  * @var Whether or not we are using an e-mail address based URL.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read permissions on.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read-write prmissions for.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have read permissions on this one.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have write permissions on this one.
+CalDAVPrincipal.php:   * @var The dav_name for this principal - a partial path
+CalDAVPrincipal.php:    $this->exists = null;
+CalDAVPrincipal.php:    $this->url = null;
+CalDAVPrincipal.php:    $this->by_email = false;
+CalDAVPrincipal.php:      dbg_error_log( 'principal', 'Principal: record for %s', $parameters->username );
+CalDAVPrincipal.php:      $this->user_no = $parameters['user_no'];
+CalDAVPrincipal.php:        $this->username = $parameters['username'];
+CalDAVPrincipal.php:        $this->user_no = $parameters['user_no'];
+CalDAVPrincipal.php:        if ( $username = $this->UsernameFromEMail($parameters['email']) ) {
+CalDAVPrincipal.php:          $this->username = $username;
+CalDAVPrincipal.php:        if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
+CalDAVPrincipal.php:          $this->username = $username;
+CalDAVPrincipal.php:      else if ( isset($parameters['principal-property-search']) ) {
+CalDAVPrincipal.php:        $usr = $this->PropertySearch($parameters['principal-property-search']);
+CalDAVPrincipal.php:      $this->exists = false;
+CalDAVPrincipal.php:    $this->exists = true;
+CalDAVPrincipal.php:    $this->InitialiseRecord($usr);
+CalDAVPrincipal.php:      $this->url = $parameters['path'];
+CalDAVPrincipal.php:      $this->dav_name = $parameters['path'];
+CalDAVPrincipal.php:      $this->{$k} = $v;
+CalDAVPrincipal.php:    if ( !isset($this->modified) ) $this->modified = $this->updated;
+CalDAVPrincipal.php:    if ( !isset($this->created) )  $this->created  = $this->joined;
+CalDAVPrincipal.php:    $this->dav_etag = md5($this->username . $this->updated);
+CalDAVPrincipal.php:    $this->_is_group = (isset($usr->type_id) && $usr->type_id == 3);
+CalDAVPrincipal.php:    $this->principal_url = ConstructURL( '/'.$this->username.'/', true );
+CalDAVPrincipal.php:    $this->url = $this->principal_url;
+CalDAVPrincipal.php:    $this->principal_address = $this->principal_url . 'principal.vcf';
+CalDAVPrincipal.php:    $this->user_address_set = array(
+CalDAVPrincipal.php:       'mailto:'.$this->email,
+CalDAVPrincipal.php:       ConstructURL( '/'.$this->username.'/', true ),
+CalDAVPrincipal.php://       ConstructURL( '/~'.$this->username.'/', true ),
+CalDAVPrincipal.php://       ConstructURL( '/__uuids__/'.$this->username.'/', true ),
+CalDAVPrincipal.php:    $this->schedule_inbox_url = sprintf( '%s.in/', $this->url);
+CalDAVPrincipal.php:    $this->schedule_outbox_url = sprintf( '%s.out/', $this->url);
+CalDAVPrincipal.php:    $this->dropbox_url = sprintf( '%s.drop/', $this->url);
+CalDAVPrincipal.php:    $this->notifications_url = sprintf( '%s.notify/', $this->url);
+CalDAVPrincipal.php:    if ( isset ( $c->notifications_server ) ) {
+CalDAVPrincipal.php:      $this->xmpp_uri = 'xmpp:pubsub.'.$c->notifications_server['host'].'?pubsub;node=/davical-'.$this->principal_id;
+CalDAVPrincipal.php:      $this->xmpp_server = $c->notifications_server['host'];
+CalDAVPrincipal.php:    if ( $this->_is_group ) {
+CalDAVPrincipal.php:      $this->group_member_set = array();
+CalDAVPrincipal.php:      $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=member_id) JOIN usr USING(user_no) WHERE group_id = :group_id ORDER BY principal.principal_id ', array( ':group_id' => $this->principal_id) );
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $member = $qry->Fetch() ) {
+CalDAVPrincipal.php:          $this->group_member_set[] = ConstructURL( '/'. $member->username . '/', true);
+CalDAVPrincipal.php:    $this->group_membership = array();
+CalDAVPrincipal.php:    $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=group_id) JOIN usr USING(user_no) WHERE member_id = :member_id UNION SELECT usr.username FROM group_member LEFT JOIN grants ON (to_principal=group_id) JOIN principal ON (principal_id=by_principal) JOIN usr USING(user_no) WHERE member_id = :member_id and by_principal != member_id ORDER BY 1', array( ':member_id' => $this->principal_id ) );
+CalDAVPrincipal.php:    if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:      while( $group = $qry->Fetch() ) {
+CalDAVPrincipal.php:        $this->group_membership[] = ConstructURL( '/'. $group->username . '/', true);
+CalDAVPrincipal.php:    $this->read_proxy_group = null;
+CalDAVPrincipal.php:    $this->write_proxy_group = null;
+CalDAVPrincipal.php:    $this->write_proxy_for = null;
+CalDAVPrincipal.php:    $this->read_proxy_for = null;
+CalDAVPrincipal.php:    dbg_error_log( 'principal', ' User: %s (%d) URL: %s, Home: %s, By Email: %d', $this->username, $this->user_no, $this->url, $this->by_email );
+CalDAVPrincipal.php:    $this->read_proxy_group = array();
+CalDAVPrincipal.php:    $this->write_proxy_group = array();
+CalDAVPrincipal.php:    $this->write_proxy_for = array();
+CalDAVPrincipal.php:    $this->read_proxy_for = array();
+CalDAVPrincipal.php:    if ( !isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy === false ) {
+CalDAVPrincipal.php:      $params = array( ':request_principal' => $this->principal_id, ':scan_depth' => $c->permission_scan_depth );
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+CalDAVPrincipal.php:          if ( (bindec($relationship->pprivs) & $write_priv) != 0 ) {
+CalDAVPrincipal.php:            $this->write_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-write/', true);
+CalDAVPrincipal.php:            $this->read_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-read/', true);
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+CalDAVPrincipal.php:          if ( bindec($relationship->pprivs) & $write_priv ) {
+CalDAVPrincipal.php:            $this->write_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->read_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-for:    %s', implode(',',$this->read_proxy_for) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-for:   %s', implode(',',$this->write_proxy_for) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-group:  %s', implode(',',$this->read_proxy_group) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-group: %s', implode(',',$this->write_proxy_group) );
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->read_proxy_group;
+CalDAVPrincipal.php:    if ( !isset($this->write_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->write_proxy_group;
+CalDAVPrincipal.php:  * @param string read/write - which sort of proxy list is requested.
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_for) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    if ( $type == 'write' ) return $this->write_proxy_for;
+CalDAVPrincipal.php:    return $this->read_proxy_for;
+CalDAVPrincipal.php:  * Accessor for the group membership - the groups this principal is a member of
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->group_membership;
+CalDAVPrincipal.php:  * Accessor for the group member set - the members of this group
+CalDAVPrincipal.php:    if ( ! $this->_is_group ) return null;
+CalDAVPrincipal.php:    return $this->group_member_set;
+CalDAVPrincipal.php:  * @param array $options The request options, controlling whether e-mail paths are allowed.
+CalDAVPrincipal.php:      return $session->username;
+CalDAVPrincipal.php:      $username = $this->UsernameFromEMail($username);
+CalDAVPrincipal.php:  * Work out the username, based on the given e-mail
+CalDAVPrincipal.php:    @dbg_error_log( 'principal', 'Retrieving username from e-mail address "%s" ', $email );
+CalDAVPrincipal.php:    if ( $qry->Exec('principal') && $user = $qry->Fetch() ) {
+CalDAVPrincipal.php:      $username = $user->username;
+CalDAVPrincipal.php:      $this->by_email = true;
+CalDAVPrincipal.php:    return $this->exists;
+CalDAVPrincipal.php:    return $this->_is_group;
+CalDAVPrincipal.php:    return (isset($this->username)?$this->username:'username not set');
+CalDAVPrincipal.php:    if ( !isset($this->dav_name) ) {
+CalDAVPrincipal.php:      if ( !isset($this->username) ) $this->dav_name = '';
+CalDAVPrincipal.php:      else $this->dav_name = '/'.$this->username.'/';
+CalDAVPrincipal.php:    return $this->dav_name;
+CalDAVPrincipal.php:    if ( !isset($this->calendar_home_set) ) {
+CalDAVPrincipal.php:      $this->calendar_home_set = array();
+CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_calendar AND user_no = :user_no', array( ':user_no' => $this->user_no));
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+CalDAVPrincipal.php:          while( $calendar = $qry->Fetch() ) {
+CalDAVPrincipal.php:            $this->calendar_home_set[] = ConstructURL($calendar->parent_container, true);
+CalDAVPrincipal.php:          $this->calendar_home_set[] = $this->principal_url;
+CalDAVPrincipal.php:    return $this->calendar_home_set;
+CalDAVPrincipal.php:    if ( !isset($this->addressbook_home_set) ) {
+CalDAVPrincipal.php:     $this->addressbook_home_set = array();
+CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_addressbook AND user_no = :user_no', array( ':user_no' => $this->user_no));
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+CalDAVPrincipal.php:          while( $addressbook = $qry->Fetch() ) {
+CalDAVPrincipal.php:            $this->addressbook_home_set[] = ConstructURL($addressbook->parent_container, true);
+CalDAVPrincipal.php:          $this->addressbook_home_set[] = $this->principal_url;
+CalDAVPrincipal.php:    return $this->addressbook_home_set;
+CalDAVPrincipal.php:    if ( !isset($this->calendar_free_busy_set) ) {
+CalDAVPrincipal.php:      * calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV
+CalDAVPrincipal.php:      $this->calendar_free_busy_set = array();
+CalDAVPrincipal.php:                        array( ':user_no' => $this->user_no) );
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        while( $calendar = $qry->Fetch() ) {
+CalDAVPrincipal.php:          $this->calendar_free_busy_set[] = ConstructURL($calendar->dav_name, true);
+CalDAVPrincipal.php:    return $this->calendar_free_busy_set;
+CalDAVPrincipal.php:    if ( !isset($this->privileges) ) $this->privileges = 0;
+CalDAVPrincipal.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+CalDAVPrincipal.php:    return $this->privileges;
+CalDAVPrincipal.php:                            'collection_id' => (isset($this->principal_id) ? $this->principal_id : 0),
+CalDAVPrincipal.php:                            'type'     => 'principal' . (substr($this->dav_name(), 0, 12) == '/principals/'?'_link':''),
+CalDAVPrincipal.php:                            'user_no'  => (isset($this->user_no)  ? $this->user_no : 0),
+CalDAVPrincipal.php:                            'username' => $this->username(),
+CalDAVPrincipal.php:                            'dav_name' => $this->dav_name,
+CalDAVPrincipal.php:                            'email'    => (isset($this->email)    ? $this->email : ''),
+CalDAVPrincipal.php:                            'created'  => (isset($this->created)  ? $this->created : date('Ymd\THis')),
+CalDAVPrincipal.php:                            'updated'  => (isset($this->updated)  ? $this->updated : date('Ymd\THis'))
+CalDAVPrincipal.php:    $collection->dav_etag = (isset($this->dav_etag) ? $this->dav_etag : md5($collection->username . $collection->updated));
+CalDAVPrincipal.php:    $collection->dav_displayname =  (isset($this->dav_displayname) ? $this->dav_displayname : (isset($this->fullname) ? $this->fullname : $collection->username));
+CalDAVPrincipal.php:        $prop->NewElement('getcontenttype', 'httpd/unix-directory' );
+CalDAVPrincipal.php:        $prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
+CalDAVPrincipal.php:        $prop->NewElement('displayname', $this->fullname );
+CalDAVPrincipal.php:      case 'DAV::principal-URL':
+CalDAVPrincipal.php:        $prop->NewElement('principal-URL', $reply->href($this->principal_url) );
+CalDAVPrincipal.php:        $prop->NewElement('getlastmodified', ISODateToHTTPDate($this->modified) );
+CalDAVPrincipal.php:        $prop->NewElement('creationdate', DateToISODate($this->created) );
+CalDAVPrincipal.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+CalDAVPrincipal.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+CalDAVPrincipal.php:        $prop->NewElement('getcontentlanguage', $locale );
+CalDAVPrincipal.php:      case 'DAV::group-member-set':
+CalDAVPrincipal.php:        if ( ! $this->_is_group ) return false;
+CalDAVPrincipal.php:        $prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
+CalDAVPrincipal.php:      case 'DAV::group-membership':
+CalDAVPrincipal.php:        $prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:dropbox-home-URL':
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-server':
+CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-uri':
+CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:carddav:addressbook-home-set':
+CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->addressbook_home_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->calendar_home_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+CalDAVPrincipal.php:        $reply->CalDAVElement( $prop, 'calendar-free-busy-set', $reply->href( $this->calendar_free_busy_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
+CalDAVPrincipal.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url ) );
+CalDAVPrincipal.php:      case 'DAV::principal-collection-set':
+CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
+CalDAVPrincipal.php:      case 'DAV::alternate-URI-set':
+CalDAVPrincipal.php:        $prop->NewElement( $reply->Tag($tag));
+CalDAVPrincipal.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+CalDAVPrincipal.php:        $denied[] = $reply->Tag($tag);
+CalDAVPrincipal.php:    dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
+CalDAVPrincipal.php:      if ( ! $this->PrincipalProperty( $tag, $prop, $reply, $denied ) ) {
+CalDAVPrincipal.php:        dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
+CalDAVPrincipal.php:        $not_found[] = $reply->Tag($tag);
+CalDAVPrincipal.php:    $href = $reply->href($this->url );
+CalDAVPrincipal.php:        $noprop->NewElement( $v );
+CalDAVPrincipal.php:        $noprop->NewElement( $v );
+caldav-PROPFIND.php:* CalDAV Server - handle PROPFIND method
+caldav-PROPFIND.php://$request->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read-current-user-privilege-set') );
+caldav-PROPFIND.php:if ( !isset($request->xml_tags) ) {
+caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body contains no XML data!") );
+caldav-PROPFIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body is not valid XML data!") );
+caldav-PROPFIND.php:$allprop    = $xmltree->GetPath('/DAV::propfind/*');
+caldav-PROPFIND.php:  switch ( $propwrap->GetTag() ) {
+caldav-PROPFIND.php:      $subprop = $propwrap->GetElements();
+caldav-PROPFIND.php:        $property_list[] = $v->GetTag();
+caldav-PROPFIND.php: * Add the calendar-proxy-read/write pseudocollections
+caldav-PROPFIND.php:  if ($parent_path != $request->principal->dav_name()) {
+caldav-PROPFIND.php:    dbg_error_log( 'PROPFIND', 'Not returning proxy response since "%s" != "%s"', $parent_path, $request->principal->dav_name() );
+caldav-PROPFIND.php:    $proxy_group = $request->principal->ReadProxyGroup();
+caldav-PROPFIND.php:    $proxy_group = $request->principal->WriteProxyGroup();
+caldav-PROPFIND.php:  $collection->parent_container = $parent_path;
+caldav-PROPFIND.php:  $collection->dav_name = $parent_path.'calendar-proxy-'.$which.'/';
+caldav-PROPFIND.php:  $collection->is_calendar    = 'f';
+caldav-PROPFIND.php:  $collection->is_addressbook = 'f';
+caldav-PROPFIND.php:  $collection->is_principal   = 't';
+caldav-PROPFIND.php:  $collection->is_proxy       = 't';
+caldav-PROPFIND.php:  $collection->proxy_type     = $which;
+caldav-PROPFIND.php:  $collection->type           = 'proxy';
+caldav-PROPFIND.php:  $collection->dav_displayname = $collection->dav_name;
+caldav-PROPFIND.php:  $collection->collection_id = 0;
+caldav-PROPFIND.php:  $collection->user_no = $session->user_no;
+caldav-PROPFIND.php:  $collection->username = $session->username;
+caldav-PROPFIND.php:  $collection->email = $session->email;
+caldav-PROPFIND.php:  $collection->created = date('Ymd\THis');
+caldav-PROPFIND.php:  $collection->dav_etag = md5($c->system_name . $collection->dav_name . implode($proxy_group) );
+caldav-PROPFIND.php:  $collection->proxy_for = $proxy_group;
+caldav-PROPFIND.php:  $collection->resourcetypes  = sprintf('<DAV::collection/><http://calendarserver.org/ns/:calendar-proxy-%s/>', $which);
+caldav-PROPFIND.php:  $collection->in_freebusy_set = 'f';
+caldav-PROPFIND.php:  $collection->schedule_transp = 'transp';
+caldav-PROPFIND.php:  $collection->timezone        = null;
+caldav-PROPFIND.php:  $collection->description     = '';
+caldav-PROPFIND.php:  $resource->FetchPrincipal();
+caldav-PROPFIND.php:  return $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:  $bound_from = $collection->bound_from();
+caldav-PROPFIND.php:  $bound_to   = $collection->dav_name();
+caldav-PROPFIND.php:  if ( !isset($parent_path) ) $parent_path = $collection->dav_name();
+caldav-PROPFIND.php:                                                              $depth, $collection->dav_name(), $bound_from, $bound_to );
+caldav-PROPFIND.php:  if ( ! $collection->IsCalendar() &&  ! $collection->IsAddressbook() ) {
+caldav-PROPFIND.php:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+caldav-PROPFIND.php:      if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:        while( $binding = $qry->Fetch() ) {
+caldav-PROPFIND.php:          $resource = new DAVResource($binding->dav_name);
+caldav-PROPFIND.php:          //if ( $resource->HavePrivilegeTo('DAV::read', false) ) {
+caldav-PROPFIND.php:            $resource->set_bind_location( str_replace($bound_from,$bound_to,$binding->dav_name));
+caldav-PROPFIND.php:            $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:              $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource, $binding->dav_name ) );
+caldav-PROPFIND.php:    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:      while( $subcollection = $qry->Fetch() ) {
+caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$subcollection->dav_name));
+caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:          $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource,
+caldav-PROPFIND.php:                                                   str_replace($resource->parent_path(), $parent_path, $resource->dav_name() ) ) );
+caldav-PROPFIND.php:    if ( $collection->IsPrincipal() ) {
+caldav-PROPFIND.php:      // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
+caldav-PROPFIND.php:      dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $bound_from );
+caldav-PROPFIND.php:  if ( $collection->HavePrivilegeTo('DAV::read', false) ) {
+caldav-PROPFIND.php:    if ( $collection->IsCalendar() ) {
+caldav-PROPFIND.php:      if ( ! $collection->HavePrivilegeTo('all', false) ) {
+caldav-PROPFIND.php:      if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+caldav-PROPFIND.php:        $time_limit_clause = " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+caldav-PROPFIND.php:    $nome = substr($request->username, 0, 9);
+caldav-PROPFIND.php:    if ( $qryp->Exec("PROPFIND",__LINE__,__FILE__) && $qryp->rows() > 0 ) {
+caldav-PROPFIND.php:       while( $part = $qryp->Fetch() ) {
+caldav-PROPFIND.php:           $sql = "SELECT * FROM phpgw_cal WHERE cal_id = $part->cal_id AND cal_type = 'E'";
+caldav-PROPFIND.php:           if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:              while( $item = $qry->Fetch() ) {
+caldav-PROPFIND.php:                       $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->owner));
+caldav-PROPFIND.php:                       $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+caldav-PROPFIND.php:    $sql = "SELECT * FROM phpgw_cal INNER JOIN phpgw_cal_repeats USING(cal_id) WHERE cal_id = $part->cal_id";
+caldav-PROPFIND.php:    if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:      while( $item = $qry->Fetch() ) {
+caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->owner));
+caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+caldav-PROPFIND.php:    //if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY caldav_data.dav_id";
+caldav-PROPFIND.php:    //if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:    //  while( $item = $qry->Fetch() ) {
+caldav-PROPFIND.php:    //    $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->dav_name));
+caldav-PROPFIND.php:    //    $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+caldav-PROPFIND.php:if ( $request->IsProxyRequest() ) {
+caldav-PROPFIND.php:  $response = add_proxy_response($request->proxy_type, $request->principal->dav_name() );
+caldav-PROPFIND.php:  $resource = new DAVResource($request->path);
+caldav-PROPFIND.php:  if ( ! $resource->Exists() ) {
+caldav-PROPFIND.php:    $request->PreconditionFailed( 404, 'must-exist', translate('That resource is not present on this server.') );
+caldav-PROPFIND.php:  //$resource->NeedPrivilege('DAV::read');
+caldav-PROPFIND.php:  if ( $resource->IsCollection() ) {
+caldav-PROPFIND.php:    dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name() );
+caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:    if ( $request->depth > 0 ) {
+caldav-PROPFIND.php:      $responses = array_merge($responses, get_collection_contents( $request->depth - 1, $resource ) );
+caldav-PROPFIND.php:  //elseif ( $request->HavePrivilegeTo('DAV::read',false) ) 
+caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:$xmldoc = $reply->Render('multistatus', $responses);
+caldav-PROPFIND.php:$request->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:* CalDAV Server - handle PROPPATCH method
+caldav-PROPPATCH.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-PROPPATCH.php:$dav_resource = new DAVResource($request->path);
+caldav-PROPPATCH.php:if ( ! ($dav_resource->HavePrivilegeTo('DAV::write-properties') || $dav_resource->IsBinding() ) ) {
+caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+caldav-PROPPATCH.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-PROPPATCH.php:// echo $xmltree->Render();
+caldav-PROPPATCH.php:if ( $xmltree->GetTag() != "DAV::propertyupdate" ) {
+caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+caldav-PROPPATCH.php:$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
+caldav-PROPPATCH.php:$rmprops  = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
+caldav-PROPPATCH.php:* the special-case stuff as needed and falling through to a default which
+caldav-PROPPATCH.php:$qry->Begin();
+caldav-PROPPATCH.php:$setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+caldav-PROPPATCH.php:      * Can't set displayname on resources - only collections or principals
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() || $dav_resource->IsPrincipal() ) {
+caldav-PROPPATCH.php:        if ( $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        else if ( $dav_resource->IsPrincipal() ) {
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':user_no' => $request->user_no) );
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:      $setcollection  = count($setting->GetPath('DAV::resourcetype/DAV::collection'));
+caldav-PROPPATCH.php:      $setaddressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal()
+caldav-PROPPATCH.php:                            && ! $dav_resource->IsBinding() && ! ($setaddressbook && $setcalendar) ) {
+caldav-PROPPATCH.php:        $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET is_calendar = :is_calendar::boolean, is_addressbook = :is_addressbook::boolean,
+caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':resourcetypes' => $resourcetypes,
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-calendar-transp':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && ( $dav_resource->IsCalendar() || $setcalendar ) && !$dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $transparency = $setting->GetPath('urn:ietf:params:xml:ns:caldav:schedule-calendar-transp/*');
+caldav-PROPPATCH.php:        $transparency = preg_replace( '{^.*:}', '', $transparency[0]->GetTag());
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET schedule_transp = :transparency WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':transparency' => $transparency ) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                                new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                                translate("The CalDAV:schedule-calendar-transp property may only be set on calendars.") )
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:          new XMLElement( 'responsedescription', translate("The calendar-free-busy-set is superseded by the schedule-transp property of a calendar collection.") )
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone');
+caldav-PROPPATCH.php:        $tzstring = $tzcomponent[0]->GetContent();
+caldav-PROPPATCH.php:        $timezones = $calendar->component->GetComponents('VTIMEZONE');
+caldav-PROPPATCH.php:        $tzid = $tz->GetPValue('TZID');
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                       array( ':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                               new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                               translate("Property is read-only") )
+caldav-PROPPATCH.php:      $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text)',
+caldav-PROPPATCH.php:            array( ':dav_name' => $dav_resource->dav_name(), ':user_no' => $request->user_no, ':tag' => $tag, ':value' => $content) );
+caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = NULL WHERE dav_name = :dav_name', array( ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:CALENDAR-USER-ADDRESS-SET':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:          new XMLElement('responsedescription', translate("Property is read-only") )
+caldav-PROPPATCH.php:      $qry->QDo('DELETE FROM property WHERE dav_name=:dav_name AND property_name=:property_name',
+caldav-PROPPATCH.php:                  array( ':dav_name' => $dav_resource->dav_name(), ':property_name' => $tag) );
+caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+caldav-PROPPATCH.php:  $qry->Rollback();
+caldav-PROPPATCH.php:  $request->DoResponse( 207, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:if ( $qry->Commit() ) {
+caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+caldav-PROPPATCH.php:  $request->DoResponse( 200, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:$request->DoResponse( 500 );
+caldav-PUT-default.php:* CalDAV Server - handle PUT method on unknown (arbitrary) content-types
+caldav-PUT-default.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT-default.php:    fwrite($fh,$request->raw_post);
+caldav-PUT-default.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT-default.php:$dest = new DAVResource($request->path);
+caldav-PUT-default.php:$container = $dest->FetchParentContainer();
+caldav-PUT-default.php:if ( $container->IsCalendar() ) {
+caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:caldav:supported-calendar-data',
+caldav-PUT-default.php:                  translate('Incorrect content type for calendar: ') . $request->content_type );
+caldav-PUT-default.php:else if ( $container->IsAddressbook() ) {
+caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:carddav:supported-address-data',
+caldav-PUT-default.php:                  translate('Incorrect content type for addressbook: ') . $request->content_type );
+caldav-PUT-default.php:if ( ! $dest->Exists() ) {
+caldav-PUT-default.php:  if ( $container->IsPrincipal() ) {
+caldav-PUT-default.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+caldav-PUT-default.php:  if ( ! $container->Exists() ) {
+caldav-PUT-default.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-PUT-default.php:  $container->NeedPrivilege('DAV::bind');
+caldav-PUT-default.php:  if ( $dest->IsCollection() ) {
+caldav-PUT-default.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+caldav-PUT-default.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+caldav-PUT-default.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+caldav-PUT-default.php:  $dest->NeedPrivilege('DAV::write-content');
+caldav-PUT-default.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+caldav-PUT-default.php:  $request->DoResponse(412);
+caldav-PUT-default.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+caldav-PUT-default.php:  $request->DoResponse(412);
+caldav-PUT-default.php:$collection_id = $container->GetProperty('collection_id');
+caldav-PUT-default.php:$qry->Begin();
+caldav-PUT-default.php:$etag = md5($request->raw_post);
+caldav-PUT-default.php:    ':user_no' => $dest->GetProperty('user_no'),
+caldav-PUT-default.php:    ':dav_name' => $dest->bound_from(),
+caldav-PUT-default.php:    ':dav_data' => $request->raw_post,
+caldav-PUT-default.php:    ':session_user' => $session->user_no
+caldav-PUT-default.php:if ( $dest->Exists() ) {
+caldav-PUT-default.php:$qry->QDo( $sql, $params );
+caldav-PUT-default.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+caldav-PUT-default.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-PUT-default.php:$request->DoResponse( $response_code );
+caldav-PUT-functions.php:* CalDAV Server - handle PUT method
+caldav-PUT-functions.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-PUT-functions.php:$tz_regex = ':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z_]+$:i';
+caldav-PUT-functions.php:  $qry->Rollback();
+caldav-PUT-functions.php:    if ( isset($bad_events) && isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+caldav-PUT-functions.php:      $request->DoResponse( $error_no, $message );
+caldav-PUT-functions.php:  $c->messages[] = sprintf(translate('Status: %d, Message: %s, User: %d, Path: %s'), $error_no, $message, $user_no, $path);
+caldav-PUT-functions.php:  if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) {
+caldav-PUT-functions.php:    if ( !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+caldav-PUT-functions.php:      if ( $qry->rows() == 0 ) {
+caldav-PUT-functions.php:        $request->DoResponse( 405 ); // Method not allowed
+caldav-PUT-functions.php:    if ( $qry->rows() == 0 ) {
+caldav-PUT-functions.php:      $qry->QDo( $sql, $params );
+caldav-PUT-functions.php:      $collection = $qry->Fetch();
+caldav-PUT-functions.php:      $params = array( ':is_public' => ($public?'t':'f'), ':collection_id' => $collection->collection_id );
+caldav-PUT-functions.php:      if ( ! $qry->QDo($sql,$params) ) {
+caldav-PUT-functions.php:  if( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+caldav-PUT-functions.php:    $collection = $qry->Fetch();
+caldav-PUT-functions.php:    if ($collection->public_events_only == 't') {
+caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+caldav-PUT-functions.php:  $attendees = $ic->GetProperties('ATTENDEE');
+caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-PUT-functions.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+caldav-PUT-functions.php:    if ( $attendee->GetParameterValue ( 'PARTSTAT' ) != 'NEEDS-ACTION' || preg_match ( '/^[35]\.[3-9]/',  $attendee->GetParameterValue ( 'SCHEDULE-STATUS' ) ) ) {
+caldav-PUT-functions.php:      $attendee->SetParameterValue ('SCHEDULE-STATUS','3.7;Invalid Calendar User');
+caldav-PUT-functions.php:    $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+caldav-PUT-functions.php:    $priv =  $ar->HavePrivilegeTo('schedule-deliver-invite' );
+caldav-PUT-functions.php:    if ( ! $ar->HavePrivilegeTo('schedule-deliver-invite' ) ){
+caldav-PUT-functions.php:      $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+caldav-PUT-functions.php:      $reply->NSElement( $privnodes[1], 'schedule-deliver-invite' );
+caldav-PUT-functions.php:      $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+caldav-PUT-functions.php:      $xmldoc = $reply->Render('error',$xml);
+caldav-PUT-functions.php:      $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"');
+caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+caldav-PUT-functions.php:    $ncal->VCalendar ();
+caldav-PUT-functions.php:    $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+caldav-PUT-functions.php:    $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:    $content = $ncal->Render();
+caldav-PUT-functions.php:    $cid = $ar->GetProperty('collection_id');
+caldav-PUT-functions.php:    dbg_error_log('DELIVER', 'to user: %s, to path: %s, collection: %s, from user: %s, caldata %s', $attendee_principal->user_no, $deliver_path, $cid, $request->user_no, $content );
+caldav-PUT-functions.php:    write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:      $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+caldav-PUT-functions.php:  $ncal->VCalendar ();
+caldav-PUT-functions.php:  $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+caldav-PUT-functions.php:  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:  $content = $ncal->Render();
+caldav-PUT-functions.php: 	$deliver_path = preg_replace ( '/^.*caldav.php/','', $request->principal->schedule_inbox_url );
+caldav-PUT-functions.php:  write_resource( $request->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:    $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:  header('Schedule-Tag: "'.$etag . '"' );
+caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+caldav-PUT-functions.php:	$organizer = $ic->GetProperties('ORGANIZER');
+caldav-PUT-functions.php:  $attendees = array_merge($organizer,$ic->GetProperties('ATTENDEE'));
+caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:	  $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+caldav-PUT-functions.php:	  if ( ! $ar->HavePrivilegeTo('schedule-deliver-reply' ) ){
+caldav-PUT-functions.php:	     $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+caldav-PUT-functions.php:	     $reply->NSElement( $privnodes[1], 'schedule-deliver-reply' );
+caldav-PUT-functions.php:	     $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+caldav-PUT-functions.php:	     $xmldoc = $reply->Render('error',$xml);
+caldav-PUT-functions.php:			 $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-PUT-functions.php:	  $ncal->VCalendar ();
+caldav-PUT-functions.php:	  $ncal->AddProperty ( 'METHOD', 'REPLY' );
+caldav-PUT-functions.php:	  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:	  $content = $ncal->Render();
+caldav-PUT-functions.php:	  write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:	    $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+caldav-PUT-functions.php:* @return float The result of the scheduling request, per caldav-sched #3.5.4
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+caldav-PUT-functions.php:  if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put'])) ) {
+caldav-PUT-functions.php:    $fh = fopen('/tmp/PUT-2.txt','w');
+caldav-PUT-functions.php:  $timezones = $calendar->GetComponents('VTIMEZONE',true);
+caldav-PUT-functions.php:  $components = $calendar->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $displayname = $calendar->GetPValue('X-WR-CALNAME');
+caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:    $tz_ids[$tz->GetPValue('TZID')] = $k;
+caldav-PUT-functions.php:    $uid = $comp->GetPValue('UID');
+caldav-PUT-functions.php:    $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
+caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $comp->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:  if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:  if ( ! $qry->rows() == 1 ) {
+caldav-PUT-functions.php:  $collection = $qry->Fetch();
+caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) $qry->Begin();
+caldav-PUT-functions.php:  $base_params = array( ':collection_id' => $collection->collection_id );
+caldav-PUT-functions.php:    if ( !$qry->QDo('DELETE FROM calendar_item WHERE collection_id = :collection_id', $base_params)
+caldav-PUT-functions.php:      || !$qry->QDo('DELETE FROM caldav_data WHERE collection_id = :collection_id', $base_params) )
+caldav-PUT-functions.php:      rollback_on_error( $caldav_context, $user_no, $collection->collection_id );
+caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Begin();
+caldav-PUT-functions.php:    $vcal->VCalendar();
+caldav-PUT-functions.php:    $vcal->SetComponents($resource);
+caldav-PUT-functions.php:    $icalendar = $vcal->Render();
+caldav-PUT-functions.php:    $dav_data_params[':caldav_type'] = $first->GetType();
+caldav-PUT-functions.php:    $dav_data_params[':session_user'] = $session->user_no;
+caldav-PUT-functions.php:    if ( !$qry->QDo($dav_data_insert,$dav_data_params) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:    $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $dav_data_params[':dav_name']));
+caldav-PUT-functions.php:    if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+caldav-PUT-functions.php:      $dav_id = $row->dav_id;
+caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DTSTART');
+caldav-PUT-functions.php:    if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+caldav-PUT-functions.php:      $dtstart = $first->GetPValue('DUE');
+caldav-PUT-functions.php:    $dtend = $first->GetPValue('DTEND');
+caldav-PUT-functions.php:      dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:      if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+caldav-PUT-functions.php:        $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:      elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-PUT-functions.php:        * non-inclusive end is the end of the calendar date specified by the
+caldav-PUT-functions.php:        * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+caldav-PUT-functions.php:        $value_type = $first->GetPParamValue('DTSTART','VALUE');
+caldav-PUT-functions.php:    $last_modified = $first->GetPValue('LAST-MODIFIED');
+caldav-PUT-functions.php:    $dtstamp = $first->GetPValue('DTSTAMP');
+caldav-PUT-functions.php:    $class = ($collection->public_events_only == 't' ? 'PUBLIC' : $first->GetPValue('CLASS') );
+caldav-PUT-functions.php:    $tzid = $first->GetPParamValue('DTSTART','TZID');
+caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:        $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+caldav-PUT-functions.php:          $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+caldav-PUT-functions.php:      if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+caldav-PUT-functions.php:        $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+caldav-PUT-functions.php:        $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+caldav-PUT-functions.php:    $calitem_params[':uid'] = $first->GetPValue('UID');
+caldav-PUT-functions.php:    $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+caldav-PUT-functions.php:    $calitem_params[':location'] = $first->GetPValue('LOCATION');
+caldav-PUT-functions.php:    $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+caldav-PUT-functions.php:    $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+caldav-PUT-functions.php:    $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+caldav-PUT-functions.php:    $calitem_params[':url'] = $first->GetPValue('URL');
+caldav-PUT-functions.php:    $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+caldav-PUT-functions.php:    $calitem_params[':due'] = $first->GetPValue('DUE');
+caldav-PUT-functions.php:    $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+caldav-PUT-functions.php:    $calitem_params[':status'] = $first->GetPValue('STATUS');
+caldav-PUT-functions.php:    $created = $first->GetPValue('CREATED');
+caldav-PUT-functions.php:    if ( !$qry->QDo($sql,$calitem_params) ) rollback_on_error( $caldav_context, $user_no, $path);
+caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Commit();
+caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) {
+caldav-PUT-functions.php:    if ( ! $qry->Commit() ) rollback_on_error( $caldav_context, $user_no, $path);
+caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $alarms = $ical->GetComponents('VALARM');
+caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_alarm ( dav_id, action, trigger, summary, description, component, next_trigger )
+caldav-PUT-functions.php:  $qry->Prepare();
+caldav-PUT-functions.php:    $trigger = array_merge($v->GetProperties('TRIGGER'));
+caldav-PUT-functions.php:    $trigger_type = $trigger->GetParameterValue('VALUE');
+caldav-PUT-functions.php:      switch ( $trigger->GetParameterValue('RELATED') ) {
+caldav-PUT-functions.php:        case 'DTEND':  $related = $ical->GetPValue('DTEND'); break;
+caldav-PUT-functions.php:        case 'DUE':    $related = $ical->GetPValue('DUE');   break;
+caldav-PUT-functions.php:        default:       $related = $ical->GetPValue('DTSTART');
+caldav-PUT-functions.php:      $duration = $trigger->Value();
+caldav-PUT-functions.php:      $minus = (substr($duration,0,1) == '-');
+caldav-PUT-functions.php:      $related_trigger = trim(preg_replace( '#[PT-]#', ' ', $duration ));
+caldav-PUT-functions.php:        $related_trigger = preg_replace( '{(\d+[WDHMS])}', '-$1 ', $related_trigger );
+caldav-PUT-functions.php:    $qry->Bind(':action', $v->GetPValue('ACTION'));
+caldav-PUT-functions.php:    $qry->Bind(':trigger', $trigger->Render());
+caldav-PUT-functions.php:    $qry->Bind(':summary', $v->GetPValue('SUMMARY'));
+caldav-PUT-functions.php:    $qry->Bind(':description', $v->GetPValue('DESCRIPTION'));
+caldav-PUT-functions.php:    $qry->Bind(':component', $v->Render());
+caldav-PUT-functions.php:    $qry->Bind(':related', $related );
+caldav-PUT-functions.php:    $qry->Bind(':related_trigger', $related_trigger );
+caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $attendees = $ical->GetProperties('ATTENDEE');
+caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_attendee ( dav_id, status, partstat, cn, attendee, role, rsvp, property )
+caldav-PUT-functions.php:  $qry->Prepare();
+caldav-PUT-functions.php:    $attendee = $v->Value();
+caldav-PUT-functions.php:      dbg_error_log( 'LOG', 'Duplicate: "%s"', $v->Render() );
+caldav-PUT-functions.php:    $qry->Bind(':attendee', $attendee );
+caldav-PUT-functions.php:    $qry->Bind(':status',   $v->GetParameterValue('STATUS') );
+caldav-PUT-functions.php:    $qry->Bind(':partstat', $v->GetParameterValue('PARTSTAT') );
+caldav-PUT-functions.php:    $qry->Bind(':cn',       $v->GetParameterValue('CN') );
+caldav-PUT-functions.php:    $qry->Bind(':role',     $v->GetParameterValue('ROLE') );
+caldav-PUT-functions.php:    $qry->Bind(':rsvp',     $v->GetParameterValue('RSVP') );
+caldav-PUT-functions.php:    $qry->Bind(':property', $v->Render() );
+caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:    $processed[$attendee] = $v->Render();
+caldav-PUT-functions.php:  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+caldav-PUT-functions.php:    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+caldav-PUT-functions.php:    $resource_type = $first->GetType();
+caldav-PUT-functions.php:  $UID = $first->GetPValue('UID');
+caldav-PUT-functions.php: $qry->Exec("PUT");
+caldav-PUT-functions.php: if ($qry->rows() < 1) {
+caldav-PUT-functions.php:                $qrycompa->Exec("PUT");
+caldav-PUT-functions.php:                if ($qrycompa->rows() < 1) {
+caldav-PUT-functions.php:  elseif ($qry->rows() == 1) {
+caldav-PUT-functions.php:  	$alarme_time = $first->GetPValue('TRIGGER');
+caldav-PUT-functions.php:        $alarme_descarta = $first->GetPValue('X-MOZ-LASTACK');
+caldav-PUT-functions.php:        $start = $first->GetPValue('DTSTART');
+caldav-PUT-functions.php:        $HJ = date("Y-m-d 00:00:00");
+caldav-PUT-functions.php:        $hj_seg = $hoje->format("U");
+caldav-PUT-functions.php:        $DT_START = $dateTime->format("U");
+caldav-PUT-functions.php:        $hora = $dateTime->format("G");
+caldav-PUT-functions.php:        $minuto = $dateTime->format("i");
+caldav-PUT-functions.php:         $end = $first->GetPValue('DTEND');
+caldav-PUT-functions.php:        $DT_END = $dateTime_end->format("U");
+caldav-PUT-functions.php:        $execao = $first->GetPValue('EXDATE');
+caldav-PUT-functions.php:        $modify = $first->GetPValue('LAST-MODIFIED');
+caldav-PUT-functions.php:        $MOD = $dateTime_mod->format("U");
+caldav-PUT-functions.php:        $snooze = "X-MOZ-SNOOZE-TIME-$seg";
+caldav-PUT-functions.php:        $adia_alarme = $first->GetPValue("$snooze");
+caldav-PUT-functions.php:        $prioridade = $first->GetPValue('PRIORITY');
+caldav-PUT-functions.php:        $RECUR = $first->GetPValue('RRULE');
+caldav-PUT-functions.php:        $categoria_utf8 = $first->GetPValue('CATEGORIES');
+caldav-PUT-functions.php:                        //$EXEC = $dateTime->format("U");
+caldav-PUT-functions.php:        $titulo_utf8 =  $first->GetPValue('SUMMARY');
+caldav-PUT-functions.php:        $descricao_utf8 = preg_replace('/\n/', "\r\n",  $first->GetPValue('DESCRIPTION'));
+caldav-PUT-functions.php:        $recur_id =  $first->GetPValue('RECURRENCE-ID');
+caldav-PUT-functions.php:        $cidade_utf8 =  $first->GetPValue('LOCATION');
+caldav-PUT-functions.php:        $class =  $first->GetPValue('CLASS');
+caldav-PUT-functions.php:                $phpgw_query->Exec();                                              
+caldav-PUT-functions.php:                if ( $phpgw_query->rows() == 0 ) {                                   
+caldav-PUT-functions.php:                    if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                           
+caldav-PUT-functions.php:                    $phpgw_query->Exec();                                                                                                                                                                                                    
+caldav-PUT-functions.php:                    $categg = $phpgw_query->Fetch();                                                                                                                                                                                         
+caldav-PUT-functions.php:                    $category_id = $categg->cat_id;                                                                                                                                                                                          
+caldav-PUT-functions.php:                   { while ($categgg = $phpgw_query->Fetch())
+caldav-PUT-functions.php:                        if ( $categgg->cat_access == 'public' || $categgg->cat_owner == "$nome")
+caldav-PUT-functions.php:                            $category_id = $categgg->cat_id;                                    
+caldav-PUT-functions.php:        $tipo_data = substr($alarme_time, -1);
+caldav-PUT-functions.php:        $alarme = $DT_START - $reducao;
+caldav-PUT-functions.php:        $participantes =  $first->GetProperties('ATTENDEE');             
+caldav-PUT-functions.php:        //$test = $ic->component->FirstNonTimezone();      
+caldav-PUT-functions.php:        //$propriedades = $test->GetProperties('PARTSTAT');
+caldav-PUT-functions.php:        //      dbg_error_log("PUT","UMBERTO %s", $c->Value());
+caldav-PUT-functions.php:                        $user = $v->content;         
+caldav-PUT-functions.php:                        //$atrr = new  ldapDrivers($c->authenticate_hook['config']);
+caldav-PUT-functions.php:                                        $stat = $v->GetParameterValue('PARTSTAT');                                 
+caldav-PUT-functions.php:                                                case "NEEDS-ACTION": $estado[$k] = 'U';                            
+caldav-PUT-functions.php:                                $teste = $first->CollectParameterValues('PARTSTAT');                           
+caldav-PUT-functions.php:                                        case "NEEDS-ACTION": $estado[0] = 'U';                                         
+caldav-PUT-functions.php:                                                    $laststack = "X-MOZ-LASTACK-PART-$nome"; 
+caldav-PUT-functions.php:                                                    if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                     
+caldav-PUT-functions.php:                                                    if ($qryusr->rows() == 0) {                                                                                                                             
+caldav-PUT-functions.php:                                                    if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                   
+caldav-PUT-functions.php:                                if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);       
+caldav-PUT-functions.php:                                $result=$qryslt->Exec("PUT");                                                                              
+caldav-PUT-functions.php:                                if ($result->rows() < 1){                                                                                    
+caldav-PUT-functions.php:                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                               
+caldav-PUT-functions.php:                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+caldav-PUT-functions.php:        $hostbanco = "-@{$iporigem}";
+caldav-PUT-functions.php:                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+caldav-PUT-functions.php:                $qryid->Exec("PUT");                                                                          
+caldav-PUT-functions.php:                $cal = $qryid->Fetch();                                                                       
+caldav-PUT-functions.php:                        $id = "cal:{$cal->max}:0";
+caldav-PUT-functions.php:                        $data = "a:5:{s:4:\"time\";{$time};s:6:\"offset\";i:{$reducao};s:5:\"owner\";i:{$nome};s:7:\"enabled\";i:1;s:6:\"cal_id\";s:5:\"{$cal->max}\";}";
+caldav-PUT-functions.php:                        if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+caldav-PUT-functions.php:                                $qryusr = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-LASTACK',:alarme)", array(':cal' => $cal->max, ':alarme' => $alarme_descarta));
+caldav-PUT-functions.php:                                if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                          
+caldav-PUT-functions.php:                        $total = preg_split("[;]", $RECUR, -1, PREG_SPLIT_NO_EMPTY);
+caldav-PUT-functions.php:                                                                $recur_enddate = $dateTime_until->format("U");
+caldav-PUT-functions.php:                                                                                $dias = $intervalo * ($val - 1) * 86400;
+caldav-PUT-functions.php:                                                                                $dias = $intervalo * ($val - 1) * 86400 * 7;
+caldav-PUT-functions.php:                                                                                $dias = $intervalo * ($val - 1) * 86400 * 31;
+caldav-PUT-functions.php:                                                                                $dias = $intervalo * ($val - 1) * 86400 * 365;
+caldav-PUT-functions.php:                                                                                //$semana = preg_split("[,]", $val, -1, PREG_SPLIT_NO_EMPTY);
+caldav-PUT-functions.php:                        $qryrepet = new AwlQuery("INSERT INTO phpgw_cal_repeats ( cal_id,recur_type, recur_use_end, recur_enddate, recur_interval,recur_data, recur_exception ) VALUES ( :cal,:tipo,0,:recur,:intervalo,:data,:exec)", array( ':cal' => $cal->max,':tipo' => $tipo, ':recur' => $recur_enddate, ':intervalo' => $intervalo, ':data' => $recur_data, ':exec' => $EXEC));                                                                                                                                                                                                               
+caldav-PUT-functions.php:                        if (!$qryrepet->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                  
+caldav-PUT-functions.php:                $qryusr = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:nome,'A','u')", array(':cal' => $cal->max, ':nome' => $nome));                                                                                      
+caldav-PUT-functions.php:                if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                            
+caldav-PUT-functions.php:                                $qrypart = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:part,'U','u')", array(':cal' => $cal->max, ':part' => $part[$k]));                                                                 
+caldav-PUT-functions.php:                                if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                           
+caldav-PUT-functions.php:                        $qrypart_ex = new AwlQuery("UPDATE phpgw_cal SET ex_participants=:expart WHERE owner=:nome AND cal_id=:cal", array(':expart' => $mails, ':nome' => $nome, ':cal' => $cal->max));                                                                                            
+caldav-PUT-functions.php:                        if (!$qrypart_ex->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                
+caldav-PUT-functions.php:                if ( $first->GetPValue('X-MOZ-SEND-INVITATIONS') == 'TRUE' ){
+caldav-PUT-functions.php:                        $qrypart = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-SEND-INVITATIONS','true')", array(':cal' => $cal->max));
+caldav-PUT-functions.php:                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                         
+caldav-PUT-functions.php:                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                     
+caldav-PUT-functions.php:                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                               
+caldav-PUT-functions.php:                        $qry->Exec("PUT");                                                                                                                                                                                                   
+caldav-PUT-functions.php:                        $qry->Exec("PUT");                                                                                                                                                                                                   
+caldav-PUT-functions.php:                        dbg_error_log("PUT", "argumento : '%s' valor : '%s' ", $alarme_descarta, $query->rows());                                                                                                                              
+caldav-PUT-functions.php:                        if ($qry->rows() != 0) {                                                                                                                                                                                               
+caldav-PUT-functions.php:                                        $qryusr = new AwlQuery("UPDATE phpgw_cal_extra set cal_extra_value=:alarme WHERE cal_id=:cal_id AND cal_extra_name = 'X-MOZ-LASTACK'",array(':alarme' =>  $alarme_descarta, ':cal_id' => $CALID));                                                 
+caldav-PUT-functions.php:                                        $qryusr->Exec("PUT");                                                                                                                                                                                
+caldav-PUT-functions.php:                                        $qryusr->Exec("PUT");                                                                                                                                                                                
+caldav-PUT-functions.php:                                        $qryusr = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-LASTACK',:alarme)", array(':cal' => $CALID,':alarme' =>  $alarme_descarta));
+caldav-PUT-functions.php:                                        if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                       
+caldav-PUT-functions.php:                                        if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                       
+caldav-PUT-functions.php:                        $qry->Exec("PUT");                                                                                                                                                      
+caldav-PUT-functions.php:                        if ($qry->rows == 0) {                                                                                                                                                  
+caldav-PUT-functions.php:                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+caldav-PUT-functions.php:                        $qry->Exec("PUT");                                                                                                                                                       
+caldav-PUT-functions.php:                                        VALUES ( DEFAULT,:hostbanco,:nome,:category_id,:DTSTART,:MOD,:DTEND,:prioridade,:tipo,:public,:titulo,:cidade,:descricao,:ref)",array(':hostbanco' => $hostbanco, ':nome' => $nome,':category_id' => $category_id,':DTSTART' => dataTParaSegundos($recur_id["DTSTART"]),':MOD' => dataTParaSegundos($recur_id["LAST-MODIFIED"]),':DTEND' => dataTParaSegundos($recur_id["DTEND"]) ,':prioridade' => $recur_id["PRIORITY"],':tipo' => $tipo,':public' => $public,':titulo' => $titulo,':cidade' => $cidade,':descricao' => $descricao,':ref' => $reference));                                                                                                                 
+caldav-PUT-functions.php:                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+caldav-PUT-functions.php:                                $qryid->Exec("PUT");                                                                                                                                             
+caldav-PUT-functions.php:                                $call = $qryid->Fetch();                                                                                                                                         
+caldav-PUT-functions.php:                                $qry = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:nome,'A','u');",array(':cal' => $call->max,':nome' => $nome));                             
+caldav-PUT-functions.php:                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+caldav-PUT-functions.php:                                if ( $qryid->Exec("PUT",__LINE__,__FILE__) && $qryid->rows() > 0 && $DT_START != $alarme)                                                                          
+caldav-PUT-functions.php:                                   {  $alarmew = $qryid->Fetch();                                                                                                                                
+caldav-PUT-functions.php:                                      $tempo_ant = $data_seg - $reducao;                                                                                                                         
+caldav-PUT-functions.php:                                      $id_cal_novo = "cal:$call->max:0";
+caldav-PUT-functions.php:                                      $dados_alarme= "a:5:{s:4:\"time\";i:$tempo_ant;s:6:\"offset\";i:$reducao;s:5:\"owner\";i:$nome;s:7:\"enabled\";i:1;s:6:\"cal_id\";s:5:\"$call->max\";}";   
+caldav-PUT-functions.php:                                      if (!$qrysy->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                       
+caldav-PUT-functions.php:                        $total = preg_split("[;]", $RECUR, -1, PREG_SPLIT_NO_EMPTY);                                                                                                                                                         
+caldav-PUT-functions.php:                                                                $recur_enddate = $dateTime_until->format("U");                                                                                                                               
+caldav-PUT-functions.php:                                                                                $semana = preg_split("[,]", $val, -1, PREG_SPLIT_NO_EMPTY);                                                                                                  
+caldav-PUT-functions.php:                        if (!$qryrepet->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                  
+caldav-PUT-functions.php:                //if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                       
+caldav-PUT-functions.php:                                        if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                 
+caldav-PUT-functions.php:                                        $result=$qryslt->Exec("PUT");                                                                                                                                                                        
+caldav-PUT-functions.php:                                        if ($result->rows < 1){                                                                                                                                                                              
+caldav-PUT-functions.php:                                                if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                           
+caldav-PUT-functions.php:                                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+caldav-PUT-functions.php:                        $qrypart_ex = new AwlQuery("UPDATE phpgw_cal SET ex_participants=:expart WHERE owner=:nome AND cal_id=:cal_id", array(':expart' => $mails,':nome' =>  $nome,':cal_id' => $cal->max));         
+caldav-PUT-functions.php:                        if (!$qrypart_ex->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                             
+caldav-PUT-functions.php:  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+caldav-PUT-functions.php:    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+caldav-PUT-functions.php:    $resource_type = $first->GetType();
+caldav-PUT-functions.php:  $qry->Begin();
+caldav-PUT-functions.php:  if ( !$qry->QDo($sql,$params) ) {
+caldav-PUT-functions.php:  $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
+caldav-PUT-functions.php:  if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+caldav-PUT-functions.php:    $dav_id = $row->dav_id;
+caldav-PUT-functions.php:  $dtstart = $first->GetPValue('DTSTART');
+caldav-PUT-functions.php:  if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DUE');
+caldav-PUT-functions.php:  $dtend = $first->GetPValue('DTEND');
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:    if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+caldav-PUT-functions.php:      $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-PUT-functions.php:      * non-inclusive end is the end of the calendar date specified by the
+caldav-PUT-functions.php:      * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+caldav-PUT-functions.php:      $value_type = $first->GetPParamValue('DTSTART','VALUE');
+caldav-PUT-functions.php:  $last_modified = $first->GetPValue('LAST-MODIFIED');
+caldav-PUT-functions.php:  $dtstamp = $first->GetPValue('DTSTAMP');
+caldav-PUT-functions.php:  $class = $first->GetPValue('CLASS');
+caldav-PUT-functions.php:  $tzid = $first->GetPParamValue('DTSTART','TZID');
+caldav-PUT-functions.php:  if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:  $timezones = $ic->GetComponents('VTIMEZONE');
+caldav-PUT-functions.php:    if ( $tz->GetPValue('TZID') != $tzid ) {
+caldav-PUT-functions.php:      dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
+caldav-PUT-functions.php:      $tzid = $tz->GetPValue('TZID');
+caldav-PUT-functions.php:    $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+caldav-PUT-functions.php:        $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+caldav-PUT-functions.php:    if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+caldav-PUT-functions.php:      $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+caldav-PUT-functions.php:      $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+caldav-PUT-functions.php:  $created = $first->GetPValue('CREATED');
+caldav-PUT-functions.php:  $calitem_params[':uid'] = $first->GetPValue('UID');
+caldav-PUT-functions.php:  $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+caldav-PUT-functions.php:  $calitem_params[':location'] = $first->GetPValue('LOCATION');
+caldav-PUT-functions.php:  $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+caldav-PUT-functions.php:  $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+caldav-PUT-functions.php:  $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+caldav-PUT-functions.php:  $calitem_params[':url'] = $first->GetPValue('URL');
+caldav-PUT-functions.php:  $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+caldav-PUT-functions.php:  $calitem_params[':due'] = $first->GetPValue('DUE');
+caldav-PUT-functions.php:  $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+caldav-PUT-functions.php:  $calitem_params[':status'] = $first->GetPValue('STATUS');
+caldav-PUT-functions.php:    log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+caldav-PUT-functions.php:            $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+caldav-PUT-functions.php:  if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
+caldav-PUT-functions.php:  $qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
+caldav-PUT-functions.php:  $qry->Commit();
+caldav-PUT-functions.php:  if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+caldav-PUT-functions.php:    $collection = $qry->Fetch();
+caldav-PUT-functions.php:    $user_no = $collection->user_no;
+caldav-PUT-functions.php:    return write_resource( $user_no, $path, $caldav_data, $collection->collection_id, $user_no, $etag, $ic, $put_action_type, false, $write_action_log );
+caldav-PUT-functions.php:        substr("$datatz", 0,4)) + $HorarioTimeZone->getoffset($HorarioTime);//ano
+caldav-PUT-functions.php:        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+caldav-PUT-functions.php:                $ajuste_dia=-86400;
+caldav-PUT.php:* CalDAV Server - handle PUT method
+caldav-PUT.php:$dav_resource = new DAVResource($request->path);
+caldav-PUT.php:if ( ! $dav_resource->HavePrivilegeTo('DAV::write-content') ) {
+caldav-PUT.php:  $request->DoResponse(403);
+caldav-PUT.php:if ( ! $dav_resource->Exists() && ! $dav_resource->HavePrivilegeTo('DAV::bind') ) {
+caldav-PUT.php:  $request->DoResponse(403);
+caldav-PUT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT.php:    fwrite($fh,$request->raw_post);
+caldav-PUT.php:include_once('caldav-PUT-functions.php');
+caldav-PUT.php://controlRequestContainer( $dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
+caldav-PUT.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT.php://if ( $dav_resource->IsCollection()  ) {
+caldav-PUT.php://  if ( $dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+caldav-PUT.php://    $request->DoResponse( 405 ); // Method not allowed
+caldav-PUT.php:  * as an import. The code is in caldav-PUT-functions.php
+caldav-PUT.php://  import_collection($request->raw_post,$request->user_no,$request->path,true, $appending);
+caldav-PUT.php://  $request->DoResponse( 200 );
+caldav-PUT.php:$etag = md5($request->raw_post);
+caldav-PUT.php:$ic = new iCalComponent( $request->raw_post );
+caldav-PUT.php:if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
+caldav-PUT.php:  $request->PreconditionFailed(412,'if-match');
+caldav-PUT.php:if ( $dav_resource->Exists() ) {
+caldav-PUT.php:  if ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $dav_resource->unique_tag() ) {
+caldav-PUT.php:    $request->PreconditionFailed(412,'if-match', translate( 'Existing resource does not match "If-Match" header - not accepted.'));
+caldav-PUT.php:  else if ( isset($request->etag_none_match) && $request->etag_none_match != ''
+caldav-PUT.php:               && ($request->etag_none_match == $dav_resource->unique_tag() || $request->etag_none_match == '*') ) {
+caldav-PUT.php:    * (without the If-None-Match header) on that resource, or if "*" is
+caldav-PUT.php:    $request->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
+caldav-PUT.php:$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
+caldav-PUT.php:write_resource( $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), $request->raw_post, $dav_resource->GetProperty('collection_id'),
+caldav-PUT.php:                                $session->user_no, $etag, $ic, $put_action_type, true, true );
+caldav-PUT.php:$request->DoResponse( ($dav_resource->Exists() ? 204 : 201) );
+caldav-PUT-vcard.php:* CalDAV Server - handle PUT method on VCARD content-types
+caldav-PUT-vcard.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT-vcard.php:    fwrite($fh,$request->raw_post);
+caldav-PUT-vcard.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT-vcard.php:$dest = new DAVResource($request->path);
+caldav-PUT-vcard.php:$container = $dest->FetchParentContainer();
+caldav-PUT-vcard.php:if ( ! $dest->Exists() ) {
+caldav-PUT-vcard.php:  if ( $container->IsPrincipal() ) {
+caldav-PUT-vcard.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+caldav-PUT-vcard.php:  if ( ! $container->Exists() ) {
+caldav-PUT-vcard.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-PUT-vcard.php:  $container->NeedPrivilege('DAV::bind');
+caldav-PUT-vcard.php:  if ( $dest->IsCollection() ) {
+caldav-PUT-vcard.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+caldav-PUT-vcard.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+caldav-PUT-vcard.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+caldav-PUT-vcard.php:  $dest->NeedPrivilege('DAV::write-content');
+caldav-PUT-vcard.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+caldav-PUT-vcard.php:  $request->DoResponse(412);
+caldav-PUT-vcard.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+caldav-PUT-vcard.php:  $request->DoResponse(412);
+caldav-PUT-vcard.php:$collection_id = $container->GetProperty('collection_id');
+caldav-PUT-vcard.php:$qry->Begin();
+caldav-PUT-vcard.php:$etag = md5($request->raw_post);
+caldav-PUT-vcard.php:    ':user_no' => $dest->GetProperty('user_no'),
+caldav-PUT-vcard.php:    ':dav_name' => $dest->bound_from(),
+caldav-PUT-vcard.php:    ':dav_data' => $request->raw_post,
+caldav-PUT-vcard.php:    ':session_user' => $session->user_no
+caldav-PUT-vcard.php:if ( $dest->Exists() ) {
+caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+caldav-PUT-vcard.php:  $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']) );
+caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+caldav-PUT-vcard.php:  $qry->QDo("SELECT currval('dav_id_seq') AS dav_id" );
+caldav-PUT-vcard.php:$row = $qry->Fetch();
+caldav-PUT-vcard.php:$vcard = new vCard( $request->raw_post );
+caldav-PUT-vcard.php:$vcard->Write( $row->dav_id, $dest->Exists() );
+caldav-PUT-vcard.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+caldav-PUT-vcard.php:if ( !$qry->QDo('COMMIT') ) rollback(500);
+caldav-PUT-vcard.php:$request->DoResponse( $response_code );
+caldav-REPORT-calquery.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+caldav-REPORT-calquery.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+caldav-REPORT-calquery.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+caldav-REPORT-calquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-query');
+caldav-REPORT-calquery.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-calquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/'.$proptype.'/*');
+caldav-REPORT-calquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-calquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-calquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-calquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-calquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-calquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-calquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-calquery.php: * There can only be *one* FILTER element, and it must contain *one* COMP-FILTER
+caldav-REPORT-calquery.php: * element.  In every case I can see this contained COMP-FILTER element will be a
+caldav-REPORT-calquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/urn:ietf:params:xml:ns:caldav:filter/*');
+caldav-REPORT-calquery.php:  if ( $qry_filters->GetTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $qry_filters->GetAttribute("name") == "VCALENDAR" )
+caldav-REPORT-calquery.php:    $qry_filters = $qry_filters->GetContent();  // Everything is inside a VCALENDAR AFAICS
+caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("name") );
+caldav-REPORT-calquery.php:  dbg_error_log("calquery","Applying filter for item '%s'", $item->dav_name );
+caldav-REPORT-calquery.php:  $ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
+caldav-REPORT-calquery.php:  return $ical->TestFilter($filters);
+caldav-REPORT-calquery.php:    $tag = $v->GetTag();
+caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
+caldav-REPORT-calquery.php:        $not_defined = "not-"; // then fall through to IS-DEFINED case
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-defined':
+caldav-REPORT-calquery.php:          dbg_error_log("calquery", "Could not handle 'is-%sdefined' on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
+caldav-REPORT-calquery.php:            case 'completed':  /** @todo when it can be handled in the SQL - see around line 200 below */
+caldav-REPORT-calquery.php:              if ( ! $target_collection->IsSchedulingCollection() ) {
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:time-range':
+caldav-REPORT-calquery.php:        $start_column = ($components[sizeof($components)-1] == 'VTODO' ? "due" : 'dtend');     // The column we compare against the START attribute
+caldav-REPORT-calquery.php:        $start = $v->GetAttribute("start");
+caldav-REPORT-calquery.php:        $finish = $v->GetAttribute("end");
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:text-match':
+caldav-REPORT-calquery.php:        $search = $v->GetContent();
+caldav-REPORT-calquery.php:        $negate = $v->GetAttribute("negate-condition");
+caldav-REPORT-calquery.php:        $collation = $v->GetAttribute("collation");
+caldav-REPORT-calquery.php:          case 'i;ascii-casemap':
+caldav-REPORT-calquery.php:        dbg_error_log("calquery", " text-match: (%s IS NULL OR %s%s %s '%s') ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:comp-filter':
+caldav-REPORT-calquery.php:        $comp_filter_name = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:prop-filter':
+caldav-REPORT-calquery.php:        $propertyname = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:          case 'PERCENT-COMPLETE':
+caldav-REPORT-calquery.php:            dbg_error_log("calquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:param-filter':
+caldav-REPORT-calquery.php:        return false; // Can't handle PARAM-FILTER conditions in the SQL
+caldav-REPORT-calquery.php:        $parameter = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:$target_collection = new DAVResource($request->path);
+caldav-REPORT-calquery.php:$bound_from = $target_collection->bound_from();
+caldav-REPORT-calquery.php:if ( !$target_collection->Exists() ) {
+caldav-REPORT-calquery.php:  $request->DoResponse( 404 );
+caldav-REPORT-calquery.php:if ( ! ($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection()) ) {
+caldav-REPORT-calquery.php:  $request->DoResponse( 403, translate('The calendar-query report must be run against a calendar or a scheduling collection') );
+caldav-REPORT-calquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+caldav-REPORT-calquery.php:if ( $target_collection->Privileges() != privilege_to_bits('DAV::all') ) {
+caldav-REPORT-calquery.php:if ( isset($c->hide_TODO) && $c->hide_TODO && ! $target_collection->HavePrivilegeTo('DAV::write-content') ) {
+caldav-REPORT-calquery.php:if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+caldav-REPORT-calquery.php:  $where .= " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+caldav-REPORT-calquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+caldav-REPORT-calquery.php:if ( $qry->Exec("calquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-calquery.php:  while( $calendar_object = $qry->Fetch() ) {
+caldav-REPORT-calquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+caldav-REPORT-calquery.php:        $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+caldav-REPORT-calquery.php:        $vResource = new vComponent($calendar_object->caldav_data);
+caldav-REPORT-calquery.php:        $calendar_object->caldav_data = $expanded->Render();
+caldav-REPORT-calquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-calquery.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-cardquery.php:  $expansion = $address_data_xml->GetElements();
+caldav-REPORT-cardquery.php:    $address_data_properties[strtoupper($v->GetAttribute('name'))] = true;
+caldav-REPORT-cardquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-query');
+caldav-REPORT-cardquery.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-cardquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/'.$proptype.'/*');
+caldav-REPORT-cardquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-cardquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-cardquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+caldav-REPORT-cardquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-cardquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-cardquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-cardquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+caldav-REPORT-cardquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/urn:ietf:params:xml:ns:carddav:filter/*');
+caldav-REPORT-cardquery.php:  dbg_error_log("cardquery","Applying filter for item '%s'", $item->dav_name );
+caldav-REPORT-cardquery.php:  $vcard = new vComponent( $item->caldav_data );
+caldav-REPORT-cardquery.php:  return $vcard->TestFilter($filters);
+caldav-REPORT-cardquery.php:    $tag = $v->GetTag();
+caldav-REPORT-cardquery.php:    dbg_error_log("cardquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:text-match':
+caldav-REPORT-cardquery.php:        $search = $v->GetContent();
+caldav-REPORT-cardquery.php:        $negate = $v->GetAttribute("negate-condition");
+caldav-REPORT-cardquery.php:        $collation = $v->GetAttribute("collation");
+caldav-REPORT-cardquery.php:          case 'i;ascii-casemap':
+caldav-REPORT-cardquery.php:          case 'i;unicode-casemap':
+caldav-REPORT-cardquery.php:        dbg_error_log("cardquery", " text-match: (%s%s %s '%s') ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:prop-filter':
+caldav-REPORT-cardquery.php:        $propertyname = $v->GetAttribute("name");
+caldav-REPORT-cardquery.php:            dbg_error_log("cardquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:param-filter':
+caldav-REPORT-cardquery.php:        return false; /** Figure out how to handle PARAM-FILTER conditions in the SQL */
+caldav-REPORT-cardquery.php:        $parameter = $v->GetAttribute("name");
+caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-cardquery.php:$target_collection = new DAVResource($request->path);
+caldav-REPORT-cardquery.php:$bound_from = $target_collection->bound_from();
+caldav-REPORT-cardquery.php:if ( !$target_collection->Exists() ) {
+caldav-REPORT-cardquery.php:  $request->DoResponse( 404 );
+caldav-REPORT-cardquery.php:if ( ! ($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection()) ) {
+caldav-REPORT-cardquery.php:  $request->DoResponse( 403, translate('The addressbook-query report must be run against an addressbook collection') );
+caldav-REPORT-cardquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+caldav-REPORT-cardquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+caldav-REPORT-cardquery.php:if ( $qry->Exec("cardquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-cardquery.php:  while( $address_object = $qry->Fetch() ) {
+caldav-REPORT-cardquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+caldav-REPORT-cardquery.php:        $address_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $address_object->dav_name);
+caldav-REPORT-cardquery.php:        $vcard = new VCard($address_object->caldav_data);
+caldav-REPORT-cardquery.php:        $vcard->MaskProperties($address_data_properties);
+caldav-REPORT-cardquery.php:        $address_object->caldav_data = $vcard->Render();
+caldav-REPORT-cardquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-cardquery.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-expand-property.php:  $propstat_set = $multistatus_response->GetElements('propstat');
+caldav-REPORT-expand-property.php:    $status = $v->GetElements('status');
+caldav-REPORT-expand-property.php:    if ( preg_match( '{^HTTP/\S+\s+200}', $status[0]->GetContent() ) ) {
+caldav-REPORT-expand-property.php:    $props = $propstat_200->GetElements('prop');
+caldav-REPORT-expand-property.php:      $properties = array_merge($properties,$p->GetElements());
+caldav-REPORT-expand-property.php://      dbg_error_log('REPORT',' get_href_containers: Checking property "%s" for hrefs.', $property->GetNSTag() );
+caldav-REPORT-expand-property.php:      $hrefs = $property->GetElements('href');
+caldav-REPORT-expand-property.php:      $pname = $property->GetAttribute('name');
+caldav-REPORT-expand-property.php:      $pns = $property->GetAttribute('namespace');
+caldav-REPORT-expand-property.php:      $subtrees[$pname] = $property->GetElements();
+caldav-REPORT-expand-property.php:    $part_response = $resource->RenderAsXML( $props, $reply );
+caldav-REPORT-expand-property.php:            $hrefs = $property->GetElements();
+caldav-REPORT-expand-property.php:            $pname = $property->GetTag();
+caldav-REPORT-expand-property.php:            $pns = $property->GetAttribute('xmlns');
+caldav-REPORT-expand-property.php:              $content = $v->GetContent();
+caldav-REPORT-expand-property.php:            $property->SetContent( expand_properties($paths, $subtrees[$pname], $reply, false) );
+caldav-REPORT-expand-property.php:$property_tree = $xmltree->GetPath('/DAV::expand-property/DAV::property');
+caldav-REPORT-expand-property.php:$multistatus = new XMLElement( "multistatus", expand_properties( $request->path, $property_tree, $reply), $reply->GetXmlNsArray() );
+caldav-REPORT-expand-property.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-freebusy.php: * Handle the FREE-BUSY-QUERY variant of REPORT
+caldav-REPORT-freebusy.php:include_once("freebusy-functions.php");
+caldav-REPORT-freebusy.php:$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
+caldav-REPORT-freebusy.php:$fbq_start = $fbq_content[0]->GetAttribute('start');
+caldav-REPORT-freebusy.php:$fbq_end   = $fbq_content[0]->GetAttribute('end');
+caldav-REPORT-freebusy.php:  $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+caldav-REPORT-freebusy.php:$freebusy = get_freebusy( '^'.$request->path.$request->DepthRegexTail(), $range_start, $range_end );
+caldav-REPORT-freebusy.php:$result->VCalendar();
+caldav-REPORT-freebusy.php:$result->AddComponent($freebusy);
+caldav-REPORT-freebusy.php:$request->DoResponse( 200, $result->Render(), 'text/calendar' );
+caldav-REPORT-multiget.php:* Handle the calendar-multiget REPORT request.
+caldav-REPORT-multiget.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+caldav-REPORT-multiget.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+caldav-REPORT-multiget.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+caldav-REPORT-multiget.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-multiget.php:    $qry_props = $xmltree->GetPath('/*/'.$proptype.'/*');
+caldav-REPORT-multiget.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-multiget.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-multiget.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-multiget.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-multiget.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-multiget.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-multiget.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-multiget.php:$collection = new DAVResource($request->path);
+caldav-REPORT-multiget.php:$bound_from = $collection->bound_from();
+caldav-REPORT-multiget.php:$mg_hrefs = $xmltree->GetPath('/*/DAV::href');
+caldav-REPORT-multiget.php:   * anything up to the matching request->path (which will include any http...) and then
+caldav-REPORT-multiget.php:  $href = $bound_from . preg_replace( "{^.*\E$request->path\Q}", '', rawurldecode($v->GetContent()) );
+caldav-REPORT-multiget.php://$where = " WHERE caldav_data.collection_id = " . $collection->resource_id();
+caldav-REPORT-multiget.php:  if ( $collection->Privileges() != privilege_to_bits('DAV::all') ) {
+caldav-REPORT-multiget.php:  if ( isset($c->hide_TODO) && $c->hide_TODO && ! $collection->Privileges() == privilege_to_bits('all') ) {
+caldav-REPORT-multiget.php:    $filtro = "uid=".$request->username;
+caldav-REPORT-multiget.php:    if ( $qryp->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qryp->rows() > 0 ) {
+caldav-REPORT-multiget.php:       while( $part = $qryp->Fetch() ) {
+caldav-REPORT-multiget.php:           $sql = "SELECT * FROM phpgw_cal WHERE cal_id = $part->cal_id AND cal_type = 'E'";
+caldav-REPORT-multiget.php:           if ( $qry->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-multiget.php:              while( $calendar_object = $qry->Fetch() ) {
+caldav-REPORT-multiget.php:                if ( $bound_from != $collection->dav_name() ) {
+caldav-REPORT-multiget.php:                      $calendar_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $calendar_object->dav_name);
+caldav-REPORT-multiget.php:                   $vResource = new vComponent($calendar_object->caldav_data);
+caldav-REPORT-multiget.php:                   $calendar_object->caldav_data = $expanded->Render();
+caldav-REPORT-multiget.php:            $sql = "SELECT * FROM phpgw_cal INNER JOIN phpgw_cal_repeats USING(cal_id) WHERE cal_id = $part->cal_id";
+caldav-REPORT-multiget.php:            if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-multiget.php:                  while( $calendar_object = $qry->Fetch() ) {
+caldav-REPORT-multiget.php:                    if ( $bound_from != $collection->dav_name() ) {
+caldav-REPORT-multiget.php:                      $calendar_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $calendar_object->dav_name);
+caldav-REPORT-multiget.php:                       $vResource = new vComponent($calendar_object->caldav_data);
+caldav-REPORT-multiget.php:                       $calendar_object->caldav_data = $expanded->Render();
+caldav-REPORT-multiget.php://if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $where .= " ORDER BY caldav_data.dav_id";
+caldav-REPORT-multiget.php://if ( $qry->Exec('REPORT',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-multiget.php://  while( $dav_object = $qry->Fetch() ) {
+caldav-REPORT-multiget.php://    if ( $bound_from != $collection->dav_name() ) {
+caldav-REPORT-multiget.php://      $dav_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $dav_object->dav_name);
+caldav-REPORT-multiget.php://      $vResource = new vComponent($dav_object->caldav_data);
+caldav-REPORT-multiget.php://      $dav_object->caldav_data = $expanded->Render();
+caldav-REPORT-multiget.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-multiget.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT.php:* CalDAV Server - handle REPORT method
+caldav-REPORT.php:require_once('RRule-v2.php');
+caldav-REPORT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
+caldav-REPORT.php:    fwrite($fh,$request->raw_post);
+caldav-REPORT.php:if ( !isset($request->xml_tags) ) {
+caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body contains no XML data!") );
+caldav-REPORT.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+caldav-REPORT.php:$target = new DAVResource($request->path);
+caldav-REPORT.php:if ( $xmltree->GetTag() != 'DAV::principal-property-search'
+caldav-REPORT.php:                && $xmltree->GetTag() != 'DAV::principal-property-search-set' ) {
+caldav-REPORT.php:  //$target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
+caldav-REPORT.php:$reportnum = -1;
+caldav-REPORT.php:if ( $xmltree->GetTag() == 'urn:ietf:params:xml:ns:caldav:free-busy-query' ) {
+caldav-REPORT.php:  include("caldav-REPORT-freebusy.php");
+caldav-REPORT.php:switch( $xmltree->GetTag() ) {
+caldav-REPORT.php:  case 'DAV::principal-property-search':
+caldav-REPORT.php:    include("caldav-REPORT-principal.php");
+caldav-REPORT.php:  case 'DAV::principal-search-property-set':
+caldav-REPORT.php:    include("caldav-REPORT-pps-set.php");
+caldav-REPORT.php:  case 'DAV::sync-collection':
+caldav-REPORT.php:    include("caldav-REPORT-sync-collection.php");
+caldav-REPORT.php:  case 'DAV::expand-property':
+caldav-REPORT.php:    include("caldav-REPORT-expand-property.php");
+caldav-REPORT.php:// dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+caldav-REPORT.php://  $caldav_data = $item->caldav_data;
+caldav-REPORT.php://  $displayname = $item->summary;
+caldav-REPORT.php://  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+caldav-REPORT.php://    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+caldav-REPORT.php://      if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+caldav-REPORT.php://        $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-REPORT.php://        $confidential->SetType($first->GetType());
+caldav-REPORT.php://        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-REPORT.php://        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-REPORT.php://        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+caldav-REPORT.php://        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+caldav-REPORT.php://        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+caldav-REPORT.php://        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+caldav-REPORT.php://        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+caldav-REPORT.php://        $ical->SetComponents(array($confidential),$confidential->GetType());
+caldav-REPORT.php://        $caldav_data = $ical->Render();
+caldav-REPORT.php://  $url = ConstructURL($item->dav_name);
+caldav-REPORT.php://        $prop->NewElement($k, $contentlength );
+caldav-REPORT.php://      case 'calendar-data':
+caldav-REPORT.php://        $reply->CalDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php://        $prop->NewElement($k, "text/calendar" );
+caldav-REPORT.php://      case 'current-user-principal':
+caldav-REPORT.php://        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+caldav-REPORT.php://        $prop->NewElement($k, $displayname );
+caldav-REPORT.php://        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+caldav-REPORT.php://        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+caldav-REPORT.php://      case '"current-user-privilege-set"':
+caldav-REPORT.php://        $prop->NewElement($k, privileges($request->permissions) );
+caldav-REPORT.php://      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+caldav-REPORT.php://      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:        $filtro = "uid=".$request->username;
+caldav-REPORT.php:                 $request->DoResponse( 501, 'Database error');
+caldav-REPORT.php:        dbg_error_log("REPORT","Building XML Response for item '%s' usuario  '%s'", $item->cal_id,$nome );
+caldav-REPORT.php:        $alarme_id = $item->cal_id;
+caldav-REPORT.php:        if ( $alarme->Exec("REPORT",__LINE__,__FILE__) && $alarme->rows() > 0 ){          
+caldav-REPORT.php:                $nao_alarme = new AwlQuery( "SELECT * FROM phpgw_cal_extra WHERE  cal_id = :alarme_id AND (cal_extra_name = 'X-MOZ-LASTACK' OR cal_extra_name = :X-MOZ-LASTACK-PART)",array( ':alarme_id' => '$alarme_id', ':X-MOZ-LASTACK-PART' => 'X-MOZ-LASTACK-PART-$nome'));
+caldav-REPORT.php:                if ( $nao_alarme->Exec("REPORT",__LINE__,__FILE__) && $nao_alarme->rows() > 0 ){                                                                                               
+caldav-REPORT.php:                        $alarmeqryno = $nao_alarme->Fetch();                                                                                                                                   
+caldav-REPORT.php:                        $moz = "\r\nX-MOZ-LASTACK:$alarmeqryno->cal_extra_value";                                                                                                              
+caldav-REPORT.php:                        $adiamento = new AwlQuery( "SELECT * FROM phpgw_cal_extra WHERE  cal_id = :alarme_id AND cal_extra_name LIKE 'X-MOZ-SNOOZE%'",  array( ':alarme_id' => '$alarme_id'));                                       
+caldav-REPORT.php:                        if ( $adiamento->Exec("REPORT",__LINE__,__FILE__) && $adiamento->rows() > 0 ){                                                                                         
+caldav-REPORT.php:                             $admno = $adiamento->Fetch();                                                                                                                                     
+caldav-REPORT.php:                            $moz .= "\r\n$admno->cal_extra_name:$admno->cal_extra_value";                                                                                                      
+caldav-REPORT.php:                        //$deleta->Exec("calquery",__LINE__,__FILE__);                                                                                                                         
+caldav-REPORT.php:                $alarmeqry = $alarme->Fetch();
+caldav-REPORT.php:                $tempo = split(";", $alarmeqry->data, 4);
+caldav-REPORT.php:                $tempoalarme = $alarmeqry->next;// + $mais;
+caldav-REPORT.php:                $agora  = $agoranow->format("U");          
+caldav-REPORT.php:                if ( $tempoalarme >= $agora | $item->cal_type == 'M'){
+caldav-REPORT.php:                                $VALARME = "\r\nBEGIN:VALARM\r\nTRIGGER;VALUE=DURATION:-PT${minutos}M\r\nDESCRIPTION:Mozilla Alarm: alarme\r\nACTION:DISPLAY\r\nEND:VALARM";
+caldav-REPORT.php:                                $VALARME = "\r\nBEGIN:VALARM\r\nTRIGGER;VALUE=DURATION:-PT${hora}H\r\nDESCRIPTION:Mozilla Alarm: alarme\r\nACTION:DISPLAY\r\nEND:VALARM";   
+caldav-REPORT.php:                                $VALARME = "\r\nBEGIN:VALARM\r\nTRIGGER;VALUE=DURATION:-P1W\r\nDESCRIPTION:Mozilla Alarm: alarme\r\nACTION:DISPLAY\r\nEND:VALARM";          
+caldav-REPORT.php:                                $VALARME = "\r\nBEGIN:VALARM\r\nTRIGGER;VALUE=DURATION:-P${dia}D\r\nDESCRIPTION:Mozilla Alarm: alarme\r\nACTION:DISPLAY\r\nEND:VALARM";     
+caldav-REPORT.php:        ///$create_data = dataSegundosParaT($item->datetime);               
+caldav-REPORT.php:        //if (strlen($item->category)>2){
+caldav-REPORT.php:        if ($item->category != "" ){     
+caldav-REPORT.php:                $phpgw_query = new AwlQuery ($x,  array(':category' => '$item->category'));                                           
+caldav-REPORT.php:                if ( $phpgw_query->Exec() && $phpgw_query->rows() > 0 ) {                    
+caldav-REPORT.php:                        while( $id_cale = $phpgw_query->Fetch() ) {                        
+caldav-REPORT.php:                                $categories = utf8_encode($id_cale->cat_name);             
+caldav-REPORT.php:                               //$categories = $id_cale->cat_name;                         
+caldav-REPORT.php:        $displayname = $item->title;                                                       
+caldav-REPORT.php:        $querypart = new AwlQuery( $part2 , array(':cal_id' => '$item->cal_id'));                                                                         
+caldav-REPORT.php:        if ( $querypart->Exec("REPORT",__LINE__,__FILE__) && $querypart->rows() > 0 ) {                             
+caldav-REPORT.php:                while( $id_cale = $querypart->Fetch() ) {                                                           
+caldav-REPORT.php:                        if ($item->owner != $id_cale->cal_login){                                                   
+caldav-REPORT.php:                                $filtro = "uidNumber=$id_cale->cal_login" ;                                         
+caldav-REPORT.php:                                                switch ($id_cale->cal_status){                                      
+caldav-REPORT.php:                                                                $estado[$i] = "NEEDS-ACTION";                       
+caldav-REPORT.php:        if ( isset($item->ex_participants))
+caldav-REPORT.php:                $mails = split("[?,]",$item->ex_participants);
+caldav-REPORT.php:                        $members = $members."\nATTENDEE;RSVP=TRUE;CN=$cn[$i];PARTSTAT=$estado[$i];ROLE=REQ-PARTICIPANT:MAILTO:$mail[$i]\r";
+caldav-REPORT.php:                        $members = $members."\nATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:MAILTO:$mails[$i]\r";         
+caldav-REPORT.php:        $mdate = $item->mdatetime;
+caldav-REPORT.php:        $calid = $item->cal_id;   
+caldav-REPORT.php:        $date = $item->datetime;                 
+caldav-REPORT.php:        $edate = $item->edatetime;               
+caldav-REPORT.php:        $titulo = utf8_encode($item->title);     
+caldav-REPORT.php:        $local = utf8_encode($item->location);   
+caldav-REPORT.php:        $descricao = utf8_encode($item->description);
+caldav-REPORT.php:        $convite = new AwlQuery( "SELECT cal_extra_value FROM phpgw_cal_extra WHERE cal_id = :cal_id", array(':cal_id' => '$item->cal_id'));
+caldav-REPORT.php:        if ( $convite->Exec("REPORT",__LINE__,__FILE__) && $convite->rows() > 0 ) {                           
+caldav-REPORT.php:                $xmozconvite = $convite->Fetch();                                                             
+caldav-REPORT.php:                if ($xmozconvite->cal_extra_value == "TRUE"){                                                 
+caldav-REPORT.php:                         $envia_convite = "X-MOZ-SEND-INVITATIONS:TRUE";                                      
+caldav-REPORT.php:                            $convite = new AwlQuery( "DELETE FROM phpgw_cal_extra WHERE cal_id = :cal_id", array(':cal_id' => '$item->cal_id'));
+caldav-REPORT.php:                            $convite->Exec("REPORT",__LINE__,__FILE__);                                         
+caldav-REPORT.php:                            $envia_convite = "X-MOZ-SEND-INVITATIONS:FALSE";                                      
+caldav-REPORT.php:        if ( $item->is_public == 0 ){
+caldav-REPORT.php:        if ( $item->cal_type == 'M' ){
+caldav-REPORT.php:                if ( $item->recur_exception != ''){                                                               
+caldav-REPORT.php:                        $listadeexcecao = explode("," , $item->recur_exception);                                  
+caldav-REPORT.php:                $phpgw_query = new AwlQuery ($x, array(':cal_id' => '$item->cal_id'));                                                                  
+caldav-REPORT.php:                if ( $phpgw_query->Exec() && $phpgw_query->rows() > 0 ) {                                           
+caldav-REPORT.php:                        while( $id_cale = $phpgw_query->Fetch() ) {                                               
+caldav-REPORT.php:                                $data_antiga = date("Ymd\THis",$item->datetime);                                  
+caldav-REPORT.php:                                $data_nova = date("Ymd\THis",$id_cale->datetime);                                 
+caldav-REPORT.php:                                if ( $data_n->getOffset() > $data_a->getOffset() )                                
+caldav-REPORT.php:                                        $dias = $data_n->format('z') - $data_a->format('z');                      
+caldav-REPORT.php:                                        $dias = $item->datetime + ($dias*86400) - 3600;                           
+caldav-REPORT.php:                                elseif ( $data_n->getOffset() < $data_a->getOffset() )                            
+caldav-REPORT.php:                                        $dias = $data_n->format('z') - $data_a->format('z');                      
+caldav-REPORT.php:                                        $dias = $item->datetime + ($dias*86400) + 3600;                           
+caldav-REPORT.php:                                        $dias = $data_n->format('z') - $data_a->format('z');
+caldav-REPORT.php:                                        $dias = $item->datetime + ($dias*86400);            
+caldav-REPORT.php:                if (  $item->recur_enddate != 0){
+caldav-REPORT.php:                        $until = dataSegundosParaT($item->recur_enddate,1);
+caldav-REPORT.php:                        $until = dataSegundosParaT($item->recur_enddate + 86400,1);
+caldav-REPORT.php:                $intervalo = $item->recur_interval;
+caldav-REPORT.php:                switch($item->recur_type){         
+caldav-REPORT.php:                                $dia_sem = converte_semana($item->recur_data);
+caldav-REPORT.php:                                if($item->recur_data != ""){                  
+caldav-REPORT.php:        $response = "BEGIN:VCALENDAR\r\nPRODID:-//davical.org//NONSGML AWL Calendar//EN\r\nVERSION:2.9.9";
+caldav-REPORT.php:        $response .= "\r\nBEGIN:VEVENT\r\nCREATED:$create_data\r\nLAST-MODIFIED:$modify_data\r\nDTSTAMP:$create_data\r\nUID:$item->cal_id@$item->owner\r\n${members}\r\nSUMMARY:$titulo\r\n$recurrence_id\r\nDTSTART;TZID=America/Sao_Paulo:$start_data\r\nDTEND;TZID=America/Sao_Paulo:$end_data\r\nLOCATION:$local\r\nCATEGORIES:$categories\r\nDESCRIPTION:".preg_replace('/\r\n/', '\\n', $descricao)."\r\nPRIORITY:$item->priority\r\nRRULE:$Rrule${exe}${moz}\r\nCLASS:$classe$VALARME\r\nTRANSP:TRANSPARENT\r\n$envia_convite\r\nEND:VEVENT\r\n";                                                                                                                                                                               
+caldav-REPORT.php:        $dav_nam = "{$item->cal_id}@{$item->owner}";
+caldav-REPORT.php:        $dav_etag_nam="{md5($item->cal_id.$item->owner.$item->lastmodified)}";
+caldav-REPORT.php:        $dav_name = "/{$session->username}/{$item->cal_id}@{$item->owner}.ics";
+caldav-REPORT.php:      				         $prop->NewElement($k, $contentlength );
+caldav-REPORT.php:     				 case 'calendar-data':
+caldav-REPORT.php:       					 $reply->CalDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:        				 $prop->NewElement($k, "text/calendar" );
+caldav-REPORT.php:     				 case 'current-user-principal':
+caldav-REPORT.php:       					 $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+caldav-REPORT.php:       					 $prop->NewElement($k, $displayname );
+caldav-REPORT.php:       					 $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+caldav-REPORT.php:       					 $prop->NewElement($k, "$dav_nam" );
+caldav-REPORT.php:     				 case '"current-user-privilege-set"':
+caldav-REPORT.php:       					 $prop->NewElement($k, privileges($request->permissions) );
+caldav-REPORT.php:     				 case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+caldav-REPORT.php:                $noprop->NewElement( strtolower($v) );                        
+caldav-REPORT.php:  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+caldav-REPORT.php:  $caldav_data = $item->caldav_data;
+caldav-REPORT.php:  $displayname = preg_replace( '{^.*/}', '', $item->dav_name );
+caldav-REPORT.php:  switch( $item->caldav_type ) {
+caldav-REPORT.php:      $displayname = $item->summary;
+caldav-REPORT.php:      $displayname = $item->fn;
+caldav-REPORT.php:      $contenttype = 'text/x-vcard';
+caldav-REPORT.php:  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+caldav-REPORT.php:    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+caldav-REPORT.php:      if ( $type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+caldav-REPORT.php:        $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-REPORT.php:        $confidential->SetType($first->GetType());
+caldav-REPORT.php:        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-REPORT.php:        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+caldav-REPORT.php:        $ical->SetComponents(array($confidential),$confidential->GetType());
+caldav-REPORT.php:        $caldav_data = $ical->Render();
+caldav-REPORT.php:  $url = ConstructURL($item->dav_name);
+caldav-REPORT.php:        $prop->NewElement($k, $contentlength );
+caldav-REPORT.php:      case 'calendar-data':
+caldav-REPORT.php:        if ( $type == 'calendar' ) $reply->CalDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:      case 'address-data':
+caldav-REPORT.php:        if ( $type == 'vcard' ) $reply->CardDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:        $prop->NewElement($k, $contenttype );
+caldav-REPORT.php:      case 'current-user-principal':
+caldav-REPORT.php:        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+caldav-REPORT.php:        $prop->NewElement($k, $displayname );
+caldav-REPORT.php:        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+caldav-REPORT.php:        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+caldav-REPORT.php:      case '"current-user-privilege-set"':
+caldav-REPORT.php:        $prop->NewElement($k, privileges($request->permissions) );
+caldav-REPORT.php:      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
+caldav-REPORT.php:  $calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
+caldav-REPORT.php:  include("caldav-REPORT-calquery.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
+caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
+caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-multiget" ) {
+caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
+caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-query" ) {
+caldav-REPORT.php:  $cardquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:carddav:addressbook-query/*");
+caldav-REPORT.php:  include("caldav-REPORT-cardquery.php");
+caldav-REPORT.php:  $request->PreconditionFailed( 403, 'DAV::supported-report', sprintf( '"%s" is not a supported report type') );
+caldav-REPORT.php:        $nova = substr($retorna, 0, -1);
+caldav-REPORT.php:        substr("$datatz", 0,4)) + $HorarioTimeZone->getoffset($HorarioTime);//ano
+caldav-REPORT.php:        //$teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+caldav-REPORT.php:        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+caldav-REPORT-pps-set.php:* DAViCal CalDAV Server - handle principal-search-property-set report (RFC3744)
+caldav-REPORT-pps-set.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-REPORT-pps-set.php:  $xmldoc->NSElement($prop, $property );
+caldav-REPORT-pps-set.php:  return new XMLElement( 'principal-search-property', $prop );
+caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-home-set',
+caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-user-address-set'
+caldav-REPORT-pps-set.php:$report = new XMLElement( 'principal-search-property-set', $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-pps-set.php:$request->XMLResponse( 207, $report );
+caldav-REPORT-principal.php:$searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search');
+caldav-REPORT-principal.php:$CS_search_test = $xmltree->GetAttribute('test');
+caldav-REPORT-principal.php:  $qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*');  // There may be many
+caldav-REPORT-principal.php:  $match     = $search->GetPath('/DAV::property-search/DAV::match');   // There may only be one
+caldav-REPORT-principal.php:  $match = $match[0]->GetContent();
+caldav-REPORT-principal.php:    switch( $v1->GetTag() ) {
+caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+caldav-REPORT-principal.php:        * @todo We should handle a lot more properties here.  principal-URL seems a likely one to be used.
+caldav-REPORT-principal.php:        dbg_error_log("principal", "Unhandled tag '%s' to match '%s'\n", $v1->GetTag(), $match );
+caldav-REPORT-principal.php:$get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*');
+caldav-REPORT-principal.php:  $properties[] = $v1->GetTag();
+caldav-REPORT-principal.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-principal.php:  while( $row = $qry->Fetch() ) {
+caldav-REPORT-principal.php:    $responses[] = $principal->RenderAsXML( $properties, $reply );
+caldav-REPORT-principal.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-principal.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-sync-collection.php:* CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
+caldav-REPORT-sync-collection.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-REPORT-sync-collection.php:$sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
+caldav-REPORT-sync-collection.php:$sync_token = $sync_tokens[0]->GetContent();
+caldav-REPORT-sync-collection.php:dbg_error_log( 'sync', " sync-token: %s", $sync_token );
+caldav-REPORT-sync-collection.php:$props = $xmltree->GetElements('DAV::prop');
+caldav-REPORT-sync-collection.php:$props = $v->GetContent();
+caldav-REPORT-sync-collection.php:  $proplist[] = $v->GetTag();
+caldav-REPORT-sync-collection.php:$collection = new DAVResource( $request->path );
+caldav-REPORT-sync-collection.php:$params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
+caldav-REPORT-sync-collection.php:if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
+caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:$row = $qry->Fetch();
+caldav-REPORT-sync-collection.php:if ( !isset($row->new_sync_token) ) {
+caldav-REPORT-sync-collection.php:  if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
+caldav-REPORT-sync-collection.php:    $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:  $row = $qry->Fetch();
+caldav-REPORT-sync-collection.php:$new_token = $row->new_sync_token;
+caldav-REPORT-sync-collection.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
+caldav-REPORT-sync-collection.php:  while( $object = $qry->Fetch() ) {
+caldav-REPORT-sync-collection.php:    if ( $object->dav_name == $last_dav_name ) {
+caldav-REPORT-sync-collection.php:      if ( $object->sync_status == 404 ) {
+caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+caldav-REPORT-sync-collection.php:      else if ( $object->sync_status == 201 && $first_status == 404 ) {
+caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+caldav-REPORT-sync-collection.php:        new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:        new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:      if ( $object->sync_status != 404 ) {
+caldav-REPORT-sync-collection.php:        $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) );
+caldav-REPORT-sync-collection.php:      if ( isset($c->use_old_sync_response_tag) && $c->use_old_sync_response_tag ) $response_tag = 'sync-response';
+caldav-REPORT-sync-collection.php:      $first_status = $object->sync_status;
+caldav-REPORT-sync-collection.php:      $last_dav_name  = $object->dav_name;
+caldav-REPORT-sync-collection.php:  $responses[] = new XMLElement( 'sync-token', $new_token );
+caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-sync-collection.php:$request->XMLResponse( 207, $multistatus );
+CalDAVRequest.php:*  - Ascertaining the paths
+CalDAVRequest.php:*  - Ascertaining the current user's permission to those paths.
+CalDAVRequest.php:*  - Utility functions which we can use to decide whether this
+CalDAVRequest.php:  * The 'current_user_principal_xml' the DAV:current-user-principal answer. An
+CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+CalDAVRequest.php:    $this->supported_privileges = array(
+CalDAVRequest.php:          'write-content' => translate('Write content'),
+CalDAVRequest.php:          'write-properties' => translate('Write properties')
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:read-free-busy' => translate('Read the free/busy information for a calendar collection'),
+CalDAVRequest.php:        'read-acl' => translate('Read ACLs for a resource or collection'),
+CalDAVRequest.php:        'read-current-user-privilege-set' => translate('Read the details of the current user\'s access control to this resource.'),
+CalDAVRequest.php:        'write-acl' => translate('Write ACLs for a resource or collection'),
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-deliver' => array(
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite'=> translate('Deliver scheduling invitations from an organiser to this scheduling inbox'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply' => translate('Deliver scheduling replies from an attendee to this scheduling inbox'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy' => translate('Allow free/busy enquiries targeted at the owner of this scheduling inbox')
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-send' => array(
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-invite' => translate('Send scheduling invitations as an organiser from the owner of this scheduling outbox.'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-reply' => translate('Send scheduling replies as an attendee from the owner of this scheduling outbox.'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy' => translate('Send free/busy enquiries')
+CalDAVRequest.php:    $this->options = $options;
+CalDAVRequest.php:    if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false;
+CalDAVRequest.php:    $this->principal = (object) array( 'username' => $session->username, 'user_no' => $session->user_no );
+CalDAVRequest.php:    $this->raw_post = file_get_contents ( 'php://input');
+CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['request']) && $c->dbg['request']) ) {
+CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s: %s", $k, $v );
+CalDAVRequest.php:      $lines = preg_split( '#[\r\n]+#', $this->raw_post);
+CalDAVRequest.php:        dbg_error_log( "LOG request", "-->%s", $v );
+CalDAVRequest.php:    $this->method = $_SERVER['REQUEST_METHOD'];
+CalDAVRequest.php:      $this->content_type = (isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null);
+CalDAVRequest.php:      if ( preg_match( '{^(\S+/\S+)\s*(;.*)?$}', $this->content_type, $matches ) ) {
+CalDAVRequest.php:        $this->content_type = $matches[1];
+CalDAVRequest.php:      if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' ) {
+CalDAVRequest.php:        if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
+CalDAVRequest.php:          dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
+CalDAVRequest.php:                                                 $request->method, $this->content_type );
+CalDAVRequest.php:          $this->content_type = 'text/xml';
+CalDAVRequest.php:    $this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
+CalDAVRequest.php:      $this->depth = $_SERVER['HTTP_DEPTH'];
+CalDAVRequest.php:      * is not present then a reasonable request-type-dependent default should be
+CalDAVRequest.php:      switch( $this->method ) {
+CalDAVRequest.php:          $this->depth = 'infinity';
+CalDAVRequest.php:          $this->depth = 0;
+CalDAVRequest.php:    if ( $this->depth == 'infinity' ) $this->depth = DEPTH_INFINITY;
+CalDAVRequest.php:    $this->depth = intval($this->depth);
+CalDAVRequest.php:    if ( isset($_SERVER['HTTP_DESTINATION']) ) $this->destination = $_SERVER['HTTP_DESTINATION'];
+CalDAVRequest.php:    $this->overwrite = ( isset($_SERVER['HTTP_OVERWRITE']) && ($_SERVER['HTTP_OVERWRITE'] == 'F') ? false : true ); // RFC4918, 9.8.4 says default True.
+CalDAVRequest.php:    * LOCK things use an "If" header to hold the lock in some cases, and "Lock-token" in others
+CalDAVRequest.php:    if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
+CalDAVRequest.php:      $this->lock_token = $matches[1];
+CalDAVRequest.php:      $this->ticket = new DAVTicket($_GET['ticket']);
+CalDAVRequest.php:      $this->ticket = new DAVTicket($_SERVER['HTTP_TICKET']);
+CalDAVRequest.php:          $this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
+CalDAVRequest.php:        elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
+CalDAVRequest.php:          $this->timeout = min( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
+CalDAVRequest.php:      if ( ! isset($this->timeout) || $this->timeout == 0 ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
+CalDAVRequest.php:    $this->path = (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : "/");
+CalDAVRequest.php:    $this->path = rawurldecode($this->path);
+CalDAVRequest.php:    if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) {
+CalDAVRequest.php:      $this->path = $matches[1]. '/';
+CalDAVRequest.php:    // dbg_error_log( "caldav", "Sanitising path '%s'", $this->path );
+CalDAVRequest.php:    if ( preg_match( $bad_chars_regex, $this->path ) ) {
+CalDAVRequest.php:      $this->DoResponse( 400, translate("The calendar path contains illegal characters.") );
+CalDAVRequest.php:    if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path);
+CalDAVRequest.php:    $this->user_no = $session->user_no;
+CalDAVRequest.php:    $this->username = $session->username;
+CalDAVRequest.php:    if ( $session->user_no > 0 ) {
+CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('href', ConstructURL('/'.$session->username.'/') );
+CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('unauthenticated' );
+CalDAVRequest.php:    * we SHOULD return a Content-location header with the correction...
+CalDAVRequest.php:    *  - The exact request.
+CalDAVRequest.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+CalDAVRequest.php:    *  - The request URL truncated to the last '/'
+CalDAVRequest.php:    $params = array( ':exact_name' => $this->path );
+CalDAVRequest.php:    if ( !preg_match( '#/$#', $this->path ) ) {
+CalDAVRequest.php:      $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $this->path);
+CalDAVRequest.php:      $params[':trailing_slash_name'] = $this->path."/";
+CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+CalDAVRequest.php:      if ( $row->dav_name == $this->path."/" ) {
+CalDAVRequest.php:        $this->path = $row->dav_name;
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:      $this->collection_id = $row->collection_id;
+CalDAVRequest.php:      $this->collection_path = $row->dav_name;
+CalDAVRequest.php:      $this->collection_type = ($row->is_calendar == 't' ? 'calendar' : 'collection');
+CalDAVRequest.php:      $this->collection = $row;
+CalDAVRequest.php:      if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
+CalDAVRequest.php:        $this->collection_type = 'schedule-'. $matches[3]. 'box';
+CalDAVRequest.php:      $this->collection->type = $this->collection_type;
+CalDAVRequest.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->path, $matches ) ) {
+CalDAVRequest.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+CalDAVRequest.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+CalDAVRequest.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+CalDAVRequest.php:        $this->collection_id = $row->collection_id;
+CalDAVRequest.php:        $this->collection_path = $matches[1];
+CalDAVRequest.php:        $this->collection = $row;
+CalDAVRequest.php:        $this->collection->type = $this->collection_type;
+CalDAVRequest.php:    else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) {
+CalDAVRequest.php:      $this->collection_type = 'proxy';
+CalDAVRequest.php:      $this->_is_proxy_request = true;
+CalDAVRequest.php:      $this->proxy_type = $matches[3];
+CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+CalDAVRequest.php:        $this->path .= '/';
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a (proxy) collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:    else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_type = 'email';
+CalDAVRequest.php:      $this->collection_path = $this->path;
+CalDAVRequest.php:      $this->_is_principal = true;
+CalDAVRequest.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+CalDAVRequest.php:      $this->collection_type = 'principal';
+CalDAVRequest.php:      $this->_is_principal = true;
+CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+CalDAVRequest.php:        $this->path .= '/';
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:        $this->depth = 0;
+CalDAVRequest.php:    else if ( $this->path == '/' ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_path = '/';
+CalDAVRequest.php:      $this->collection_type = 'root';
+CalDAVRequest.php:    if ( $this->collection_path == $this->path ) $this->_is_collection = true;
+CalDAVRequest.php:    dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
+CalDAVRequest.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
+CalDAVRequest.php:    if ( isset($this->principal->user_no) ) $this->user_no  = $this->principal->user_no;
+CalDAVRequest.php:    if ( isset($this->principal->username)) $this->username = $this->principal->username;
+CalDAVRequest.php:    if ( isset($this->principal->by_email) && $this->principal->by_email) $this->by_email = true;
+CalDAVRequest.php:    if ( isset($this->principal->principal_id)) $this->principal_id = $this->principal->principal_id;
+CalDAVRequest.php:    if ( $this->collection_type == 'principal' || $this->collection_type == 'email' || $this->collection_type == 'proxy' ) {
+CalDAVRequest.php:      $this->collection = $this->principal->AsCollection();
+CalDAVRequest.php:      if( $this->collection_type == 'proxy' ) {
+CalDAVRequest.php:        $this->collection = $this->principal->AsCollection();
+CalDAVRequest.php:        $this->collection->is_proxy = 't';
+CalDAVRequest.php:        $this->collection->type = 'proxy';
+CalDAVRequest.php:        $this->collection->proxy_type = $this->proxy_type;
+CalDAVRequest.php:        $this->collection->dav_displayname = sprintf('Proxy %s for %s', $this->proxy_type, $this->principal->username() );
+CalDAVRequest.php:    elseif( $this->collection_type == 'root' ) {
+CalDAVRequest.php:      $this->collection = (object) array(
+CalDAVRequest.php:                            'dav_etag' => md5($c->system_name),
+CalDAVRequest.php:                            'dav_displayname' => $c->system_name,
+CalDAVRequest.php:    $this->setPermissions();
+CalDAVRequest.php:    $this->supported_methods = array(
+CalDAVRequest.php:    if ( $this->IsCollection() ) {
+CalDAVRequest.php:      switch ( $this->collection_type ) {
+CalDAVRequest.php:          $this->supported_methods = array(
+CalDAVRequest.php:        case 'schedule-inbox':
+CalDAVRequest.php:        case 'schedule-outbox':
+CalDAVRequest.php:          $this->supported_methods = array_merge(
+CalDAVRequest.php:            $this->supported_methods,
+CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+CalDAVRequest.php:          $this->supported_methods['MKCOL'] = '';
+CalDAVRequest.php:          $this->supported_methods['MKCALENDAR'] = '';
+CalDAVRequest.php:          $this->supported_methods['PROPPATCH'] = '';
+CalDAVRequest.php:          $this->supported_methods['BIND'] = '';
+CalDAVRequest.php:      $this->supported_methods = array_merge(
+CalDAVRequest.php:        $this->supported_methods,
+CalDAVRequest.php:    /**** comentado por motivos de incmpatibilidade do sync-collection
+CalDAVRequest.php:    $this->supported_reports = array(
+CalDAVRequest.php:      'DAV::principal-property-search' => '',
+CalDAVRequest.php:      'DAV::expand-property' => '',
+CalDAVRequest.php:      'DAV::sync-collection' => ''
+CalDAVRequest.php:    $this->supported_reports = array(
+CalDAVRequest.php:      'DAV::principal-property-search' => '',
+CalDAVRequest.php:      'DAV::expand-property' => ''
+CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_calendar ) {
+CalDAVRequest.php:      $this->supported_reports = array_merge(
+CalDAVRequest.php:        $this->supported_reports,
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_addressbook ) {
+CalDAVRequest.php:      $this->supported_reports = array_merge(
+CalDAVRequest.php:        $this->supported_reports,
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+CalDAVRequest.php:    if ( isset($this->content_type) && preg_match( '#(application|text)/xml#', $this->content_type ) ) {
+CalDAVRequest.php:      $xml_parser = xml_parser_create_ns('UTF-8');
+CalDAVRequest.php:      $this->xml_tags = array();
+CalDAVRequest.php:      $rc = xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
+CalDAVRequest.php:        $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml'), array( 'xmlns' => 'DAV:') ) );
+CalDAVRequest.php:      if ( count($this->xml_tags) ) {
+CalDAVRequest.php:        $this->xml_tags = null;
+CalDAVRequest.php:        dbg_error_log( "ERROR", "Incoming request sent content-type XML with no XML request body." );
+CalDAVRequest.php:    * Look out for If-None-Match or If-Match headers
+CalDAVRequest.php:      $this->etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
+CalDAVRequest.php:      if ( $this->etag_none_match == '' ) unset($this->etag_none_match);
+CalDAVRequest.php:      $this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
+CalDAVRequest.php:      if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
+CalDAVRequest.php:    $this->user_no = $session->user_no;
+CalDAVRequest.php:    $this->username = $session->username;
+CalDAVRequest.php:    $this->principal_id = $session->principal_id;
+CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+CalDAVRequest.php:    $path_split = explode('/', $this->path );
+CalDAVRequest.php:    $this->username = $path_split[1];
+CalDAVRequest.php:    if ( $this->username == 'principals' ) $this->username = $path_split[3];
+CalDAVRequest.php:    if ( isset($this->options['allow_by_email']) && preg_match( '#/(\S+@\S+[.]\S+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:      $this->by_email = $matches[1];
+CalDAVRequest.php:                          array(':email' => $this->by_email ) );
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $user = $qry->Fetch() ) {
+CalDAVRequest.php:        $this->user_no = $user->user_no;
+CalDAVRequest.php:        $this->username = $user->username;
+CalDAVRequest.php:        $this->principal_id = $user->principal_id;
+CalDAVRequest.php:    elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)) {
+CalDAVRequest.php:      $this->principal = $user;
+CalDAVRequest.php:      $this->user_no = $user->user_no;
+CalDAVRequest.php:      $this->principal_id = $user->principal_id;
+CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+CalDAVRequest.php:      $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+CalDAVRequest.php:    else if ( $session->AllowedTo("Admin") || $session->user_no == $this->user_no ) {
+CalDAVRequest.php:      $this->privileges = privilege_to_bits('all');
+CalDAVRequest.php:      dbg_error_log( "caldav", "Full permissions for %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator") );
+CalDAVRequest.php:      $this->privileges = 0;
+CalDAVRequest.php:      if ( $this->IsPublic() ) {
+CalDAVRequest.php:        $this->privileges = privilege_to_bits(array('read','read-free-busy'));
+CalDAVRequest.php:      else if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
+CalDAVRequest.php:        $this->privileges = privilege_to_bits('read-free-busy');
+CalDAVRequest.php:      $params = array( ':session_principal_id' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+CalDAVRequest.php:      if ( isset($this->by_email) && $this->by_email ) {
+CalDAVRequest.php:        $params[':request_principal_id'] = $this->principal_id;
+CalDAVRequest.php:        $params[':request_path'] = $this->path;
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $permission_result = $qry->Fetch() )
+CalDAVRequest.php:        $this->privileges |= bindec($permission_result->perm);
+CalDAVRequest.php:      dbg_error_log( 'caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges) );
+CalDAVRequest.php:      if ( isset($this->ticket) && $this->ticket->MatchesPath($this->path) ) {
+CalDAVRequest.php:        $this->privileges |= $this->ticket->privileges();
+CalDAVRequest.php:        dbg_error_log( 'caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges) );
+CalDAVRequest.php:    $this->permissions = array();
+CalDAVRequest.php:    $privs = bits_to_privilege($this->privileges);
+CalDAVRequest.php:      $this->permissions[$v] = $type;
+CalDAVRequest.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+CalDAVRequest.php:    if ( !isset($this->_locks_found) ) {
+CalDAVRequest.php:      $this->_locks_found = array();
+CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+CalDAVRequest.php:      $qry = new AwlQuery($sql, array( ':dav_name' => $this->path, ':pattern_end_match' => ($this->IsInfiniteDepth() ? '' : '$') ) );
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+CalDAVRequest.php:        while( $lock_row = $qry->Fetch() ) {
+CalDAVRequest.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+CalDAVRequest.php:        $this->DoResponse(500,translate("Database Error"));
+CalDAVRequest.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+CalDAVRequest.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
+CalDAVRequest.php:    if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
+CalDAVRequest.php:    if ( isset($this->path) ) return $this->path;
+CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return 'infinity';
+CalDAVRequest.php:    return $this->depth;
+CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return '';
+CalDAVRequest.php:    if ( $this->depth == 0 ) return '$';
+CalDAVRequest.php:    if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
+CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+CalDAVRequest.php:      $lock_row = $qry->Fetch();
+CalDAVRequest.php:      $this->_locks_found = array( $lock_token => $lock_row );
+CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+CalDAVRequest.php:      $this->DoResponse( 500, translate("Database Error") );
+CalDAVRequest.php:    if ( isset($this->lock_token) && $this->lock_token == $lock_token ) {
+CalDAVRequest.php:    if ( isset($this->if_clause) ) {
+CalDAVRequest.php:      dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $this->if_clause );
+CalDAVRequest.php:      $tokens = preg_split( '/[<>]/', $this->if_clause );
+CalDAVRequest.php:      @dbg_error_log( "caldav", "Invalid lock token '%s' - not in Lock-token (%s) or If headers (%s) ", $lock_token, $this->lock_token, $this->if_clause );
+CalDAVRequest.php:    if ( !isset($this->_locks_found) && false === $this->IsLocked() ) return false;
+CalDAVRequest.php:    if ( isset($this->_locks_found[$lock_token]) ) return $this->_locks_found[$lock_token];
+CalDAVRequest.php:    if ( $existing_lock = $this->IsLocked() ) { // NOTE Assignment in if() is expected here.
+CalDAVRequest.php:      dbg_error_log( "caldav", "There is a lock on '%s'", $this->path);
+CalDAVRequest.php:      if ( ! $this->ValidateLockToken($existing_lock) ) {
+CalDAVRequest.php:        $lock_row = $this->GetLockRow($existing_lock);
+CalDAVRequest.php:        * Already locked - deny it
+CalDAVRequest.php:            new XMLElement( 'href',   $lock_row->dav_name ),
+CalDAVRequest.php:        if ( $lock_row->dav_name != $this->path ) {
+CalDAVRequest.php:              new XMLElement( 'href',   $this->path ),
+CalDAVRequest.php:        $xmldoc = $response->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+CalDAVRequest.php:        $this->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:  * Coerces the Content-type of the request into something valid/appropriate
+CalDAVRequest.php:    if ( isset($this->content_type) ) {
+CalDAVRequest.php:      $type = explode( '/', $this->content_type, 2);
+CalDAVRequest.php:    /** Null (or peculiar) content-type supplied so we have to try and work it out... */
+CalDAVRequest.php:    $first_word = trim(substr( $this->raw_post, 0, 30));
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/xml"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/xml';
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/calendar"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/calendar';
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/vcard"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/vcard';
+CalDAVRequest.php:        dbg_error_log( 'LOG NOTICE', 'Unusual content-type of "%s" and first word of content is "%s"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)'), $first_word );
+CalDAVRequest.php:    if ( !isset($this->_is_collection) ) {
+CalDAVRequest.php:      $this->_is_collection = preg_match( '#/$#', $this->path );
+CalDAVRequest.php:    return $this->_is_collection;
+CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+CalDAVRequest.php:    return $this->collection->is_calendar == 't';
+CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+CalDAVRequest.php:    return $this->collection->is_addressbook == 't';
+CalDAVRequest.php:    if ( !isset($this->_is_principal) ) {
+CalDAVRequest.php:      $this->_is_principal = preg_match( '#^/[^/]+/$#', $this->path );
+CalDAVRequest.php:    return $this->_is_principal;
+CalDAVRequest.php:    if ( !isset($this->_is_proxy_request) ) {
+CalDAVRequest.php:      $this->_is_proxy_request = preg_match( '#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->path );
+CalDAVRequest.php:    return $this->_is_proxy_request;
+CalDAVRequest.php:    return ($this->depth == DEPTH_INFINITY);
+CalDAVRequest.php:    return $this->collection_id;
+CalDAVRequest.php:    if ( $privs === null ) $privs = $this->supported_privileges;
+CalDAVRequest.php:      $reply->NSElement($privilege,$k);
+CalDAVRequest.php:        dbg_error_log( 'caldav', '"%s" is a container of sub-privileges.', $k );
+CalDAVRequest.php:        $privset = array_merge($privset, $this->BuildSupportedPrivileges($reply,$v));
+CalDAVRequest.php:      $privileges[] = new XMLElement('supported-privilege',$privset);
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:  * | REPORT     | DAV:read or CALDAV:read-free-busy (on all referenced |
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:    dbg_error_log('caldav', 'Checking whether "%s" is allowed to "%s"', $session->username, $activity);
+CalDAVRequest.php:    if ( isset($this->permissions['all']) ) return true;
+CalDAVRequest.php:      case "CALDAV:schedule-send-freebusy":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:      case "CALDAV:schedule-send-invite":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:      case "CALDAV:schedule-send-reply":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['unbind']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-content']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['bind']);
+CalDAVRequest.php:        if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
+CalDAVRequest.php:        if ( $this->is_principal ) return false;
+CalDAVRequest.php:        if ( $this->path == '/' ) return false;
+CalDAVRequest.php://             decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+CalDAVRequest.php:        return (($this->privileges & $test_bits) > 0 );
+CalDAVRequest.php:    return $this->privileges;
+CalDAVRequest.php://              decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+CalDAVRequest.php:    return ($this->privileges & $test_bits) > 0;
+CalDAVRequest.php:  * Sometimes it's a perfectly formed request, but we just don't do that :-(
+CalDAVRequest.php:        dbg_error_log("ERROR", " %s: Support for $v:$k properties is not implemented yet", $this->method );
+CalDAVRequest.php:        $badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
+CalDAVRequest.php:      $this->XMLResponse( 422, $error );
+CalDAVRequest.php:  * Send a need-privileges error response.  This function will only return
+CalDAVRequest.php:      if ( $this->HavePrivilegeTo($privileges) ) return;
+CalDAVRequest.php:      $href = $this->path;
+CalDAVRequest.php:    $privnodes = array( $reply->href(ConstructURL($href)), new XMLElement( 'privilege' ) );
+CalDAVRequest.php:    $reply->NSElement( $privnodes[1], $privileges[0] );
+CalDAVRequest.php:    $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+CalDAVRequest.php:    $xmldoc = $reply->Render('error',$xml);
+CalDAVRequest.php:    $this->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:    $xmldoc = sprintf('<?xml version="1.0" encoding="utf-8" ?>
+CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:    $this->DoResponse( 400, $text );
+CalDAVRequest.php:    $xmldoc = $xmltree->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:  * Utility function we call when we have a simple status-based response to
+CalDAVRequest.php:  function DoResponse( $status, $message="", $content_type="text/plain; charset=\"utf-8\"" ) {
+CalDAVRequest.php:    @header( sprintf("X-DAViCal-Version: DAViCal/%d.%d.%d; DB/%d.%d.%d", $c->code_major, $c->code_minor, $c->code_patch, $c->schema_major, $c->schema_minor, $c->schema_patch) );
+CalDAVRequest.php:    @header( "Content-type: ".$content_type );
+CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['response']) && $c->dbg['response']) || $status > 399 ) {
+CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s", $v );
+CalDAVRequest.php:        dbg_error_log( "LOG response", "-->%s", $v );
+CalDAVRequest.php:    header( "Content-Length: ".strlen($message) );
+CalDAVRequest.php:    if ( isset($c->dbg['caldav']) && $c->dbg['caldav'] ) {
+CalDAVRequest.php:      dbg_error_log("caldav", "Status: %d, Message: %s, User: %d, Path: %s", $status, $message, $session->user_no, $this->path);
+CalDAVRequest.php:    if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+CalDAVRequest.php:      $script_time = microtime(true) - $c->script_start_time;
+CalDAVRequest.php:                         $this->method, $status, $script_time, $c->total_query_time, $this->path);
+check_UTF8.php: * http://iki.fi/hsivonen/php-utf8/
+check_UTF8.php: * Version 1.0, 2003-05-30
+check_UTF8.php: * Takes an UTF-8 string and returns an array of ints representing the
+check_UTF8.php: * Returns false if the input string isn't a valid UTF-8 octet sequence.
+check_UTF8.php:      // When mState is zero we expect either a US-ASCII character or a
+check_UTF8.php:      // multi-octet sequence.
+check_UTF8.php:        // US-ASCII, pass straight through.
+check_UTF8.php:         * (b) outside the Unicode range of 0-0x10FFFF.
+check_UTF8.php:        /* Current octet is neither in the US-ASCII range nor a legal first
+check_UTF8.php:         * octet of a multi-octet sequence.
+check_UTF8.php:      // When mState is non-zero, we expect a continuation of the multi-octet
+check_UTF8.php:        $shift = ($mState - 1) * 6;
+check_UTF8.php:        if (0 == --$mState) {
+check_UTF8.php:          /* End of the multi-octet sequence. mUcs4 now contains the final
+check_UTF8.php:          // From Unicode 3.1, non-shortest form is illegal
+check_UTF8.php:         * Incomplete multi-octet sequence.
+check_UTF8.php: * a UTF-8 string. Astral planes are supported ie. the ints in the
+check_UTF8.php:      // nop -- zap the BOM
+davical_configuration_missing.php:include("page-header.php");
+davical_configuration_missing.php:<p>There is no configuration file present in <b>/etc/davical/$_SERVER[SERVER_NAME]-conf.php</b> so
+davical_configuration_missing.php:<p>Well, you're seeing this! At least you have DAViCal <i>installed</i> :-) You also have Apache and PHP working
+davical_configuration_missing.php:   or later so keep downloading new versions and trying again.  Or make some guesses.  Or bug Andrew :-)</p>
+davical_configuration_missing.php://  \$c->domain_name  = 'davical.example.com';
+davical_configuration_missing.php://  \$c->sysabbr     = 'davical';
+davical_configuration_missing.php://  \$c->system_name = 'DAViCal CalDAV Server';
+davical_configuration_missing.php:  \$c->admin_email  = 'admin@example.com';
+davical_configuration_missing.php:  \$c->pg_connect[] = 'dbname=davical user=davical_app';
+davical_configuration_missing.php:configuring someone for the admin e-mail is a really good idea.</p>
+davical_configuration_missing.php:include("page-footer.php");
+DAViCalSession.php:// the records related to the logged-on user...  (the where clause gets added).
+DAViCalSession.php:    $this->Session($sid);
+DAViCalSession.php:    if ( !isset($u->principal_id) ) {
+DAViCalSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+DAViCalSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+DAViCalSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+DAViCalSession.php:        $u = $qry->Fetch();
+DAViCalSession.php:    $this->GetRoles();
+DAViCalSession.php:    if ( function_exists('awl_set_locale') && isset($this->locale) && $this->locale != '' ) {
+DAViCalSession.php:      awl_set_locale($this->locale);
+DAViCalSession.php:    $this->roles = array();
+DAViCalSession.php:    $sql = 'SELECT role_name FROM roles JOIN role_member ON roles.role_no=role_member.role_no WHERE user_no = '.$this->user_no;
+DAViCalSession.php:    if ( $qry->Exec('DAViCalSession') && $qry->rows() > 0 ) {
+DAViCalSession.php:      while( $role = $qry->Fetch() ) {
+DAViCalSession.php:        $this->roles[$role->role_name] = 1;
+DAViCalSession.php:    if ( (isset($c->restrict_admin_domain) && $c->restrict_admin_domain != $current_domain)
+DAViCalSession.php:      || (isset($c->restrict_admin_port) && $c->restrict_admin_port != $_SERVER['SERVER_PORT'] ) ) {
+DAViCalSession.php:    if ( isset($c->restrict_admin_roles) && $roles == '' ) $roles = $c->restrict_admin_roles;
+DAViCalSession.php:    if ( $this->logged_in && $roles == '' ) return;
+DAViCalSession.php:    if ( ! $this->logged_in ) {
+DAViCalSession.php:      $c->messages[] = i18n('You must log in to use this system.');
+DAViCalSession.php:      include_once('page-header.php');
+DAViCalSession.php:        if ( $this->login_failed ) {
+DAViCalSession.php:          $c->messages[] = i18n('Invalid user name or password.');
+DAViCalSession.php:                  .' '.translate($c->system_name).' '
+DAViCalSession.php:        echo '<p>'.translate('If you would like to request access, please e-mail').' '.$c->admin_email."</p>\n";
+DAViCalSession.php:        echo $this->RenderLoginPanel();
+DAViCalSession.php:        if ( $this->AllowedTo($v) ) return;
+DAViCalSession.php:      $c->messages[] = i18n('You are not authorised to use this function.');
+DAViCalSession.php:      include_once('page-header.php');
+DAViCalSession.php:    include('page-footer.php');
+DAViCalSession.php:$session->_CheckLogin();
+DAVResource.php:  * @var True if this resource is a principal-URL
+DAVResource.php:    $this->exists        = null;
+DAVResource.php:    $this->bound_from    = null;
+DAVResource.php:    $this->dav_name      = null;
+DAVResource.php:    $this->unique_tag    = null;
+DAVResource.php:    $this->resource      = null;
+DAVResource.php:    $this->collection    = null;
+DAVResource.php:    $this->principal     = null;
+DAVResource.php:    $this->parent        = null;
+DAVResource.php:    $this->resourcetypes = null;
+DAVResource.php:    $this->contenttype   = null;
+DAVResource.php:    $this->privileges    = null;
+DAVResource.php:    $this->dead_properties   = null;
+DAVResource.php:    $this->supported_methods = null;
+DAVResource.php:    $this->supported_reports = null;
+DAVResource.php:    $this->_is_collection    = false;
+DAVResource.php:    $this->_is_principal     = false;
+DAVResource.php:    $this->_is_calendar      = false;
+DAVResource.php:    $this->_is_binding       = false;
+DAVResource.php:    $this->_is_addressbook   = false;
+DAVResource.php:    $this->_is_proxy_request = false;
+DAVResource.php:      $this->FromRow($parameters);
+DAVResource.php:        $this->FromPath($parameters['path']);
+DAVResource.php:      $this->FromPath($parameters);
+DAVResource.php:    $this->exists = true;
+DAVResource.php:    // $this->dav_name = $row->dav_name;
+DAVResource.php:    $this->dav_name = $row->owner;
+DAVResource.php:    //$this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+DAVResource.php:    $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->owner);
+DAVResource.php:    $this->_is_collection = preg_match( '{/$}', $this->owner );
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+DAVResource.php:      $this->collection = (object) array();
+DAVResource.php:      $this->resource_id = $row->collection_id;
+DAVResource.php:      $this->_is_principal = preg_match( '{^/[^/]+/$}', $this->dav_name );
+DAVResource.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
+DAVResource.php:        $this->collection->dav_name = $matches[1].'/';
+DAVResource.php:        $this->collection->type = 'principal_link';
+DAVResource.php:        $this->_is_principal = true;
+DAVResource.php:      $this->resource = (object) array();
+DAVResource.php:      //if ( isset($row->dav_id) ) $this->resource_id = $row->dav_id;
+DAVResource.php:      if ( isset($row->cal_id) ) $this->resource_id = $row->cal_id;
+DAVResource.php:    dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->dav_name, ($this->_is_collection?'':' not') );
+DAVResource.php:      if ( $this->_is_collection )
+DAVResource.php:        $this->collection->{$k} = $v;
+DAVResource.php:        $this->resource->{$k} = $v;
+DAVResource.php:        //  $this->{$k} = $v;
+DAVResource.php:          $this->resourcetypes = '';
+DAVResource.php:          $this->unique_tag = '"'.$v.'@'.$this->dav_name.'"'; 
+DAVResource.php:          $this->unique_tag = '"'.$v.'@'.$this->dav_name.'"'; 
+DAVResource.php:          $this->resourcetypes = '';
+DAVResource.php:        //  $this->unique_tag = '"'.$v.'"';
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      if ( !isset( $this->collection->type ) || $this->collection->type == 'collection' ) {
+DAVResource.php:        if ( $this->_is_principal )
+DAVResource.php:          $this->collection->type = 'principal';
+DAVResource.php:        else if ( $row->is_calendar == 't' ) {
+DAVResource.php:          $this->collection->type = 'calendar';
+DAVResource.php:        else if ( $row->is_addressbook == 't' ) {
+DAVResource.php:          $this->collection->type = 'addressbook';
+DAVResource.php:        else if ( isset($row->is_proxy) && $row->is_proxy == 't' ) {
+DAVResource.php:          $this->collection->type = 'proxy';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:        else if ( $this->dav_name == '/' )
+DAVResource.php:          $this->collection->type = 'root';
+DAVResource.php:          $this->collection->type = 'collection';
+DAVResource.php:      $this->_is_calendar     = ($this->collection->is_calendar == 't');
+DAVResource.php:      $this->_is_addressbook  = ($this->collection->is_addressbook == 't');
+DAVResource.php:      $this->_is_proxy_request= ($this->collection->type == 'proxy');
+DAVResource.php:      if ( $this->_is_principal && !isset($this->resourcetypes) ) {
+DAVResource.php:        $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+DAVResource.php:      else if ( $this->_is_proxy_request ) {
+DAVResource.php:        $this->resourcetypes  = $this->collection->resourcetypes;
+DAVResource.php:      if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+DAVResource.php:      $this->resourcetypes = '';
+DAVResource.php:      //if ( isset($this->resource->caldav_data) ) {
+DAVResource.php:      if ( isset($this->resource->owner) ) {
+DAVResource.php:        //if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) $this->contenttype = 'text/calendar';
+DAVResource.php:        $this->contenttype = 'text/calendar';
+DAVResource.php:        $this->resource->displayname = $this->resource->title;
+DAVResource.php:        $this->contenttype = 'text/vcard';
+DAVResource.php:        $this->resource->displayname = $this->resource->names_ordered;
+DAVResource.php:    $this->dav_name = DeconstructURL($inpath);
+DAVResource.php:    $this->FetchCollection();
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      if ( $this->_is_principal || $this->collection->type == 'principal' )   $this->FetchPrincipal();
+DAVResource.php:      $this->FetchResource();
+DAVResource.php:               $this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
+DAVResource.php:    *    - also discussed at more length in 5.2
+DAVResource.php:    *  - The exact request.
+DAVResource.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+DAVResource.php:    *  - The request URL truncated to the last '/'
+DAVResource.php:    dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->dav_name );
+DAVResource.php:    $this->collection = (object) array(
+DAVResource.php:      'collection_id' => -1,
+DAVResource.php:    $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:    if ( !preg_match( '#/$#', $this->dav_name ) ) {
+DAVResource.php:      $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+DAVResource.php:      $params[':plus_slash']  = $this->dav_name.'/';
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:      $this->collection = $row;
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      if ( $row->is_calendar == 't' )
+DAVResource.php:        $this->collection->type = 'calendar';
+DAVResource.php:      else if ( $row->is_addressbook == 't' )
+DAVResource.php:        $this->collection->type = 'addressbook';
+DAVResource.php:      else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:        $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:        $this->collection->type = 'collection';
+DAVResource.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->dav_name, $matches ) ) {
+DAVResource.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+DAVResource.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+DAVResource.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+DAVResource.php:      $qry->Exec('DAVResource');
+DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:        $this->collection = $row;
+DAVResource.php:        $this->collection->exists = true;
+DAVResource.php:        $this->collection->type = $this->collection_type;
+DAVResource.php:    else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
+DAVResource.php:      $this->collection->type = 'proxy';
+DAVResource.php:      $this->_is_proxy_request = true;
+DAVResource.php:      $this->proxy_type = $matches[3];
+DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+DAVResource.php:      $this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      $this->collection->parent_container = $matches[1] . '/';
+DAVResource.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches)
+DAVResource.php:           || preg_match( '#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
+DAVResource.php:      $this->_is_principal = true;
+DAVResource.php:      $this->FetchPrincipal();
+DAVResource.php:    else if ( $this->dav_name == '/' ) {
+DAVResource.php:      $this->collection->dav_name = '/';
+DAVResource.php:      $this->collection->type = 'root';
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      $this->collection->displayname = $c->system_name;
+DAVResource.php:      $this->collection->default_privileges = (1 | 16 | 32);
+DAVResource.php:      $this->collection->parent_container = '/';
+DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:      if ( !preg_match( '#/$#', $this->dav_name ) ) {
+DAVResource.php:        $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+DAVResource.php:        $params[':plus_slash']  = $this->dav_name.'/';
+DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:        $this->collection = $row;
+DAVResource.php:        $this->collection->exists = true;
+DAVResource.php:        $this->_is_binding = true;
+DAVResource.php:        $this->collection->parent_set = $row->parent_container;
+DAVResource.php:        $this->collection->parent_container = $row->bind_parent_container;
+DAVResource.php:        $this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
+DAVResource.php:        $this->collection->bound_from = $row->dav_name;
+DAVResource.php:        $this->collection->dav_name = $row->bound_to;
+DAVResource.php:        if ( isset($row->access_ticket_id) ) {
+DAVResource.php:          if ( !isset($this->tickets) ) $this->tickets = array();
+DAVResource.php:          $this->tickets[] = new DAVTicket($row->access_ticket_id);
+DAVResource.php:        if ( $row->is_calendar == 't' )
+DAVResource.php:          $this->collection->type = 'calendar';
+DAVResource.php:        else if ( $row->is_addressbook == 't' )
+DAVResource.php:          $this->collection->type = 'addressbook';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:          $this->collection->type = 'collection';
+DAVResource.php:        dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
+DAVResource.php:        $this->collection->exists = false;
+DAVResource.php:        $this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
+DAVResource.php:    @dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
+DAVResource.php:    $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      $this->dav_name = $this->collection->dav_name;
+DAVResource.php:      $this->resource_id = $this->collection->collection_id;
+DAVResource.php:      $this->_is_calendar    = ($this->collection->type == 'calendar');
+DAVResource.php:      $this->_is_addressbook = ($this->collection->type == 'addressbook');
+DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+DAVResource.php:      if ( !isset($this->exists) && isset($this->collection->exists) ) {
+DAVResource.php:        $this->exists = $this->collection->exists;
+DAVResource.php:      if ( $this->exists ) {
+DAVResource.php:        if ( isset($this->collection->dav_etag) ) $this->unique_tag = '"'.$this->collection->dav_etag.'"';
+DAVResource.php:        if ( isset($this->collection->created) )  $this->created = $this->collection->created;
+DAVResource.php:        if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
+DAVResource.php:        if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+DAVResource.php:        if ( !isset($this->parent) ){ 
+DAVResource.php:            $this->FetchParentContainer();
+DAVResource.php:        $this->user_no = $this->parent->GetProperty('user_no');
+DAVResource.php:      if ( isset($this->collection->resourcetypes) )
+DAVResource.php:        $this->resourcetypes = $this->collection->resourcetypes;
+DAVResource.php:        $this->resourcetypes = '<DAV::collection/>';
+DAVResource.php:        if ( $this->_is_principal )   $this->resourcetypes .= '<DAV::principal/>';
+DAVResource.php:        if ( $this->_is_addressbook ) $this->resourcetypes .= '<urn:ietf:params:xml:ns:carddav:addressbook/>';
+DAVResource.php:        if ( $this->_is_calendar )    $this->resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+DAVResource.php:    if ( isset($this->principal) ) return;
+DAVResource.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->bound_from() ) );
+DAVResource.php:    if ( $this->_is_principal && $this->principal->Exists() ) {
+DAVResource.php:      $this->exists = true;
+DAVResource.php:      $this->unique_tag = $this->principal->dav_etag;
+DAVResource.php:      $this->created = $this->principal->created;
+DAVResource.php:      $this->modified = $this->principal->modified;
+DAVResource.php:      $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+DAVResource.php:      $this->resource_id = $this->principal->principal_id;
+DAVResource.php:      $this->collection = $this->principal->AsCollection();
+DAVResource.php:      $this->user_no = $this->principal->user_no;
+DAVResource.php:    elseif ( $this->_is_principal ) {
+DAVResource.php:      $this->exists = false;
+DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+DAVResource.php:      $this->collection->type = 'principal';
+DAVResource.php:    if ( isset($this->exists) ) return;   // True or false, we've got what we can already
+DAVResource.php:    if ( $this->_is_collection ) return;   // We have all we're going to read
+DAVResource.php:    $params = array( ':dav_name' => $this->bound_from() );
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+DAVResource.php:      $this->exists = true;
+DAVResource.php:      $this->resource = $qry->Fetch();
+DAVResource.php:      $this->unique_tag = $this->resource->dav_etag;
+DAVResource.php:      $this->created = $this->resource->created;
+DAVResource.php:      $this->modified = $this->resource->modified;
+DAVResource.php:      $this->resource_id = $this->resource->dav_id;
+DAVResource.php:      if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
+DAVResource.php:        $this->contenttype = 'text/calendar';
+DAVResource.php:      $this->resourcetypes = '';
+DAVResource.php:      $this->exists = false;
+DAVResource.php:    if ( isset($this->dead_properties) ) return;
+DAVResource.php:    $this->dead_properties = array();
+DAVResource.php:    $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
+DAVResource.php:    if ( $qry->Exec('DAVResource') ) {
+DAVResource.php:      while ( $property = $qry->Fetch() ) {
+DAVResource.php:        $this->dead_properties[$property->property_name] = $property->property_value;
+DAVResource.php:    if ( $this->dav_name == '/' ) return null;
+DAVResource.php:    if ( !isset($this->parent) ) {
+DAVResource.php:      if ( $this->_is_collection ) {
+DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+DAVResource.php:        $this->parent = new DAVResource( $this->parent_path() );
+DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+DAVResource.php:        $this->parent = new DAVResource($this->collection->dav_name);
+DAVResource.php:    return $this->parent;
+DAVResource.php:    if ( $this->dav_name == '/' || $this->dav_name == '' ) {
+DAVResource.php:      $this->privileges = (1 | 16 | 32); // read + read-acl + read-current-user-privilege-set
+DAVResource.php:    if ( $session->AllowedTo('Admin') ) {
+DAVResource.php:      $this->privileges = privilege_to_bits('all');
+DAVResource.php:    if ( $this->IsPrincipal() ) {
+DAVResource.php:      if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:      $this->privileges = $this->principal->Privileges();
+DAVResource.php:      dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing "%s"', $this->privileges, $this->principal->username() );
+DAVResource.php:    if ( ! isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:    $this->privileges = 0;
+DAVResource.php:    if ( !isset($this->collection->path_privs) ) {
+DAVResource.php:      /*if ( !isset($this->parent) ) $this->FetchParentContainer();
+DAVResource.php:      $this->collection->path_privs = $this->parent->Privileges();
+DAVResource.php:      $this->collection->user_no = $this->parent->GetProperty('user_no');
+DAVResource.php:      $this->collection->principal_id = $this->parent->GetProperty('principal_id');
+DAVResource.php:       $filtro = "uid=".$session->username;
+DAVResource.php:       $this->collection->user_no = $uidnumber['uidNumber'];  
+DAVResource.php:    $this->privileges = privilege_to_bits('all');//$this->collection->path_privs;
+DAVResource.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+DAVResource.php:                       decbin($this->privileges), $session->username, $this->dav_name() );
+DAVResource.php:    if ( isset($request->ticket) && $request->ticket->MatchesPath($this->bound_from()) ) {
+DAVResource.php:      $this->privileges |= $request->ticket->privileges();
+DAVResource.php:      dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
+DAVResource.php:    if ( isset($this->tickets) ) {
+DAVResource.php:      if ( !isset($this->resource_id) ) $this->FetchResource();
+DAVResource.php:      foreach( $this->tickets AS $k => $ticket ) {
+DAVResource.php:        if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
+DAVResource.php:          $this->privileges |= $ticket->privileges();
+DAVResource.php:          dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
+DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:    return $this->privileges;
+DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:        $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+DAVResource.php:      return ($this->privileges & $test_bits) > 0;
+DAVResource.php:      return ($this->privileges & $test_bits) == $test_bits;
+DAVResource.php:    if ( $this->HavePrivilegeTo($privilege, $any) ) return;
+DAVResource.php:    $request->NeedPrivilege( $privilege, $this->dav_name );
+DAVResource.php:      if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:      $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type : null ) );
+DAVResource.php:    if ( isset($this->supported_methods) ) return $this->supported_methods;
+DAVResource.php:    $this->supported_methods = array(
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:/*      if ( $this->IsPrincipal() ) {
+DAVResource.php:        $this->supported_methods['MKCALENDAR'] = '';
+DAVResource.php:        $this->supported_methods['MKCOL'] = '';
+DAVResource.php:      switch ( $this->collection->type ) {
+DAVResource.php:          $this->supported_methods = array(
+DAVResource.php:        case 'schedule-outbox':
+DAVResource.php:          $this->supported_methods = array_merge(
+DAVResource.php:            $this->supported_methods,
+DAVResource.php:        case 'schedule-inbox':
+DAVResource.php:          $this->supported_methods['GET'] = '';
+DAVResource.php:          $this->supported_methods['PUT'] = '';
+DAVResource.php:          $this->supported_methods['HEAD'] = '';
+DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+DAVResource.php:          $this->supported_methods['GET'] = '';
+DAVResource.php:          $this->supported_methods['PUT'] = '';
+DAVResource.php:          $this->supported_methods['HEAD'] = '';
+DAVResource.php:          $this->supported_methods['MKCOL'] = '';
+DAVResource.php:          $this->supported_methods['MKCALENDAR'] = '';
+DAVResource.php:          $this->supported_methods['PROPPATCH'] = '';
+DAVResource.php:      $this->supported_methods = array_merge(
+DAVResource.php:        $this->supported_methods,
+DAVResource.php:    return $this->supported_methods;
+DAVResource.php:    if ( !isset($this->supported_methods) ) $this->FetchSupportedMethods();
+DAVResource.php:    foreach( $this->supported_methods AS $k => $v ) {
+DAVResource.php:      $methods[] = new XMLElement( 'supported-method', null, array('name' => $k) );
+DAVResource.php:    if ( isset($this->supported_reports) ) return $this->supported_reports;
+DAVResource.php:   /************* comentado por incompatibilidade do sync-collection 
+DAVResource.php:    $this->supported_reports = array(
+DAVResource.php:      'DAV::principal-property-search' => '',
+DAVResource.php:      'DAV::principal-search-property-set' => '',
+DAVResource.php:      'DAV::expand-property' => '',
+DAVResource.php:      'DAV::sync-collection' => ''
+DAVResource.php:    $this->supported_reports = array(
+DAVResource.php:      'DAV::principal-property-search' => '',
+DAVResource.php:      'DAV::principal-search-property-set' => '',
+DAVResource.php:      'DAV::expand-property' => ''
+DAVResource.php:    if ( !isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:    if ( $this->collection->is_calendar ) {
+DAVResource.php:      $this->supported_reports = array_merge(
+DAVResource.php:        $this->supported_reports,
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+DAVResource.php:    if ( $this->collection->is_addressbook ) {
+DAVResource.php:      $this->supported_reports = array_merge(
+DAVResource.php:        $this->supported_reports,
+DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+DAVResource.php:    return $this->supported_reports;
+DAVResource.php:    if ( !isset($this->supported_reports) ) $this->FetchSupportedReports();
+DAVResource.php:    foreach( $this->supported_reports AS $k => $v ) {
+DAVResource.php:      $reply->NSElement($report, $k );
+DAVResource.php:      $reports[] = new XMLElement('supported-report', $report );
+DAVResource.php:    if ( isset($this->access_tickets) ) return;
+DAVResource.php:    $this->access_tickets = array();
+DAVResource.php:        EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds,
+DAVResource.php:    $params = array(':collection_id' => $this->collection->collection_id, ':scan_depth' => $c->permission_scan_depth);
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:      if ( !isset($this->exists) ) $this->FetchResource();
+DAVResource.php:      $params[':dav_id'] = $this->resource->dav_id;
+DAVResource.php:    if ( isset($this->exists) && !$this->exists ) return;
+DAVResource.php:    if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
+DAVResource.php:      while( $ticket = $qry->Fetch() ) {
+DAVResource.php:        $this->access_tickets[] = $ticket;
+DAVResource.php:  * If the current user does not have DAV::read-acl privilege on this resource they
+DAVResource.php:    if ( !isset($this->access_tickets) ) $this->FetchTickets();
+DAVResource.php:    $show_all = $this->HavePrivilegeTo('DAV::read-acl');
+DAVResource.php:    foreach( $this->access_tickets AS $meh => $trow ) {
+DAVResource.php:      if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) ) continue;
+DAVResource.php:      dbg_error_log( 'DAVResource', ':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
+DAVResource.php:      $ticket = new XMLElement( $reply->Tag( 'ticketinfo', 'http://www.xythos.com/namespaces/StorageServer', 'TKT' ) );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf( 'Seconds-%d', $trow->seconds) : 'infinity') );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:visits', 'infinity' );
+DAVResource.php:      foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
+DAVResource.php:        $privs[] = $reply->NewXMLElement($v);
+DAVResource.php:      $reply->NSElement($ticket, 'DAV::privilege', $privs );
+DAVResource.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+DAVResource.php:    if ( !isset($this->_locks_found) ) {
+DAVResource.php:      $this->_locks_found = array();
+DAVResource.php:      $qry = new AwlQuery($sql, array( ':this_path' => $this->dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
+DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) ) {
+DAVResource.php:        while( $lock_row = $qry->Fetch() ) {
+DAVResource.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+DAVResource.php:        $this->DoResponse(500,i18n("Database Error"));
+DAVResource.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+DAVResource.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
+DAVResource.php:    return $this->_is_collection;
+DAVResource.php:    return $this->_is_collection && $this->_is_principal;
+DAVResource.php:    return $this->_is_collection && $this->_is_calendar;
+DAVResource.php:    if ( $this->_is_collection && preg_match( '{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
+DAVResource.php:    return $this->_is_collection && $this->_is_addressbook;
+DAVResource.php:    return $this->_is_binding;
+DAVResource.php:    if ( ! isset($this->exists) ) {
+DAVResource.php:      if ( $this->IsPrincipal() ) {
+DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:        $this->exists = $this->principal->Exists();
+DAVResource.php:      else if ( ! $this->IsCollection() ) {
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php://    dbg_error_log('DAVResource',' Checking whether "%s" exists.  It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
+DAVResource.php:    return $this->exists;
+DAVResource.php:    if ( $this->collection->dav_name != $this->dav_name ) {
+DAVResource.php:      return $this->collection->exists;
+DAVResource.php:    $parent = $this->FetchParentContainer();
+DAVResource.php:    return $parent->Exists();
+DAVResource.php:    if ( isset($this->dav_name) ) return $this->dav_name;
+DAVResource.php:    if ( isset($this->bound_from) ) return $this->bound_from;
+DAVResource.php:    return $this->dav_name();
+DAVResource.php:    if ( !isset($this->bound_from) && isset($this->dav_name) ) {
+DAVResource.php:      $this->bound_from = $this->dav_name;
+DAVResource.php:    $this->dav_name = $new_dav_name;
+DAVResource.php:    return $this->dav_name;
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:      if ( !isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:      if ( !isset($this->collection->parent_container) ) {
+DAVResource.php:        $this->collection->parent_container = preg_replace( '{[^/]+/$}', '', $this->bound_from());
+DAVResource.php:      return $this->collection->parent_container;
+DAVResource.php:    return preg_replace( '{[^/]+$}', '', $this->bound_from());
+DAVResource.php:  * Returns the principal-URL for this resource
+DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    if ( $this->principal->Exists() ) {
+DAVResource.php:      return $this->principal->principal_url;
+DAVResource.php:    if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    return $this->resource;
+DAVResource.php:    //if ( isset($this->unique_tag) ) return $this->unique_tag;
+DAVResource.php:    if ( isset($this->unique_tag) ) 
+DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
+DAVResource.php:     //return $this->unique_tag;
+DAVResource.php:  * Returns the definitive resource_id for this resource - usually a dav_id
+DAVResource.php:    if ( isset($this->resource_id) ) return $this->resource_id;
+DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    if ( $this->exists !== true || !isset($this->resource_id) ) $this->resource_id = null;
+DAVResource.php:    return $this->resource_id;
+DAVResource.php:    return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' );
+DAVResource.php:    if ( $this->IsPrincipal() ) return 'root';
+DAVResource.php:    if ( !$this->IsCollection() ) return $this->collection->type;
+DAVResource.php:    if ( ! isset($this->collection->parent_container) ) return null;
+DAVResource.php:    if ( isset($this->parent_container_type) ) return $this->parent_container_type;
+DAVResource.php:    if ( preg_match('#/[^/]+/#', $this->collection->parent_container) ) {
+DAVResource.php:      $this->parent_container_type = 'principal';
+DAVResource.php:                                array( ':parent_name' => $this->collection->parent_container ) );
+DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
+DAVResource.php:        if ( $parent->is_calendar == 't' )
+DAVResource.php:          $this->parent_container_type = 'calendar';
+DAVResource.php:        else if ( $parent->is_addressbook == 't' )
+DAVResource.php:          $this->parent_container_type = 'addressbook';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->parent_container_type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:          $this->parent_container_type = 'collection';
+DAVResource.php:        $this->parent_container_type = null;
+DAVResource.php:    return $this->parent_container_type;
+DAVResource.php:  * BuildACE - construct an XMLElement subtree for a DAV::ace
+DAVResource.php:    $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type : 'resource'));
+DAVResource.php:        $xmldoc->NSElement($privilege,$k);
+DAVResource.php:        $privilege->NewElement($k);
+DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    $default_privs = $this->principal->default_privileges;
+DAVResource.php:    if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
+DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, pow(2,25) - 1, new XMLElement('property', new XMLElement('owner')) );
+DAVResource.php:                                array( ':collection_id' => $this->collection->collection_id, ':principal_id' => $this->principal->principal_id ) );
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+DAVResource.php:      while( $grant = $qry->Fetch() ) {
+DAVResource.php:        if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
+DAVResource.php:        if ( $by_collection &&  !isset($grant->by_collection) ) break;
+DAVResource.php:        $acl[] = $this->BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
+DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, $default_privs, new XMLElement('authenticated') );
+DAVResource.php:  * Return general server-related properties, in plain form
+DAVResource.php:        return $this->collection->collection_id;
+DAVResource.php:        if ( isset($this->resourcetypes) ) {
+DAVResource.php:          $this->resourcetypes = preg_replace('{^\s*<(.*)/>\s*$}', '$1', $this->resourcetypes);
+DAVResource.php:          $type_list = preg_split('{(/>\s*<|\n)}', $this->resourcetypes);
+DAVResource.php:        if ( isset($this->{$name}) ) return $this->{$name};
+DAVResource.php:        if ( $this->_is_principal ) {
+DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php:        else if ( $this->_is_collection ) {
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:          if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
+DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php:      //  dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
+DAVResource.php:    if ( isset($this->dead_properties) ) $this->FetchDeadProperties();
+DAVResource.php:    $allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()),
+DAVResource.php:        'DAV::owner', 'DAV::principal-URL', 'DAV::current-user-principal',
+DAVResource.php:        'urn:ietf:params:xml:ns:carddav:max-resource-size', 'urn:ietf:params:xml:ns:carddav:supported-address-data',
+DAVResource.php:        'urn:ietf:params:xml:ns:carddav:addressbook-description', 'urn:ietf:params:xml:ns:carddav:addressbook-home-set'
+DAVResource.php:  * Return general server-related properties for this URL
+DAVResource.php://    dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
+DAVResource.php:        $property_list = $this->DAV_AllProperties();
+DAVResource.php:          $this->ResourceProperty($v, $prop, $reply, $discarded);
+DAVResource.php:        $prop->NewElement('href', ConstructURL($this->dav_name) );
+DAVResource.php:      case 'DAV::resource-id':
+DAVResource.php:        if ( $this->resource_id > 0 )
+DAVResource.php:          $reply->DAVElement( $prop, 'resource-id', $reply->href(ConstructURL('/.resources/'.$this->resource_id) ) );
+DAVResource.php:      case 'DAV::parent-set':
+DAVResource.php:        $qry = new AwlQuery($sql, array( ':bound_from' => $this->bound_from() ) );
+DAVResource.php:        if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+DAVResource.php:          while( $row = $qry->Fetch() ) {
+DAVResource.php:            $parents[$row->parent_container] = true;
+DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->bound_from())] = true;
+DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->dav_name())] = true;
+DAVResource.php:        $parent_set = $reply->DAVElement( $prop, 'parent-set' );
+DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+DAVResource.php:        //if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        if ( !isset($this->contenttype)  && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        $prop->NewElement('getcontenttype', $this->contenttype );
+DAVResource.php:        $resourcetypes = $prop->NewElement('resourcetype' );
+DAVResource.php:        $type_list = $this->GetProperty('resourcetype');
+DAVResource.php:          $reply->NSElement( $resourcetypes, $v );
+DAVResource.php:        if ( $this->_is_binding ) {
+DAVResource.php:          $reply->NSElement( $resourcetypes, 'http://xmlns.davical.org/davical:webdav-binding' );
+DAVResource.php:        $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
+DAVResource.php:        $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        if ( isset($this->resource) ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
+DAVResource.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+DAVResource.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+DAVResource.php:        $reply->NSElement($prop, $tag, $locale );
+DAVResource.php:      case 'DAV::acl-restrictions':
+DAVResource.php:        $reply->NSElement($prop, $tag, array( new XMLElement('grant-only'), new XMLElement('no-invert') ) );
+DAVResource.php:      case 'DAV::inherited-acl-set':
+DAVResource.php:        if ( ! $this->_is_collection ) {
+DAVResource.php:          $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
+DAVResource.php:        $reply->NSElement($prop, $tag, $inherited_acls );
+DAVResource.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+DAVResource.php:        $owner_url = ( isset($this->_is_binding) && $this->_is_binding ? $this->collection->bind_owner_url : $this->principal_url() );
+DAVResource.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $owner_url ) );
+DAVResource.php:      case 'DAV::alternate-URI-set':
+DAVResource.php:        $reply->NSElement($prop, $tag );
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
+DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
+DAVResource.php:        $reply->CalendarserverElement($prop, 'calendar-proxy-'.$proxy_type.'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
+DAVResource.php:      case 'DAV::current-user-privilege-set':
+DAVResource.php:        //if ( $this->HavePrivilegeTo('DAV::read-current-user-privilege-set') ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, $this->BuildPrivileges() );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
+DAVResource.php:        if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, 'text/calendar' );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        if ( $this->IsCalendar() )
+DAVResource.php:        else if ( $this->IsSchedulingCollection() )
+DAVResource.php:          $components[] = $reply->NewXMLElement( 'comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
+DAVResource.php:        $reply->CalDAVElement($prop, 'supported-calendar-component-set', $components );
+DAVResource.php:      case 'DAV::supported-method-set':
+DAVResource.php:        $prop->NewElement('supported-method-set', $this->BuildSupportedMethods() );
+DAVResource.php:      case 'DAV::supported-report-set':
+DAVResource.php:        $prop->NewElement('supported-report-set', $this->BuildSupportedReports( $reply ) );
+DAVResource.php:        $prop->NewElement('supportedlock',
+DAVResource.php:      case 'DAV::supported-privilege-set':
+DAVResource.php:        $prop->NewElement('supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
+DAVResource.php:      case 'DAV::current-user-principal':
+DAVResource.php:        $prop->NewElement('current-user-principal', $reply->href( $request->principal->principal_url ) );
+DAVResource.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+DAVResource.php:        $denied[] = $reply->Tag($tag);
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->collection->tz_spec) || $this->collection->tz_spec == '' ) return false;
+DAVResource.php:        $cal->VCalendar();
+DAVResource.php:        $cal->AddComponent( new iCalComponent($this->collection->tz_spec) );
+DAVResource.php:        $reply->NSElement($prop, $tag, $cal->Render() );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:address-data':
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->resource->caldav_data );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
+DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, 65500 );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
+DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+DAVResource.php:        $address_data = $reply->NewXMLElement( 'address-data', false,
+DAVResource.php:                      array( 'content-type' => 'text/vcard', 'version' => '3.0'), 'urn:ietf:params:xml:ns:carddav');
+DAVResource.php:        $reply->NSElement($prop, $tag, $address_data );
+DAVResource.php:        if ( $this->HavePrivilegeTo('DAV::read-acl') ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, $this->GetACL( $reply ) );
+DAVResource.php:        $reply->NSElement($prop,'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply) );
+DAVResource.php:        $property_value = $this->GetProperty(preg_replace('{^.*:}', '', $tag));
+DAVResource.php:          $reply->NSElement($prop, $tag, $property_value );
+DAVResource.php:          if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
+DAVResource.php:          if ( isset($this->dead_properties[$tag]) ) {
+DAVResource.php:            $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
+DAVResource.php://            dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
+DAVResource.php:    dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
+DAVResource.php:        $tag = $tag->GetTag();
+DAVResource.php:      $found = $this->ResourceProperty($tag, $prop, $reply, $denied );
+DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:        $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
+DAVResource.php://        dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
+DAVResource.php:        $not_found[] = $reply->Tag($tag);
+DAVResource.php:        $reply->NSElement($noprop, $v);
+DAVResource.php:        $noprop->NewElement($v);
+DAVResource.php:    dbg_error_log('DAVResource',':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
+DAVResource.php:    if ( !$this->Exists() ) return null;
+DAVResource.php:    $elements = $this->GetPropStat( $properties, $reply );
+DAVResource.php:      $dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
+DAVResource.php:      $dav_name = $this->dav_name;
+DAVResource.php:    array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
+DAVTicket.php:    $this->dav_name             = null;
+DAVTicket.php:    $this->target_collection_id = null;
+DAVTicket.php:    $this->target_resource_id   = null;
+DAVTicket.php:    $this->expiry               = null;
+DAVTicket.php:    $this->expired              = true;
+DAVTicket.php:    $this->dav_owner_id         = null;
+DAVTicket.php:    $this->ticket_id            = $ticket_id;
+DAVTicket.php:    $this->privileges           = 0;
+DAVTicket.php:    $this->grantor_collection_privileges = 0;
+DAVTicket.php:        array(':ticket_id' => $ticket_id, ':scan_depth' => $c->permission_scan_depth)
+DAVTicket.php:    if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $t = $qry->Fetch() ) {
+DAVTicket.php:      if ( ! $t->expired ) {
+DAVTicket.php:          $this->{$k} = $v;
+DAVTicket.php:        $this->expired = false;
+DAVTicket.php:        $this->privileges = bindec($this->privileges);
+DAVTicket.php:        $this->grantor_collection_privileges = bindec($this->grantor_collection_privileges);
+DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found a current ticket for "%s"', implode(', ',bits_to_privilege($this->privileges())) );
+DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found an expired ticket: %s - %s', $ticket_id, $t->expires );
+DAVTicket.php:    if ( isset($this->target_resource_id) ) {
+DAVTicket.php:      $qry = new AwlQuery( 'SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id', array(':dav_id' => $this->target_resource_id ) );
+DAVTicket.php:      if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $r = $qry->Fetch() ) {
+DAVTicket.php:        $this->dav_name = $r->dav_name;
+DAVTicket.php:    return $this->dav_name;
+DAVTicket.php:    return $this->ticket_id;
+DAVTicket.php:    return ($this->privileges & $this->grantor_collection_privileges);
+DAVTicket.php:    $length = strlen($this->dav_name);
+DAVTicket.php:    return (substr($test_path, 0, $length) == $this->dav_name);
+DAVTicket.php:    return ($test_resource_id == $this->target_collection_id || $test_resource_id == $this->target_resource_id);
+drivers_ldap.php:require_once("auth-functions.php");
+drivers_ldap.php:  /**#@-*/
+drivers_ldap.php:      $this->__construct($config);
+drivers_ldap.php:          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
+drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_REFERRALS, $config['optReferrals']);
+drivers_ldap.php:          $this->connect=ldap_connect($host, $port);
+drivers_ldap.php:          $this->connect=ldap_connect($host);
+drivers_ldap.php:      if (! $this->connect){
+drivers_ldap.php:          $c->messages[] = sprintf(translate( 'drivers_ldap : Unable to connect to LDAP with port %s on host %s'), $port, $host );
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        if (!ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Failed to set LDAP to use protocol version 3, TLS not supported');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        if (!ldap_start_tls($this->connect)) {
+drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Could not start TLS: ldap_start_tls() failed');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_read';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_read';
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_list';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_search';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+drivers_ldap.php:      if (!ldap_bind($this->connect, (isset($config['bindDN']) ? $config['bindDN'] : null), (isset($config['passDN']) ? $config['passDN'] : null) ) ){
+drivers_ldap.php:          $c->messages[] = i18n( 'drivers_ldap : Unable to bind to LDAP - check your configuration for bindDN and passDN, and that your LDAP server is reachable');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:      $this->valid = true;
+drivers_ldap.php:      $this->baseDNUsers  = is_string($config['baseDNUsers']) ? array($config['baseDNUsers']) : $config['baseDNUsers'];
+drivers_ldap.php:      $this->filterUsers  = (isset($config['filterUsers'])  ? $config['filterUsers']  : null);
+drivers_ldap.php:      $this->baseDNGroups = is_string($config['baseDNGroups']) ? array($config['baseDNGroups']) : $config['baseDNGroups'];
+drivers_ldap.php:      $this->filterGroups = (isset($config['filterGroups']) ? $config['filterGroups'] : null);
+drivers_ldap.php:    $host=$c->authenticate_hook['config']['host'];    
+drivers_ldap.php:    $port=$c->authenticate_hook['config']['port'];    
+drivers_ldap.php:          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+drivers_ldap.php:          $this->valid=false;                                                                                   
+drivers_ldap.php:          $c->messages[] = sprintf(i18n( "drivers_ldap : Unable to connect to LDAP with port %s on host %s"), $port,$host );
+drivers_ldap.php:          $this->valid=false;                                                                                               
+drivers_ldap.php:    $baseDNUsers  = $c->authenticate_hook['config']['baseDNUsers'];                                                         
+drivers_ldap.php:    $query = $this->ldap_query_all;
+drivers_ldap.php:    foreach($this->baseDNGroups as $baseDNGroups) {
+drivers_ldap.php:      $entry = $query($this->connect,$baseDNGroups,$this->filterGroups,$attributes);
+drivers_ldap.php:      if (!ldap_first_entry($this->connect,$entry)) {
+drivers_ldap.php:        $c->messages[] = sprintf(translate('Error NoGroupFound with filter >%s<, attributes >%s< , dn >%s<'),
+drivers_ldap.php:                                 $this->filterGroups,
+drivers_ldap.php:      for($i = ldap_first_entry($this->connect,$entry);
+drivers_ldap.php:          $i && $arr = ldap_get_attributes($this->connect,$i);
+drivers_ldap.php:          $i = ldap_next_entry($this->connect,$i) ) {
+drivers_ldap.php:    $query = $this->ldap_query_one;
+drivers_ldap.php:    foreach($this->baseDNUsers as $baseDNUsers) {
+drivers_ldap.php:      $entry = $query($this->connect, $baseDNUsers, $filter, $attributes);
+drivers_ldap.php:      if (ldap_first_entry($this->connect,$entry) )
+drivers_ldap.php:    if ( !ldap_first_entry($this->connect, $entry) ){
+drivers_ldap.php:    $dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
+drivers_ldap.php:    if ( isset($c->authenticate_hook['config']['i_use_mode_kerberos']) && $c->authenticate_hook['config']['i_use_mode_kerberos'] == "i_know_what_i_am_doing") {
+drivers_ldap.php:      if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
+drivers_ldap.php:    $i = ldap_first_entry($this->connect,$entry);
+drivers_ldap.php:    $arr = ldap_get_attributes($this->connect,$i);
+drivers_ldap.php:    $ldapDrivers =& new ldapDrivers($c->authenticate_hook['config']);
+drivers_ldap.php:  if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+drivers_ldap.php:    foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+drivers_ldap.php:        $usr->{$field} =  $value;
+drivers_ldap.php:        dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+drivers_ldap.php:      $usr->{$field} =  $ldap_values[$value];
+drivers_ldap.php:      dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+drivers_ldap.php:  if ( !$ldapDriver->valid ) {
+drivers_ldap.php:  $mapping = $c->authenticate_hook['config']['mapping_field'];
+drivers_ldap.php:  if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
+drivers_ldap.php:    $filter_munge = $ldapDriver->filterUsers;
+drivers_ldap.php:  else if ( isset($ldapDriver->filterUsers) && $ldapDriver->filterUsers != '' ) {
+drivers_ldap.php:    $filter_munge = "($ldapDriver->filterUsers)";
+drivers_ldap.php:  $valid = $ldapDriver->requestUser( $filter, $attributes, $username, $password );
+drivers_ldap.php:  foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:  $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:    $db_timestamp = $usr->updated;
+drivers_ldap.php:    $db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
+drivers_ldap.php:  if($ldapDriver->valid){
+drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['group_mapping_field'];
+drivers_ldap.php:    $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes);
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_group = $qry->Fetch()) {
+drivers_ldap.php:      $db_groups[$db_group->group_name] = $db_group->group_name;
+drivers_ldap.php:      $db_group_members[$db_group->group_name][] = $db_group->member_name;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating groups : %s'),join(', ',$groups_to_create));
+drivers_ldap.php:        if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+drivers_ldap.php:          foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+drivers_ldap.php:              $usr->{$field} =  $value;
+drivers_ldap.php:              dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+drivers_ldap.php:        $user->user_no = 0;
+drivers_ldap.php:            $user->{$field} =  $ldap_values[$value];
+drivers_ldap.php:            dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+drivers_ldap.php:        if ($user->fullname=="") {
+drivers_ldap.php:          $user->fullname = $group;
+drivers_ldap.php:        if ($user->displayname=="") {
+drivers_ldap.php:          $user->displayname = $group;
+drivers_ldap.php:        $user->username = $group;
+drivers_ldap.php:        $user->updated = "now";  /** @todo Use the 'updated' timestamp from LDAP for groups too */
+drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- adding users %s to group : %s'),join(',',$ldap_groups_info[$group][$mapping['members']]),$group);
+drivers_ldap.php:          $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- updating groups : %s'),join(', ',$groups_to_update));
+drivers_ldap.php:          $c->messages[] = sprintf(i18n('- adding %s to group : %s'),join(', ', $add_users ), $group);
+drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:          $c->messages[] = sprintf(i18n('- removing %s from group : %s'),join(', ', $remove_users ), $group);
+drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivate groups : %s'),join(', ',$groups_to_deactivate));
+drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:  if($ldapDriver->valid){
+drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['mapping_field'];
+drivers_ldap.php:    $ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+drivers_ldap.php:      $db_users[] = $db_user->username;
+drivers_ldap.php:      $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating record for users :  %s'),join(', ',$users_to_create));
+drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:      if ( isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v]) ) continue;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivating users : %s'),join(', ',$users_to_deactivate));
+drivers_ldap.php:      $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:        $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- updating user records : %s'),join(', ',$users_to_update));
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- nothing done on : %s'),join(', ', $users_nothing_done));
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+drivers_ldap.php:      $admins = $db_user->admins;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('Warning: there are no active admin users, you should fix this before logging out.'));
+drivers_pwauth_pam.php:require_once("auth-functions.php");
+drivers_pwauth_pam.php:  /**#@-*/
+drivers_pwauth_pam.php:    $this->__construct($config);
+drivers_pwauth_pam.php:      $c->messages[] =
+drivers_pwauth_pam.php:      $this->valid=false;
+drivers_pwauth_pam.php:  $program = $c->authenticate_hook['config']['path'];
+drivers_pwauth_pam.php:  $email_base = $c->authenticate_hook['config']['email_base'];
+drivers_squid_pam.php:require_once("auth-functions.php");
+drivers_squid_pam.php:  /**#@-*/
+drivers_squid_pam.php:      $this->__construct($config);
+drivers_squid_pam.php:          $c->messages[] = sprintf(i18n( 'drivers_squid_pam : Unable to find %s file'), $config );
+drivers_squid_pam.php:          $this->valid=false;
+drivers_squid_pam.php:  $cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth";
+drivers_squid_pam.php:              'email' => $username . "@" . $c->authenticate_hook['config']['email_base'],
+freebusy-functions.php:include_once('RRule-v2.php');
+freebusy-functions.php:  //if ( !isset($bin_privs) ) $bin_privs = $request->Privileges();
+freebusy-functions.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+freebusy-functions.php:     $request->DoResponse( 501, 'Database error');
+freebusy-functions.php: // $params = array( ':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
+freebusy-functions.php: // if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+freebusy-functions.php: // if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+freebusy-functions.php: //   while( $calendar_object = $qry->Fetch() ) {
+freebusy-functions.php: //     if ( $calendar_object->status == 'TENTATIVE' ) {
+freebusy-functions.php: //       $extra = ';BUSY-TENTATIVE';
+freebusy-functions.php: //     dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+freebusy-functions.php: //     $ics = new vComponent($calendar_object->caldav_data);
+freebusy-functions.php: //     $expansion = $expanded->GetComponents( array('VEVENT'=>true,'VTODO'=>true,'VJOURNAL'=>true) );
+freebusy-functions.php://        printf( "Type: %s\n", $v->GetType());
+freebusy-functions.php://        echo "-----------------------------------------------------\n";
+freebusy-functions.php: //       $start_date = $v->GetProperty('DTSTART');
+freebusy-functions.php: //       $start_date = new RepeatRuleDateTime($start_date->Value());
+freebusy-functions.php: //       $duration = $v->GetProperty('DURATION');
+freebusy-functions.php: //       $duration = ( !isset($duration) ? 'P1D' : $duration->Value());
+freebusy-functions.php: //      $end_date->modify( $duration );
+freebusy-functions.php: //       $thisfb = $start_date->UTC() .'/'. $end_date->UTC() . $extra;
+freebusy-functions.php:    if ( $qry->Exec("POST",__LINE__,__FILE__) && $qry->rows() > 0 ) {                                                                                                                                                           
+freebusy-functions.php:      while( $calendar_object = $qry->Fetch() ) {                                                                                                                                                                             
+freebusy-functions.php:        if ( $calendar_object->is_public == 1 ) {
+freebusy-functions.php:           if ( $calendar_object->cal_status == 'A' ) {
+freebusy-functions.php:           elseif ( $calendar_object->cal_status == 'U' ) {
+freebusy-functions.php:              $extra = ';BUSY-UNAVAILABLE';
+freebusy-functions.php:           elseif ( $calendar_object->cal_status == 'T' ) {
+freebusy-functions.php:              $extra = ';BUSY-TENTATIVE';
+freebusy-functions.php:           switch ( $calendar_object->cal_type ) {                                                                                                                                                                            
+freebusy-functions.php:                     dbg_error_log( "POST", " FreeBusy: nao recorrente: %s, %s", $calendar_object->datetime, $calendar_object->edatetime );                                                                                   
+freebusy-functions.php:                     $DT_START = $dateTime->format("U");                                                                                                                                                                      
+freebusy-functions.php:                     $DT_END = $dateTime_end->format("U");                                                                                                                                                                    
+freebusy-functions.php:                     if ( $calendar_object->datetime >= $DT_START && $calendar_object->edatetime <= $DT_END)                                                                                                                  
+freebusy-functions.php:                          $busydate_s = dataSegundosParaT($calendar_object->datetime,1);                                                                                                                                      
+freebusy-functions.php:                          $busydate_e = dataSegundosParaT($calendar_object->edatetime,1);                                                                                                                                     
+freebusy-functions.php:                          $busydate_s = dataSegundosParaT($calendar_object->datetime,1);                                                                                                                                      
+freebusy-functions.php:                          $busydate_e = dataSegundosParaT($calendar_object->edatetime,1);                                                                                                                                     
+freebusy-functions.php:                     dbg_error_log( "POST", " FreeBusy: recorrente: %s, %s", $calendar_object->datetime, $calendar_object->edatetime );
+freebusy-functions.php:                     $DT_START = $dateTime->format("U");                                                                               
+freebusy-functions.php:                     $DT_END = $dateTime_end->format("U");                                                                             
+freebusy-functions.php:                     $hora_extra = $dateTime->format("G") * 3600;                                                                      
+freebusy-functions.php:                     $difer  =  $calendar_object->edatetime - $calendar_object->datetime;                                              
+freebusy-functions.php:                     $qryp = new PgQuery( "SELECT * from phpgw_cal_repeats where cal_id = ?  ", $calendar_object->cal_id);             
+freebusy-functions.php:                     if ( $qryp->Exec("POST",__LINE__,__FILE__) && $qryp->rows > 0 ) {                                                 
+freebusy-functions.php:                        $calendar_repeats = $qryp->Fetch();                                                                            
+freebusy-functions.php:                        if ( $calendar_repeats->recur_enddate >= $DT_START && $calendar_repeats->recur_enddate <= $DT_END )            
+freebusy-functions.php:                         { $next_cal = date_create($calendar_object->datetime);                                                        
+freebusy-functions.php:                          $data_diferencial =$DT_START - $calendar_object->datetime;                                                   
+freebusy-functions.php:                          // while ($data_incrementa <= $calendar_repeats->recur_enddate)                                              
+freebusy-functions.php:                           $intervalo = ($calendar_repeats->recur_interval == 0) ? "1" : "$calendar_repeats->recur_interval";          
+freebusy-functions.php:                               switch ( $calendar_repeats->recur_type )                                                                
+freebusy-functions.php:                                          $data_incrementa = $DT_START  - $soma;                                                       
+freebusy-functions.php:                                          while ($data_incrementa <= $calendar_repeats->recur_enddate)                                 
+freebusy-functions.php:                                         $data_incrementa = $DT_START - $soma;                                                         
+freebusy-functions.php:                                         while ($data_incrementa <= $calendar_repeats->recur_enddate)                                  
+freebusy-functions.php:                                            $difer_semana = $DT_START - $data_incrementa;                                              
+freebusy-functions.php:                                                $semana = str_split(converte_semana($calendar_repeats->recur_data));   
+freebusy-functions.php:                                                   $week = $dia_semana->format("w") + 1;                               
+freebusy-functions.php:                                                   $diferenca = $semana[$i] - $week;                                   
+freebusy-functions.php:                                          $data_incrementa = $calendar_object->datetime;                               
+freebusy-functions.php:                                          while ($data_incrementa <= $calendar_repeats->recur_enddate)                 
+freebusy-functions.php:                                              $dias_mes = $mes_data->format("t");                                      
+freebusy-functions.php:                                              $dias_mes = $mes_data->format("t");                                      
+freebusy-functions.php:                                              $i--;                                                                    
+freebusy-functions.php:                                          $obj_mes = dataSegundosParaT($calendar_object->datetime,1);                  
+freebusy-functions.php:                                          $mes = $data_mes->format("m");                                               
+freebusy-functions.php:                                          $ano = $data_mes->format("Y");                                               
+freebusy-functions.php:                                          // $semana_mes = $data_mes->format("W") - $dia_primeiro->format("W");        
+freebusy-functions.php:                                          $difer_semana = $calendar_object->datetime - $dia_primeiro->format("U");     
+freebusy-functions.php:                                          $dia_semana = $data_mes->format("D");                                        
+freebusy-functions.php:                                               $final = "-1 week $dia_semana";                                         
+freebusy-functions.php:                                          $data_incrementa = $calendar_object->datetime;                               
+freebusy-functions.php:                                        $obj_mes = dataSegundosParaT($calendar_object->datetime,1);
+freebusy-functions.php:                                        $mes = $date_mes->format("m");                             
+freebusy-functions.php:                                        $ano = $date_mes->format("Y");                             
+freebusy-functions.php:                                        $dia = $date_mes->format("d");                             
+freebusy-functions.php:                                        $hora = $date_mes->format("G");                            
+freebusy-functions.php:                                        $minuto = $date_mes->format("i");                          
+freebusy-functions.php:                                        $data_incrementa = $calendar_object->datetime ;            
+freebusy-functions.php:                                        while ($data_incrementa <= $calendar_repeats->recur_enddate)
+freebusy-functions.php:                                             $data_incrementa =  date("U",strtotime("$ano-$mes-$dia +1 year"));      
+freebusy-functions.php:                          { if ( $calendar_repeats->recur_enddate >= $DT_START || $calendar_repeats->recur_enddate == 0 )
+freebusy-functions.php:                              {$next_cal = date_create($calendar_object->datetime);                                                        
+freebusy-functions.php:                                $data_diferencial =$DT_START - $calendar_object->datetime;                                                   
+freebusy-functions.php:                               // while ($data_incrementa <= $calendar_repeats->recur_enddate)                                               
+freebusy-functions.php:                               $intervalo = ($calendar_repeats->recur_interval == 0) ? "1" : "$calendar_repeats->recur_interval";            
+freebusy-functions.php:                               switch ( $calendar_repeats->recur_type )                                                                      
+freebusy-functions.php:                                          //$hora_extra = $dateTime->format("G") * 3600;                                                     
+freebusy-functions.php:                                          $data_incrementa = $DT_START - $soma;                                                                             
+freebusy-functions.php:                                         $data_incrementa = $DT_START - $soma;                                                                               
+freebusy-functions.php:                                            $difer_semana = $DT_START - $data_incrementa;                                                                    
+freebusy-functions.php:                                                $semana = str_split(converte_semana($calendar_repeats->recur_data));                                              
+freebusy-functions.php:                                                   $week = $dia_semana->format("w") + 1;                                                                          
+freebusy-functions.php:                                                   $diferenca = $semana[$i] - $week;                                                                              
+freebusy-functions.php:                                          $data_incrementa = $calendar_object->datetime;                                                                          
+freebusy-functions.php:                                              $dias_mes = $mes_data->format("t");                                                                                 
+freebusy-functions.php:                                              $dias_mes = $mes_data->format("t");                                                                                 
+freebusy-functions.php:                                              $i--;                                                                                                               
+freebusy-functions.php:                                          $obj_mes = dataSegundosParaT($calendar_object->datetime,1);                                                             
+freebusy-functions.php:                                          $mes = $data_mes->format("m");                                                                                          
+freebusy-functions.php:                                          $ano = $data_mes->format("Y");                                                                                          
+freebusy-functions.php:                                          //$semana_mes = $data_mes->format("W") - $dia_primeiro->format("W");                                                    
+freebusy-functions.php:                                          $difer_semana = $calendar_object->datetime - $dia_primeiro->format("U");                                                
+freebusy-functions.php:                                          $dia_semana = $data_mes->format("D");                                                                                   
+freebusy-functions.php:                                               $final = "-1 week $dia_semana";                                                                                    
+freebusy-functions.php:                                          $data_incrementa = $calendar_object->datetime;                                                                          
+freebusy-functions.php:                                        $obj_mes = dataSegundosParaT($calendar_object->datetime,1);
+freebusy-functions.php:                                        $mes = $date_mes->format("m");                             
+freebusy-functions.php:                                        $ano = $date_mes->format("Y");                             
+freebusy-functions.php:                                        $dia = $date_mes->format("d");                             
+freebusy-functions.php:                                        $hora = $date_mes->format("G");                                              
+freebusy-functions.php:                                        $minuto = $date_mes->format("i");                                            
+freebusy-functions.php:                                        $data_incrementa = $calendar_object->datetime ;                              
+freebusy-functions.php:                                              $data_incrementa =  date("U",strtotime("$ano-$mes-$dia +1 year"));       
+freebusy-functions.php:                                              $i--;                                                                    
+freebusy-functions.php:                 //  dbg_error_log( "POST", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+freebusy-functions.php:  $freebusy->SetType('VFREEBUSY');
+freebusy-functions.php:  $freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
+freebusy-functions.php:  $freebusy->AddProperty('DTSTART', $range_start->UTC());
+freebusy-functions.php:  $freebusy->AddProperty('DTEND', $range_end->UTC());
+freebusy-functions.php:    $freebusy->AddProperty( 'FREEBUSY', $text[0], (isset($text[1]) ? array('FBTYPE' => $text[1]) : null) );
+freebusy-functions.php:        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+freebusy-functions.php:        //$nova = substr($retorna, 0, -1);
+HTTPAuthSession.php:  * User e-mail
+HTTPAuthSession.php:  /**#@-*/
+HTTPAuthSession.php:    if ( isset($c->http_auth_mode) && $c->http_auth_mode == "Digest" ) {
+HTTPAuthSession.php:      $this->DigestAuthSession();
+HTTPAuthSession.php:      $this->BasicAuthSession();
+HTTPAuthSession.php:  * @param string $auth_header The WWW-Authenticate header details.
+HTTPAuthSession.php:      $auth_header = sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name);
+HTTPAuthSession.php:    header('Content-type: text/plain; ; charset="utf-8"' );
+HTTPAuthSession.php:    else if ( isset($c->authenticate_hook['server_auth_type']) && $c->authenticate_hook['server_auth_type'] == $_SERVER['AUTH_TYPE']
+HTTPAuthSession.php:      if ( ! isset($c->authenticate_hook['call']) ) {
+HTTPAuthSession.php:        $c->authenticate_hook['call'] = 'auth_external';
+HTTPAuthSession.php:      if ( $u = $this->CheckPassword( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
+HTTPAuthSession.php:        $this->AssignSessionDetails($u);
+HTTPAuthSession.php:    if ( isset($c->allow_unauthenticated) && $c->allow_unauthenticated ) {
+HTTPAuthSession.php:      $this->user_no = -1;
+HTTPAuthSession.php:      $this->username = 'guest';
+HTTPAuthSession.php:      $this->fullname = 'Unauthenticated User';
+HTTPAuthSession.php:      $this->email = 'invalid';
+HTTPAuthSession.php:    $this->AuthFailedResponse();
+HTTPAuthSession.php:      if ( $data = $this->ParseDigestHeader($_SERVER['PHP_AUTH_DIGEST']) ) {
+HTTPAuthSession.php:        $A1 = md5($data['username'] . ':' . $c->system_name . ':' . $user_password);
+HTTPAuthSession.php:          $this->AssignSessionDetails($u);
+HTTPAuthSession.php:    $opaque = md5($c->system_name);
+HTTPAuthSession.php:    $this->AuthFailedResponse( sprintf('WWW-Authenticate: Digest realm="%s", qop="auth,auth-int", nonce="%s", opaque="%s"', $c->system_name, $nonce, $opaque ) );
+HTTPAuthSession.php:  *  - largely sourced from the PHP documentation
+HTTPAuthSession.php:    if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && function_exists($c->authenticate_hook['call']) ) {
+HTTPAuthSession.php:      *   - Accept a username / password
+HTTPAuthSession.php:      *   - Confirm the username / password are correct
+HTTPAuthSession.php:      *   - Create (or update) a 'usr' record in our database
+HTTPAuthSession.php:      *   - Return the 'usr' record as an object
+HTTPAuthSession.php:      *   - Return === false when authentication fails
+HTTPAuthSession.php:      *   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+HTTPAuthSession.php:      $hook_response = call_user_func( $c->authenticate_hook['call'], $username, $password );
+HTTPAuthSession.php:      if (isset($c->authenticate_hook['optional']) && $c->authenticate_hook['optional']) {
+HTTPAuthSession.php:      dbg_error_log( "BasicAuth", ":CheckPassword: Name:%s, Pass:%s, File:%s, Active:%s", $username, $password, $usr->password, ($usr->active?'Yes':'No') );
+HTTPAuthSession.php:      if ( $usr->active && session_validate_password( $password, $usr->password ) ) {
+HTTPAuthSession.php:    return ( isset($this->logged_in) && $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
+HTTPAuthSession.php:    $this->roles = array();
+HTTPAuthSession.php:                                array( ':user_no' => $this->user_no) );
+HTTPAuthSession.php:    if ( $qry->Exec('BasicAuth') && $qry->rows() > 0 ) {
+HTTPAuthSession.php:      while( $role = $qry->Fetch() ) {
+HTTPAuthSession.php:        $this->roles[$role->role_name] = true;
+HTTPAuthSession.php:    if ( !isset($u->principal_id) ) {
+HTTPAuthSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+HTTPAuthSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+HTTPAuthSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+HTTPAuthSession.php:        $u = $qry->Fetch();
+HTTPAuthSession.php:      $this->{$k} = $v;
+HTTPAuthSession.php:    $this->GetRoles();
+HTTPAuthSession.php:    $this->logged_in = true;
+HTTPAuthSession.php:    if ( function_exists("awl_set_locale") && isset($this->locale) && $this->locale != "" ) {
+HTTPAuthSession.php:      awl_set_locale($this->locale);
+interactive-page.php:$home_menu->AddOption(translate('Logout'), $c->base_url.'/index.php?logout', translate('Log out of DAViCal') );
+interactive-page.php:$help_menu->AddOption(translate('DAViCal Homepage'),'http://www.davical.org/',translate('The DAViCal Home Page'), false, 6000, true );
+interactive-page.php:$help_menu->AddOption(translate('DAViCal Wiki'),'http://wiki.davical.org/'.$wiki_help,translate('Visit the DAViCal Wiki'), false, 7000, true );
+interactive-page.php:$help_menu->AddOption(translate('Request Feature'),'http://davical.uservoice.com/',translate('Go to the DAViCal Feature Requests'), false, 8000, true );
+interactive-page.php:$help_menu->AddOption(translate('Report Bug'),'http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785',translate('Report a bug in the system'), false, 9000, true );
+interactive-page.php:$user_menu->AddOption(translate('View My Details'),$c->base_url.'/admin.php?action=edit&t=principal&id='.$session->principal_id,translate('View my own principal record'));
+interactive-page.php:$user_menu->AddOption(translate('List Users'),$c->base_url.'/admin.php?action=browse&t=principal&type=1');
+interactive-page.php:$user_menu->AddOption(translate('List Resources'),$c->base_url.'/admin.php?action=browse&t=principal&type=2');
+interactive-page.php:$user_menu->AddOption(translate('List Groups'),$c->base_url.'/admin.php?action=browse&t=principal&type=3');
+interactive-page.php:if ( $session->AllowedTo('Admin' )) {
+interactive-page.php:  $admin_menu->AddOption(translate('Setup'),$c->base_url.'/setup.php',translate('Setup DAViCal') );
+interactive-page.php:  $admin_menu->AddOption(translate('Upgrade Database'),$c->base_url.'/upgrade.php',translate('Upgrade DAViCal database schema') );
+interactive-page.php:  $admin_menu->AddOption(translate('Import Calendars'),$c->base_url.'/tools.php',translate('Operations on your calendar') );
+interactive-page.php:  $user_menu->AddOption(translate('Inactive Principals'),$c->base_url.'/admin.php?action=browse&t=principal&active=f');
+interactive-page.php:  $user_menu->AddOption(translate('Create Principal'),$c->base_url.'/admin.php?action=edit&t=principal',translate('Create a new principal (i.e. a new user, resource or group)'));
+interactive-page.php:$main_menu->AddSubMenu($home_menu, translate('Home'), $c->base_url.'/index.php', translate('Home Page'), false, 1000);
+interactive-page.php:$main_menu->AddSubMenu($user_menu, translate('User Functions'), $c->base_url.'/admin.php?action=browse&t=principal&type=1', translate('Browse all users'), false, 2000);
+interactive-page.php:$main_menu->AddSubMenu($admin_menu, translate('Administration'), $c->base_url.'/index.php', translate('Administration'), false, 3000);
+interactive-page.php:$main_menu->AddSubMenu($help_menu, translate('Help'), $c->base_url.'/help.php',translate('Help on the current screen'), false, 9000);
+iSchedule.php:*  - verifying Domain Key signatures
+iSchedule.php:*  - delivering remote scheduling requests to local users inboxes
+iSchedule.php:*  - Utility functions which we can use to decide whether this
+iSchedule.php:		$this->selector = 'cal';
+iSchedule.php:		if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
+iSchedule.php:			$this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
+iSchedule.php:		$dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
+iSchedule.php:			$this->dk = $dkim [ 0 ] [ 'txt' ];
+iSchedule.php:			$this->failed = true;
+iSchedule.php:		if ( $this->failed == true )
+iSchedule.php:		$clean = preg_replace ( '/[\s\t]*([;=])[\s\t]*/', '$1', $this->dk );
+iSchedule.php:		$this->parsed = array();
+iSchedule.php:				$this->parsed [ $key ] = $value;
+iSchedule.php:				$this->parsed_ignored [ $key ] = $value;
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		if ( isset ( $this->parsed [ 's' ] ) )
+iSchedule.php:			if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) )
+iSchedule.php:		if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' )
+iSchedule.php:		if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) )
+iSchedule.php:			if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
+iSchedule.php:				$this->failOnError = false;
+iSchedule.php:			if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
+iSchedule.php:				$this->subdomainsOK = false;
+iSchedule.php:		if ( isset ( $this->parsed [ 'g' ] ) )
+iSchedule.php:			$this->remote_user_rule = $this->parsed [ 'g' ];
+iSchedule.php:		if ( isset ( $this->parsed [ 'p' ] ) )
+iSchedule.php:			$data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( preg_replace ( '/_/', '', $this->parsed [ 'p' ] ), 64 )) . "\n-----END PUBLIC KEY-----"; 
+iSchedule.php:			$this->remote_public_key = $data;
+iSchedule.php:		$this->failed = false;
+iSchedule.php:		$this->remote_ssl = false;
+iSchedule.php:		$r = dns_get_record ( '_ischedules._tcp.' . $this->domain , DNS_SRV );
+iSchedule.php:			$this->remote_ssl = true;
+iSchedule.php:			$r = dns_get_record ( '_ischedule._tcp.' . $this->domain , DNS_SRV );
+iSchedule.php:		elseif ( $this->try_anyway == true )
+iSchedule.php:				$remote_server = $this->domain;
+iSchedule.php:		$this->remote_server = $remote_server;
+iSchedule.php:		$this->remote_port = $remote_port;
+iSchedule.php:		$remote_capabilities = file_get_contents ( 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' . 
+iSchedule.php:			$this->remote_server . ':' . $this->remote_port . 
+iSchedule.php:			'/.well-known/ischedule?query=capabilities' );
+iSchedule.php:		$xmltree = BuildXMLTree( $request->xml_tags, $position);
+iSchedule.php:			  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+iSchedule.php:		$dk['s'] = $this->selector;
+iSchedule.php:		$dk['d'] = $this->domain;
+iSchedule.php:		$dk['c'] = 'simple-http';
+iSchedule.php:		 //a=rsa-sha1; d=caveman.name; s=cal; c=simple-http; q=dns/txt; h=Originator:Recipient:Host:Content-Type; b
+iSchedule.php:	* @param string $sig the value of the DKIM-Signature header
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		// the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
+iSchedule.php:		// but it does define the value, it should be simple-http.  RFC4871 also defines two methods
+iSchedule.php:		if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
+iSchedule.php:			$this->body_cannon = $matches[2];
+iSchedule.php:			$this->body_cannon = $matches[1];
+iSchedule.php:		$this->header_cannon = $matches[1];
+iSchedule.php:		if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) 
+iSchedule.php:		$this->remote_server = $dkim['d'];
+iSchedule.php:			$this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
+iSchedule.php:		$this->remote_selector = $dkim['s'];
+iSchedule.php:		$this->signed_headers = preg_split ( '/:/', $dkim['h'] );
+iSchedule.php:		foreach ( $this->signed_headers as $h )
+iSchedule.php:			if ( ( ! isset ( $_SERVER['HTTP_' . strtr ( strtoupper ( $h ), '-', '_' ) ] ) &&
+iSchedule.php:             ! isset ( $_SERVER[ strtr ( strtoupper ( $h ), '-', '_' ) ] ) ) 
+iSchedule.php:				  || strtolower ( $h ) == 'dkim-signature' )
+iSchedule.php:			$this->signed_length = $dkim['l'];
+iSchedule.php:		$this->failed = false;
+iSchedule.php:		$this->DKSig = $dkim;
+iSchedule.php:			$this->remote_user = $matches[1];
+iSchedule.php:			$this->domain = $matches[2];
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		foreach ( $this->signed_headers as $h )
+iSchedule.php:			if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
+iSchedule.php:				$signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+iSchedule.php:				$signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+iSchedule.php:		$body = $request->raw_post;
+iSchedule.php:		if ( ! isset ( $this->signed_length ) )
+iSchedule.php:			$this->signed_length = strlen ( $body );
+iSchedule.php:			$body = substr ( $body, 0, $this->signed_length );
+iSchedule.php:		if ( isset ( $this->remote_user_rule ) )
+iSchedule.php:			if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
+iSchedule.php:		$body_hash = base64_encode ( hash ( preg_replace ( '/^.*(sha[1256]+).*/','$1', $this->DKSig['a'] ), $body , true ) );
+iSchedule.php:		if ( $this->DKSig['bh'] != $body_hash )
+iSchedule.php:		$signed .= 'DKIM-Signature: ' . $sig;
+iSchedule.php:		$verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key );
+iSchedule.php:		$this->failed = false;
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature missing') );
+iSchedule.php:    $err = $this->parseDKIM ( $sig );
+iSchedule.php:		if ( $err !== true || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature invalid ' ) . "\n" . $err . "\n" . $sig );
+iSchedule.php:		if ( ! $this->getTxt () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(DNS ERROR)') );
+iSchedule.php:		if ( ! $this->parseTxt () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Parse ERROR)') );
+iSchedule.php:		if ( ! $this->validateKey () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Validation ERROR)') );
+iSchedule.php:		if ( ! $this->verifySignature () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(Signature verification ERROR)') . $this->verifySignature() );
+iSchedule.php:if ( $d->validateRequest ( ) )
+iSchedule.php:	include ( 'caldav-POST.php' );
+log_caldav_action.php:  if ( !isset($c->action_log_name) ) {
+log_caldav_action.php:  $logfile = fopen( $c->action_log_name, "a+" );
+other_translated_strings.php:i18n("Admin <!-- in the sense of 'systems admin' -->");
+other_translated_strings.php:i18n("Group <!-- in the sense of a group of people -->");
+other_translated_strings.php:i18n("Public <!-- in the sense of being available to all users -->");
+other_translated_strings.php:i18n("Resource <!-- a thing which might be booked: a room, a carpark, a projector... -->");
+other_translated_strings.php:i18n("No. <!-- short for 'Number' -->");
+other_translated_strings.php:i18n("No <!-- not 'Yes' -->");
+other_translated_strings.php:i18n("Id <!-- short for 'Identifier' -->");
+other_translated_strings.php:i18n("forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option allowing people to log in automatically in future -->");
+page-footer.php:  if ( isset($c->scripts) && is_array($c->scripts) ) {
+page-footer.php:    foreach ( $c->scripts AS $script ) {
+page-footer.php:  if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+page-footer.php:    $script_time = microtime(true) - $c->script_start_time;
+page-footer.php:                        $_SERVER['REQUEST_METHOD'], 200, $script_time, $c->total_query_time, $_SERVER['REQUEST_URI']);
+page-header.php:if ( !isset($c->page_title) ) {
+page-header.php:  $c->page_title = translate('DAViCal CalDAV Server');
+page-header.php:  if ( $GLOBALS['session']->AllowedTo('Admin') || $GLOBALS['session']->AllowedTo('Support') ) {
+page-header.php:  return ' <a class="help" href="'.$c->base_url.'/help.php?h='.$help_topic.'" title="'.translate('Show help on').' &39;'.$help_topic.'&39;" target="_new">['.$display_url.']</a> ';
+page-header.php:  header( 'Content-type: text/html; charset="utf-8"' );
+page-header.php:  echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+page-header.php:<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+page-header.php:<title>$c->page_title</title>
+page-header.php:  foreach ( $c->stylesheets AS $stylesheet ) {
+page-header.php:  if ( isset($c->local_styles) ) {
+page-header.php:    foreach ( $c->local_styles AS $stylesheet ) {
+page-header.php:  if ( isset($c->print_styles) ) {
+page-header.php:    foreach ( $c->print_styles AS $stylesheet ) {
+page-header.php:  if ( isset($main_menu) ) echo $main_menu->RenderAsCSS();
+page-header.php:  if ( isset($related_menu) && $related_menu->Size() > 0 ) {
+page-header.php:    echo $related_menu->Render( true );
+page-header.php:  if ( isset($c->messages) && is_array($c->messages) && count($c->messages) > 0 ) {
+page-header.php:    foreach( $c->messages AS $i => $msg ) {
+Privileges:always.php.in:$c->script_start_time = microtime(true);
+Privileges:always.php.in:// An ultra-simple exception handler to catch errors that occur
+Privileges:always.php.in:  echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
+Privileges:always.php.in:  $trace = array_reverse($e->getTrace());
+Privileges:always.php.in:$c->sysabbr     = 'davical';
+Privileges:always.php.in:$c->admin_email = 'admin@davical.example.com';
+Privileges:always.php.in:$c->system_name = 'DAViCal CalDAV Server';
+Privileges:always.php.in:$c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+Privileges:always.php.in:$c->save_time_zone_defs = true;
+Privileges:always.php.in:$c->collections_always_exist = false;
+Privileges:always.php.in:$c->allow_get_email_visibility = false;
+Privileges:always.php.in:$c->permission_scan_depth = 2;
+Privileges:always.php.in:$c->expand_pdo_parameters = true;
+Privileges:always.php.in:$c->home_calendar_name = 'home';
+Privileges:always.php.in:$c->enable_row_linking = true;
+Privileges:always.php.in:$c->enable_scheduling = false;
+Privileges:always.php.in:$c->http_auth_mode = 'Basic';
+Privileges:always.php.in:// $c->default_locale = array('es_MX', 'es_AR', 'es', 'pt');  // An array of locales to try, or just a single locale
+Privileges:always.php.in:// $c->local_tzid = 'Pacific/Auckland';  // Perhaps we should read from /etc/timezone - I wonder how standard that is?
+Privileges:always.php.in:$c->default_locale = 'en';
+Privileges:always.php.in:$c->locale_path = '../locale';
+Privileges:always.php.in:$c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
+Privileges:always.php.in:$c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
+Privileges:always.php.in:$c->default_privileges = array('read-free-busy', 'schedule-deliver');
+Privileges:always.php.in:$c->stylesheets = array( $c->base_url.'/davical.css' );
+Privileges:always.php.in:$c->images      = $c->base_url . '/images';
+Privileges:always.php.in:$c->template_usr = array( 'active' => true,
+Privileges:always.php.in:                          'email_ok' => date('Y-m-d')
+Privileges:always.php.in:$c->hide_TODO = true;                      // VTODO only visible to collection owner
+Privileges:always.php.in:$c->readonly_webdav_collections = true;    // WebDAV access is readonly
+Privileges:always.php.in:$c->total_query_time = 0;
+Privileges:always.php.in:$c->dbg = array();
+Privileges:always.php.in:  @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration.  Using IP address meanhwhile..." );
+Privileges:always.php.in:$c->protocol_server_port = sprintf( '%s://%s%s',
+Privileges:always.php.in:$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
+Privileges:always.php.in:if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+Privileges:always.php.in:  include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+Privileges:always.php.in:else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+Privileges:always.php.in:  include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+Privileges:always.php.in:if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
+Privileges:always.php.in:else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
+Privileges:always.php.in:         $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
+Privileges:always.php.in:putenv("LANG=". $c->default_locale);
+Privileges:always.php.in:awl_set_locale($c->default_locale);
+Privileges:always.php.in:init_gettext( 'davical', $c->locale_path );
+Privileges:always.php.in:$c->code_version = 0;
+Privileges:always.php.in:$c->version_string = '0.9.8.3'; // The actual version # is replaced into that during the build /release process
+Privileges:always.php.in:if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
+Privileges:always.php.in:  $c->code_major = $matches[1];
+Privileges:always.php.in:  $c->code_minor = $matches[2];
+Privileges:always.php.in:  $c->code_patch = $matches[3];
+Privileges:always.php.in:  $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
+Privileges:always.php.in:  dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
+Privileges:always.php.in:  header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
+Privileges:always.php.in:$_SERVER['SERVER_NAME'] = $c->domain_name;
+Privileges:always.php.in:$c->want_dbversion = array(1,2,8);
+Privileges:always.php.in:$c->schema_version = 0;
+Privileges:always.php.in:if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
+Privileges:always.php.in:  $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
+Privileges:always.php.in:  $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
+Privileges:always.php.in:  $c->schema_major = $row->schema_major;
+Privileges:always.php.in:  $c->schema_minor = $row->schema_minor;
+Privileges:always.php.in:  $c->schema_patch = $row->schema_patch;
+Privileges:always.php.in:  if ( $c->schema_version < $c->wanted_version ) {
+Privileges:always.php.in:    $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
+Privileges:always.php.in:             $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
+Privileges:always.php.in:  if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) $qry->QDo('SET TIMEZONE TO \'Pacific/Auckland\'');
+Privileges:always.php.in:  if ( isset($session->principal_id) ) {
+Privileges:always.php.in:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:always.php.in:  if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
+Privileges:always.php.in:    if ( isset($session->principal_id) ) {
+Privileges:always.php.in:      $_known_users_name[$row->username]      = $row;
+Privileges:always.php.in:      $_known_users_id[$row->user_no]         = $row;
+Privileges:always.php.in:      $_known_users_pid[$row->principal_id]   = $row;
+Privileges:always.php.in:* Return a user record identified by e-mail address, caching it for any subsequent lookup
+Privileges:always.php.in:    /** We don't actually maintain a cache-access by e-mail, since it's rare */
+Privileges:always.php.in:      if ( strtolower($email) == strtolower($v->email) ) return $v;
+Privileges:always.php.in:    case 203:  $ans = 'Non-Authoritative Information';        break;
+Privileges:always.php.in:    case 207:  $ans = 'Multi-Status';                         break;
+Privileges:always.php.in:    case 414:  $ans = 'Request-URI Too Long';                 break;
+Privileges:always.php.in:  if ( ! isset($c->_url_script_path) ) {
+Privileges:always.php.in:    $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
+Privileges:always.php.in:    $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
+Privileges:always.php.in:  $url = $c->_url_script_path;
+Privileges:always.php.in:  if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
+Privileges:always.php.in:  if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
+Privileges:always.php.in:    if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
+Privileges:always.php.in:      case 'write-properties'                : $out_priv |=     2;  break;
+Privileges:always.php.in:      case 'write-content'                   : $out_priv |=     4;  break;
+Privileges:always.php.in:      case 'read-acl'                        : $out_priv |=    16;  break;
+Privileges:always.php.in:      case 'read-current-user-privilege-set' : $out_priv |=    32;  break;
+Privileges:always.php.in:      case 'write-acl'                       : $out_priv |=   256;  break;
+Privileges:always.php.in:      case 'read-free-busy'                  : $out_priv |=   512;  break;
+Privileges:always.php.in:      case 'schedule-deliver-invite'         : $out_priv |=  1024;  break;
+Privileges:always.php.in:      case 'schedule-deliver-reply'          : $out_priv |=  2048;  break;
+Privileges:always.php.in:      case 'schedule-query-freebusy'         : $out_priv |=  4096;  break;
+Privileges:always.php.in:      case 'schedule-send-invite'            : $out_priv |=  8192;  break;
+Privileges:always.php.in:      case 'schedule-send-reply'             : $out_priv |= 16384;  break;
+Privileges:always.php.in:      case 'schedule-send-freebusy'          : $out_priv |= 32768;  break;
+Privileges:always.php.in:      case 'schedule-deliver'                : $out_priv |=  7168;  break; // 1024 + 2048 + 4096
+Privileges:always.php.in:      case 'schedule-send'                   : $out_priv |= 57344;  break; // 8192 + 16384 + 32768
+Privileges:always.php.in:  if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
+Privileges:always.php.in:  if ( ($raw_bits &  16) != 0 ) $out_priv[] = 'DAV::read-acl';
+Privileges:always.php.in:  if ( ($raw_bits &  32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
+Privileges:always.php.in:  if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
+Privileges:always.php.in:  if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
+Privileges:always.php.in:    if ( ($raw_bits &   2) != 0 ) $out_priv[] = 'DAV::write-properties';
+Privileges:always.php.in:    if ( ($raw_bits &   4) != 0 ) $out_priv[] = 'DAV::write-content';
+Privileges:always.php.in:  if ( $resourcetype == 'schedule-inbox' && ($raw_bits & 7168) != 0 ) {
+Privileges:always.php.in:    if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
+Privileges:always.php.in:    if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
+Privileges:always.php.in:    if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
+Privileges:always.php.in:    if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
+Privileges:always.php.in:  if ( $resourcetype == 'schedule-outbox' && ($raw_bits & 57344) != 0 ) {
+Privileges:always.php.in:    if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
+Privileges:always.php.in:    if ( ($raw_bits &  8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
+Privileges:always.php.in:    if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
+Privileges:always.php.in:    if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
+Privileges:always.php.in:      $xmldoc->NSElement($privilege,$k);
+Privileges:always.php.in:      $privilege->NewElement($k);
+Privileges:auth-functions.php:*   - Accept a username / password
+Privileges:auth-functions.php:*   - Confirm the username / password are correct
+Privileges:auth-functions.php:*   - Create (or update) a 'usr' record in our database
+Privileges:auth-functions.php:*   - Return the 'usr' record as an object
+Privileges:auth-functions.php:*   - Return === false when authentication fails
+Privileges:auth-functions.php:*   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+Privileges:auth-functions.php:*   - This file should be included
+Privileges:auth-functions.php:*   - $c->authenticate_hook['call'] should be set to the name of the plugin
+Privileges:auth-functions.php:*   - $c->authenticate_hook['config'] should be set up with any configuration data for the plugin
+Privileges:auth-functions.php:  if ( ! isset($c->home_calendar_name) || strlen($c->home_calendar_name) == 0 ) return true;
+Privileges:auth-functions.php:  //$calendar_path = $parent_path . $c->home_calendar_name."/";
+Privileges:auth-functions.php:  $dav_etag = md5($usr->user_no . $calendar_path);
+Privileges:auth-functions.php:      ':user_no' => $usr->user_no,
+Privileges:auth-functions.php:      ':displayname' => $usr->fullname,
+Privileges:auth-functions.php:  if ( $qry->Exec() ) {
+Privileges:auth-functions.php:    $c->messages[] = i18n("Home calendar added.");
+Privileges:auth-functions.php:    $c->messages[] = i18n("There was an error writing to the database.");
+Privileges:auth-functions.php:  if ( !isset($usr->user_no) || intval($usr->user_no) == 0 ) {
+Privileges:auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+Privileges:auth-functions.php:    $sequence_value = $qry->Fetch(true);  // Fetch as an array
+Privileges:auth-functions.php:    $usr->user_no = $sequence_value[0];
+Privileges:auth-functions.php:  $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no) );
+Privileges:auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+Privileges:auth-functions.php:    if ( $old = $qry->Fetch() ) {
+Privileges:auth-functions.php:        if ( $old->{$k} != $v ) {
+Privileges:auth-functions.php:          dbg_error_log("Login","User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v );
+Privileges:auth-functions.php:        dbg_error_log("Login","No changes to user record for '%s' - leaving as-is.", $usr->username );
+Privileges:auth-functions.php:        if ( isset($usr->active) && $usr->active == 'f' ) return false;
+Privileges:auth-functions.php:        dbg_error_log("Login","Changes to user record for '%s' - updating.", $usr->username );
+Privileges:auth-functions.php:  if ( $type != 'INSERT' ) $params[':user_no'] = $usr->user_no;
+Privileges:auth-functions.php:  $qry->Exec('Login',__LINE__,__FILE__);
+Privileges:auth-functions.php:  if ( isset($usr->active) && ($usr->active === 'f' || $usr->active === false) ) return false;
+Privileges:auth-functions.php:                          array( ':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username) );
+Privileges:auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+Privileges:auth-functions.php:    CreateHomeCalendar($usr->username);
+Privileges:auth-functions.php:* require_once('auth-functions.php');
+Privileges:auth-functions.php:*  $c->authenticate_hook = array(
+Privileges:auth-functions.php:  $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent'];
+Privileges:auth-functions.php:  if ( isset($c->authenticate_hook['config']['columns']) )
+Privileges:auth-functions.php:    $cols = $c->authenticate_hook['config']['columns'];
+Privileges:auth-functions.php:  if ( isset($c->authenticate_hook['config']['where']) )
+Privileges:auth-functions.php:    $andwhere = ' AND '.$c->authenticate_hook['config']['where'];
+Privileges:auth-functions.php:  $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], ($persistent ? array(PDO::ATTR_PERSISTENT => true) : null));
+Privileges:auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+Privileges:auth-functions.php:    $usr = $qry->Fetch();
+Privileges:auth-functions.php:    if ( session_validate_password( $password, $usr->password ) ) {
+Privileges:auth-functions.php:      if ( isset($usr->active) && $usr->active == 'f' ) return false;
+Privileges:auth-functions.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username) );
+Privileges:auth-functions.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+Privileges:auth-functions.php:        $principal = $qry->Fetch();
+Privileges:caldav-ACL.php:* CalDAV Server - handle ACL method
+Privileges:caldav-ACL.php:$request->NeedPrivilege('DAV::write-acl');
+Privileges:caldav-ACL.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+Privileges:caldav-ACL.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-ACL.php:$resource = new DAVResource( $request->path );
+Privileges:caldav-ACL.php:   (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
+Privileges:caldav-ACL.php:   that an implementation-specific ACL restriction has been violated.
+Privileges:caldav-ACL.php:   (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
+Privileges:caldav-ACL.php:   (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
+Privileges:caldav-ACL.php:   implementation-dependent.  Implementations MUST either report this
+Privileges:caldav-ACL.php:   (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
+Privileges:caldav-ACL.php:   However, ACL-compliant servers MUST support at least one ACE granting
+Privileges:caldav-ACL.php:   (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
+Privileges:caldav-ACL.php:   non-inherited grant ACEs.
+Privileges:caldav-ACL.php:   (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
+Privileges:caldav-ACL.php:   restrictions of the resource include the DAV:grant-only constraint
+Privileges:caldav-ACL.php:   (DAV:no-invert):  The ACL request MUST NOT include a DAV:invert
+Privileges:caldav-ACL.php:   the resource includes the DAV:no-invert constraint (defined in
+Privileges:caldav-ACL.php:   (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
+Privileges:caldav-ACL.php:   (DAV:not-supported-privilege): The ACEs submitted in the ACL request
+Privileges:caldav-ACL.php:   (DAV:missing-required-principal): The result of the ACL request MUST
+Privileges:caldav-ACL.php:   DAV:required-principal XML element in the ACL semantics of that
+Privileges:caldav-ACL.php:   (DAV:recognized-principal): Every principal URL in the ACL request
+Privileges:caldav-ACL.php:   (DAV:allowed-principal): The principals specified in the ACEs
+Privileges:caldav-ACL.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-ACL.php:$aces = $xmltree->GetPath("/DAV::acl/*");
+Privileges:caldav-ACL.php:$grantor = new DAVResource($request->path);
+Privileges:caldav-ACL.php:if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
+Privileges:caldav-ACL.php:if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id');
+Privileges:caldav-ACL.php:else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id');
+Privileges:caldav-ACL.php:else $request->PreconditionFailed(403,'not-supported-privilege','ACLs may only be applied to Principals or Collections');
+Privileges:caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+Privileges:caldav-ACL.php:  $elements = $ace->GetContent();
+Privileges:caldav-ACL.php:  if ( $principal->GetTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetTag());
+Privileges:caldav-ACL.php:  $grant_tag = $grant->GetTag();
+Privileges:caldav-ACL.php:  if ( $grant_tag == 'DAV::deny' )   $request->PreconditionFailed(403,'grant-only');
+Privileges:caldav-ACL.php:  if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
+Privileges:caldav-ACL.php:  if ( $grant->GetTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
+Privileges:caldav-ACL.php:  $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
+Privileges:caldav-ACL.php:    $privilege_names[] = $priv->GetTag();
+Privileges:caldav-ACL.php:  $principal_content = $principal->GetContent();
+Privileges:caldav-ACL.php:  if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
+Privileges:caldav-ACL.php:  switch( $principal_content->GetTag() ) {
+Privileges:caldav-ACL.php:      $principal_property = $principal_content->GetContent();
+Privileges:caldav-ACL.php:      if ( $principal_property[0]->GetTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
+Privileges:caldav-ACL.php:        $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
+Privileges:caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+Privileges:caldav-ACL.php:      $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) );
+Privileges:caldav-ACL.php:      if ( ! $principal->Exists() || !$principal->IsPrincipal() )
+Privileges:caldav-ACL.php:        $request->PreconditionFailed(403,'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
+Privileges:caldav-ACL.php:      $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_id') );
+Privileges:caldav-ACL.php:      if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
+Privileges:caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+Privileges:caldav-ACL.php:      if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
+Privileges:caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+Privileges:caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+Privileges:caldav-ACL.php:      $request->PreconditionFailed(403, 'recognized-principal' );
+Privileges:caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+Privileges:caldav-ACL.php:$request->DoResponse( 200 );
+Privileges:caldav-BIND.php:* CalDAV Server - handle BIND method
+Privileges:caldav-BIND.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-BIND.php:$request->NeedPrivilege('DAV::bind');
+Privileges:caldav-BIND.php:if ( ! $request->IsCollection() ) {
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-into-collection',translate('The BIND Request-URI MUST identify a collection.'));
+Privileges:caldav-BIND.php:$parent_container = $request->path;
+Privileges:caldav-BIND.php:if ( ! $parent->Exists() || $parent->IsSchedulingCollection() ) {
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::method-not-allowed',translate('The BIND method is not allowed at that location.') );
+Privileges:caldav-BIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-BIND.php:$segment = $xmltree->GetElements('DAV::segment');
+Privileges:caldav-BIND.php:$segment = $segment[0]->GetContent();
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::name-allowed',translate('That destination name contains invalid characters.') );
+Privileges:caldav-BIND.php:$href    = $xmltree->GetElements('DAV::href');
+Privileges:caldav-BIND.php:$href    = $href[0]->GetContent();
+Privileges:caldav-BIND.php:if ( $destination->Exists() ) {
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
+Privileges:caldav-BIND.php:if ( !$source->Exists() ) {
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
+Privileges:caldav-BIND.php:if ( $source->IsPrincipal() || !$source->IsCollection() ) {
+Privileges:caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
+Privileges:caldav-BIND.php:    ':target_id'    => $source->GetProperty('collection_id'),
+Privileges:caldav-BIND.php:    ':ticket_id'    => (isset($request->ticket) ? $request->ticket->id() : null),
+Privileges:caldav-BIND.php:    ':parent_container' => $parent->dav_name(),
+Privileges:caldav-BIND.php:    ':session_principal' => $session->principal_id,
+Privileges:caldav-BIND.php:    ':displayname'  => $source->GetProperty('displayname')
+Privileges:caldav-BIND.php:if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
+Privileges:caldav-BIND.php:  $request->DoResponse(201);
+Privileges:caldav-BIND.php:  $request->DoResponse(500,translate('Database Error'));
+Privileges:caldav-client.php:* removed curl - now using fsockopen
+Privileges:caldav-client.php:* changed 2009 by Andres Obrero - Switzerland andres@obrero.ch
+Privileges:caldav-client.php:    $this->user = $user;
+Privileges:caldav-client.php:    $this->pass = $pass;
+Privileges:caldav-client.php:    $this->calendar = $calendar;
+Privileges:caldav-client.php:    $this->headers = array();
+Privileges:caldav-client.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+Privileges:caldav-client.php:      $this->server = $matches[2];
+Privileges:caldav-client.php:      $this->base_url = $matches[5];
+Privileges:caldav-client.php:        $this->protocol = 'ssl';
+Privileges:caldav-client.php:        $this->port = 443;
+Privileges:caldav-client.php:        $this->protocol = 'tcp';
+Privileges:caldav-client.php:        $this->port = 80;
+Privileges:caldav-client.php:        $this->port = intval($matches[4]);
+Privileges:caldav-client.php:  * Adds an If-Match or If-None-Match header
+Privileges:caldav-client.php:    $this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+Privileges:caldav-client.php:    $this->headers[] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+Privileges:caldav-client.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+Privileges:caldav-client.php:    $this->user_agent = $user_agent;
+Privileges:caldav-client.php:  * Add a Content-type: header.
+Privileges:caldav-client.php:    $this->headers[] = "Content-type: $type";
+Privileges:caldav-client.php:        $this->httpResponse = trim($response);
+Privileges:caldav-client.php:        $this->httpResponse = trim(substr($response, 0, $pos));
+Privileges:caldav-client.php:        $this->xmlResponse = trim(substr($response, $pos));
+Privileges:caldav-client.php:      return $this->httpRequest;
+Privileges:caldav-client.php:      return $this->httpResponse;
+Privileges:caldav-client.php:      return $this->xmlRequest;
+Privileges:caldav-client.php:      return $this->xmlResponse;
+Privileges:caldav-client.php:    $headers[] = $this->requestMethod." ". $this->base_url . $relative_url . " HTTP/1.1";
+Privileges:caldav-client.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+Privileges:caldav-client.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+Privileges:caldav-client.php:    foreach( $this->headers as $ii => $head ) {
+Privileges:caldav-client.php:    $headers[] = "Content-Length: " . strlen($this->body);
+Privileges:caldav-client.php:    $headers[] = "User-Agent: " . $this->user_agent;
+Privileges:caldav-client.php:    $this->httpRequest = join("\r\n",$headers);
+Privileges:caldav-client.php:    $this->xmlRequest = $this->body;
+Privileges:caldav-client.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+Privileges:caldav-client.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+Privileges:caldav-client.php:    $this->headers = array();  // reset the headers array for our next request
+Privileges:caldav-client.php:    $this->ParseResponse($rsp);
+Privileges:caldav-client.php:    $this->requestMethod = "OPTIONS";
+Privileges:caldav-client.php:    $this->body = "";
+Privileges:caldav-client.php:    $headers = $this->DoRequest($relative_url);
+Privileges:caldav-client.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+Privileges:caldav-client.php:    $this->body = $xml;
+Privileges:caldav-client.php:    $this->requestMethod = $request_method;
+Privileges:caldav-client.php:    $this->SetContentType("text/xml");
+Privileges:caldav-client.php:    return $this->DoRequest($relative_url);
+Privileges:caldav-client.php:    $this->body = "";
+Privileges:caldav-client.php:    $this->requestMethod = "GET";
+Privileges:caldav-client.php:    return $this->DoRequest( $relative_url );
+Privileges:caldav-client.php:    $this->body = $icalendar;
+Privileges:caldav-client.php:    $this->requestMethod = "PUT";
+Privileges:caldav-client.php:      $this->SetMatch( ($etag != '*'), $etag );
+Privileges:caldav-client.php:    $this->SetContentType("text/icalendar");
+Privileges:caldav-client.php:    $headers = $this->DoRequest($relative_url);
+Privileges:caldav-client.php:    $this->body = "";
+Privileges:caldav-client.php:    $this->requestMethod = "DELETE";
+Privileges:caldav-client.php:      $this->SetMatch( true, $etag );
+Privileges:caldav-client.php:    $this->DoRequest($relative_url);
+Privileges:caldav-client.php:    return $this->resultcode;
+Privileges:caldav-client.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+Privileges:caldav-client.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+Privileges:caldav-client.php:<?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-client.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+Privileges:caldav-client.php:    <C:calendar-data/>
+Privileges:caldav-client.php:</C:calendar-query>
+Privileges:caldav-client.php:    $this->DoXMLRequest( 'REPORT', $xml, $relative_url );
+Privileges:caldav-client.php:    $xml_parser = xml_parser_create_ns('UTF-8');
+Privileges:caldav-client.php:    $this->xml_tags = array();
+Privileges:caldav-client.php:    xml_parse_into_struct( $xml_parser, $this->xmlResponse, $this->xml_tags );
+Privileges:caldav-client.php:    foreach( $this->xml_tags as $k => $v ) {
+Privileges:caldav-client.php:        case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
+Privileges:caldav-client.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+Privileges:caldav-client.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client.php:      <C:comp-filter name="VEVENT">
+Privileges:caldav-client.php:      </C:comp-filter>
+Privileges:caldav-client.php:    </C:comp-filter>
+Privileges:caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client.php:                <C:time-range start="$start" end="$finish"/>
+Privileges:caldav-client.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client.php:          <C:comp-filter name="VTODO">
+Privileges:caldav-client.php:                <C:prop-filter name="STATUS">
+Privileges:caldav-client.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+Privileges:caldav-client.php:                </C:prop-filter>
+Privileges:caldav-client.php:                <C:prop-filter name="STATUS">
+Privileges:caldav-client.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+Privileges:caldav-client.php:                </C:prop-filter>$time_range
+Privileges:caldav-client.php:          </C:comp-filter>
+Privileges:caldav-client.php:    </C:comp-filter>
+Privileges:caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client.php:          <C:comp-filter name="VEVENT">
+Privileges:caldav-client.php:                <C:prop-filter name="UID">
+Privileges:caldav-client.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+Privileges:caldav-client.php:                </C:prop-filter>
+Privileges:caldav-client.php:          </C:comp-filter>
+Privileges:caldav-client.php:    </C:comp-filter>
+Privileges:caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client.php:    return $this->DoGETRequest( $relative_url . $href );
+Privileges:caldav-client.php:* $options = $cal->DoOptionsRequest();
+Privileges:caldav-client.php:*   $cal->SetDepth(1);
+Privileges:caldav-client.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+Privileges:caldav-client.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+Privileges:caldav-client.php:* $options = $cal->DoOptionsRequest();
+Privileges:caldav-client.php:* <?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-client.php:* //  $cal->SetDepth(1);
+Privileges:caldav-client.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+Privileges:caldav-client.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+Privileges:caldav-client.php:*     print "\n---------------------------------------------\n";
+Privileges:caldav-client-v2.php:* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
+Privileges:caldav-client-v2.php:    $this->url = $url;
+Privileges:caldav-client-v2.php:    $this->displayname = $displayname;
+Privileges:caldav-client-v2.php:    $this->getctag = $getctag;
+Privileges:caldav-client-v2.php:    return( '(URL: '.$this->url.'   Ctag: '.$this->getctag.'   Displayname: '.$this->displayname .')'. "\n" );
+Privileges:caldav-client-v2.php:  * The principal-URL we're using
+Privileges:caldav-client-v2.php:  * The calendar-URL we're using
+Privileges:caldav-client-v2.php:  * The calendar-home-set we're using
+Privileges:caldav-client-v2.php:    $this->user = $user;
+Privileges:caldav-client-v2.php:    $this->pass = $pass;
+Privileges:caldav-client-v2.php:    $this->headers = array();
+Privileges:caldav-client-v2.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+Privileges:caldav-client-v2.php:      $this->server = $matches[2];
+Privileges:caldav-client-v2.php:      $this->base_url = $matches[5];
+Privileges:caldav-client-v2.php:        $this->protocol = 'ssl';
+Privileges:caldav-client-v2.php:        $this->port = 443;
+Privileges:caldav-client-v2.php:        $this->protocol = 'tcp';
+Privileges:caldav-client-v2.php:        $this->port = 80;
+Privileges:caldav-client-v2.php:        $this->port = intval($matches[4]);
+Privileges:caldav-client-v2.php:  * Adds an If-Match or If-None-Match header
+Privileges:caldav-client-v2.php:    $this->headers['match'] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+Privileges:caldav-client-v2.php:    $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+Privileges:caldav-client-v2.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+Privileges:caldav-client-v2.php:    $this->user_agent = $user_agent;
+Privileges:caldav-client-v2.php:  * Add a Content-type: header.
+Privileges:caldav-client-v2.php:    $this->headers['content-type'] = "Content-type: $type";
+Privileges:caldav-client-v2.php:    $this->calendar_url = $url;
+Privileges:caldav-client-v2.php:      $this->httpResponse = trim($response);
+Privileges:caldav-client-v2.php:      $this->httpResponse = trim(substr($response, 0, $pos));
+Privileges:caldav-client-v2.php:      $this->xmlResponse = trim(substr($response, $pos));
+Privileges:caldav-client-v2.php:      $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
+Privileges:caldav-client-v2.php:      $parser = xml_parser_create_ns('UTF-8');
+Privileges:caldav-client-v2.php:      if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
+Privileges:caldav-client-v2.php:        printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
+Privileges:caldav-client-v2.php://        echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes );
+Privileges:caldav-client-v2.php://        echo "\nTags array............................................................\n";  print_r( $this->xmltags );
+Privileges:caldav-client-v2.php:        printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
+Privileges:caldav-client-v2.php:      return $this->httpRequest;
+Privileges:caldav-client-v2.php:      return $this->httpResponseHeaders;
+Privileges:caldav-client-v2.php:      return $this->httpResponseBody;
+Privileges:caldav-client-v2.php:      return $this->xmlRequest;
+Privileges:caldav-client-v2.php:      return $this->xmlResponse;
+Privileges:caldav-client-v2.php:    if ( !isset($url) ) $url = $this->base_url;
+Privileges:caldav-client-v2.php:    $this->request_url = $url;
+Privileges:caldav-client-v2.php:    if ( preg_match( '{[^%?&=+,.-_/a-z0-9]}', $url ) ) {
+Privileges:caldav-client-v2.php:    $headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
+Privileges:caldav-client-v2.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+Privileges:caldav-client-v2.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+Privileges:caldav-client-v2.php:    if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
+Privileges:caldav-client-v2.php:    foreach( $this->headers as $ii => $head ) {
+Privileges:caldav-client-v2.php:    $headers[] = "Content-Length: " . strlen($this->body);
+Privileges:caldav-client-v2.php:    $headers[] = "User-Agent: " . $this->user_agent;
+Privileges:caldav-client-v2.php:    $this->httpRequest = join("\r\n",$headers);
+Privileges:caldav-client-v2.php:    $this->xmlRequest = $this->body;
+Privileges:caldav-client-v2.php:    $this->httpResponse = '';
+Privileges:caldav-client-v2.php:    $this->xmlResponse = '';
+Privileges:caldav-client-v2.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+Privileges:caldav-client-v2.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+Privileges:caldav-client-v2.php:    list( $this->httpResponseHeaders, $this->httpResponseBody ) = preg_split( '{\r?\n\r?\n}s', $response, 2 );
+Privileges:caldav-client-v2.php:    if ( preg_match( '{Transfer-Encoding: chunked}i', $this->httpResponseHeaders ) ) $this->Unchunk();
+Privileges:caldav-client-v2.php:    $this->headers = array();  // reset the headers array for our next request
+Privileges:caldav-client-v2.php:    $this->ParseResponse($this->httpResponseBody);
+Privileges:caldav-client-v2.php:    $chunks = $this->httpResponseBody;
+Privileges:caldav-client-v2.php:      if ( preg_match('{^((\r\n)?\s*([ 0-9a-fA-F]+)(;[^\n]*)?\r?\n)}', $chunks, $matches ) ) {
+Privileges:caldav-client-v2.php:          // printf( "---------------------------------\n%s\n---------------------------------\n", substr($chunks,$pos,$bytes) );
+Privileges:caldav-client-v2.php:    $this->httpResponseBody = $content;
+Privileges:caldav-client-v2.php:    $this->requestMethod = "OPTIONS";
+Privileges:caldav-client-v2.php:    $this->body = "";
+Privileges:caldav-client-v2.php:    $headers = $this->DoRequest($url);
+Privileges:caldav-client-v2.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+Privileges:caldav-client-v2.php:    $this->body = $xml;
+Privileges:caldav-client-v2.php:    $this->requestMethod = $request_method;
+Privileges:caldav-client-v2.php:    $this->SetContentType("text/xml");
+Privileges:caldav-client-v2.php:    return $this->DoRequest($url);
+Privileges:caldav-client-v2.php:    $this->body = "";
+Privileges:caldav-client-v2.php:    $this->requestMethod = "GET";
+Privileges:caldav-client-v2.php:    return $this->DoRequest( $url );
+Privileges:caldav-client-v2.php:    $this->body = "";
+Privileges:caldav-client-v2.php:    $this->requestMethod = "HEAD";
+Privileges:caldav-client-v2.php:    return $this->DoRequest( $url );
+Privileges:caldav-client-v2.php:    $this->body = $icalendar;
+Privileges:caldav-client-v2.php:    $this->requestMethod = "PUT";
+Privileges:caldav-client-v2.php:      $this->SetMatch( ($etag != '*'), $etag );
+Privileges:caldav-client-v2.php:    $this->SetContentType('text/calendar; encoding="utf-8"');
+Privileges:caldav-client-v2.php:    $this->DoRequest($url);
+Privileges:caldav-client-v2.php:    if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+Privileges:caldav-client-v2.php:      printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
+Privileges:caldav-client-v2.php:      $save_request = $this->httpRequest;
+Privileges:caldav-client-v2.php:      $save_response_headers = $this->httpResponseHeaders;
+Privileges:caldav-client-v2.php:      $this->DoHEADRequest( $url );
+Privileges:caldav-client-v2.php:      if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+Privileges:caldav-client-v2.php:        printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
+Privileges:caldav-client-v2.php:      $this->httpRequest = $save_request;
+Privileges:caldav-client-v2.php:      $this->httpResponseHeaders = $save_response_headers;
+Privileges:caldav-client-v2.php:    $this->body = "";
+Privileges:caldav-client-v2.php:    $this->requestMethod = "DELETE";
+Privileges:caldav-client-v2.php:      $this->SetMatch( true, $etag );
+Privileges:caldav-client-v2.php:    $this->DoRequest($url);
+Privileges:caldav-client-v2.php:    return $this->resultcode;
+Privileges:caldav-client-v2.php:    $this->SetDepth($depth);
+Privileges:caldav-client-v2.php:      $xml->NSElement($prop,$v);
+Privileges:caldav-client-v2.php:    $this->body = $xml->Render('propfind',$prop );
+Privileges:caldav-client-v2.php:    $this->requestMethod = "PROPFIND";
+Privileges:caldav-client-v2.php:    $this->SetContentType("text/xml");
+Privileges:caldav-client-v2.php:    $this->DoRequest($url);
+Privileges:caldav-client-v2.php:    return $this->GetXmlResponse();
+Privileges:caldav-client-v2.php:      $this->principal_url = $url;
+Privileges:caldav-client-v2.php:    return $this->principal_url;
+Privileges:caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+Privileges:caldav-client-v2.php:  * @param $url array of string The calendar-home-set URLs to set
+Privileges:caldav-client-v2.php:      $this->calendar_home_set = $urls;
+Privileges:caldav-client-v2.php:    return $this->calendar_home_set;
+Privileges:caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+Privileges:caldav-client-v2.php:      $this->calendar_urls = $urls;
+Privileges:caldav-client-v2.php:    return $this->calendar_urls;
+Privileges:caldav-client-v2.php:    foreach( $this->xmltags[$tagname] AS $k => $v ) {
+Privileges:caldav-client-v2.php:      if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
+Privileges:caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+Privileges:caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+Privileges:caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+Privileges:caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
+Privileges:caldav-client-v2.php://        printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+Privileges:caldav-client-v2.php:        if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
+Privileges:caldav-client-v2.php://      printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+Privileges:caldav-client-v2.php:      if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+Privileges:caldav-client-v2.php://        printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']);
+Privileges:caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+Privileges:caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+Privileges:caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+Privileges:caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' );
+Privileges:caldav-client-v2.php:        while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
+Privileges:caldav-client-v2.php:        if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+Privileges:caldav-client-v2.php:          return rawurldecode($this->xmlnodes[$j]['value']);
+Privileges:caldav-client-v2.php:    $level = $this->xmlnodes[$nodenum]['level'];
+Privileges:caldav-client-v2.php:    while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) {
+Privileges:caldav-client-v2.php:      if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) {
+Privileges:caldav-client-v2.php:        if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) {
+Privileges:caldav-client-v2.php:      elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) {
+Privileges:caldav-client-v2.php:      elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) {
+Privileges:caldav-client-v2.php:        $status = $this->xmlnodes[$nodenum]['value'];
+Privileges:caldav-client-v2.php:        $props[] = $this->xmlnodes[$nodenum];
+Privileges:caldav-client-v2.php:  * @param string $url The URL to find the principal-URL from
+Privileges:caldav-client-v2.php:    $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL',
+Privileges:caldav-client-v2.php:                                  'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
+Privileges:caldav-client-v2.php:    $principal_url = $this->HrefForProp('DAV::principal');
+Privileges:caldav-client-v2.php:      foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) {
+Privileges:caldav-client-v2.php:          $principal_url = $this->HrefValueInside($href);
+Privileges:caldav-client-v2.php:    return $this->PrincipalURL($principal_url);
+Privileges:caldav-client-v2.php:  * @param string $url The URL to find the calendar-home-set from
+Privileges:caldav-client-v2.php:    if ( !isset($this->principal_url) ) {
+Privileges:caldav-client-v2.php:      $this->FindPrincipal();
+Privileges:caldav-client-v2.php:      $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0);
+Privileges:caldav-client-v2.php:    foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) {
+Privileges:caldav-client-v2.php:      if ( $this->xmlnodes[$v]['type'] != 'open' ) continue;
+Privileges:caldav-client-v2.php:      while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
+Privileges:caldav-client-v2.php://        printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
+Privileges:caldav-client-v2.php:        if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
+Privileges:caldav-client-v2.php:          $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
+Privileges:caldav-client-v2.php:      $calendar_home = $this->FindCalendarHome(true);
+Privileges:caldav-client-v2.php:    return $this->CalendarHomeSet($calendar_home);
+Privileges:caldav-client-v2.php:    if ( !isset($this->calendar_home_set[0]) ) {
+Privileges:caldav-client-v2.php:      $this->FindCalendarHome();
+Privileges:caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1);
+Privileges:caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) {
+Privileges:caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) {
+Privileges:caldav-client-v2.php:        $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1;
+Privileges:caldav-client-v2.php:      foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
+Privileges:caldav-client-v2.php:        $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+Privileges:caldav-client-v2.php:        $ok_props = $this->GetOKProps($hnode);
+Privileges:caldav-client-v2.php:              $calendar->getctag = $v['value'];
+Privileges:caldav-client-v2.php:              $calendar->displayname = $v['value'];
+Privileges:caldav-client-v2.php:    return $this->CalendarUrls($calendars);
+Privileges:caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+Privileges:caldav-client-v2.php:    $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
+Privileges:caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
+Privileges:caldav-client-v2.php:    $hnode = $this->xmltags['DAV::href'][0];
+Privileges:caldav-client-v2.php:    $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+Privileges:caldav-client-v2.php:    $ok_props = $this->GetOKProps($hnode);
+Privileges:caldav-client-v2.php:        $calendar->{$name} = $v['value'];
+Privileges:caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+Privileges:caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
+Privileges:caldav-client-v2.php:    if ( isset($this->xmltags['DAV::getetag']) ) {
+Privileges:caldav-client-v2.php:      foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
+Privileges:caldav-client-v2.php:        $href = $this->HrefForProp('DAV::getetag', $k);
+Privileges:caldav-client-v2.php:        if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) $etags[$href] = $this->xmlnodes[$v]['value'];
+Privileges:caldav-client-v2.php:  * Get a bunch of events for a calendar with a calendar-multiget report
+Privileges:caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+Privileges:caldav-client-v2.php:    $this->body = <<<EOXML
+Privileges:caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-client-v2.php:<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+Privileges:caldav-client-v2.php:<prop><getetag/><C:calendar-data/></prop>
+Privileges:caldav-client-v2.php:</C:calendar-multiget>
+Privileges:caldav-client-v2.php:    $this->requestMethod = "REPORT";
+Privileges:caldav-client-v2.php:    $this->SetContentType("text/xml");
+Privileges:caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+Privileges:caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
+Privileges:caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
+Privileges:caldav-client-v2.php:        $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
+Privileges:caldav-client-v2.php://        echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]);
+Privileges:caldav-client-v2.php:        $events[$href] = $this->xmlnodes[$v]['value'];
+Privileges:caldav-client-v2.php:        $this->DoGETRequest($href);
+Privileges:caldav-client-v2.php:        $events[$href] = $this->httpResponseBody;
+Privileges:caldav-client-v2.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+Privileges:caldav-client-v2.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+Privileges:caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+Privileges:caldav-client-v2.php:    $this->body = <<<EOXML
+Privileges:caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-client-v2.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+Privileges:caldav-client-v2.php:    <C:calendar-data/>
+Privileges:caldav-client-v2.php:</C:calendar-query>
+Privileges:caldav-client-v2.php:    $this->requestMethod = "REPORT";
+Privileges:caldav-client-v2.php:    $this->SetContentType("text/xml");
+Privileges:caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+Privileges:caldav-client-v2.php:    foreach( $this->xmltags as $k => $v ) {
+Privileges:caldav-client-v2.php:        case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+Privileges:caldav-client-v2.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+Privileges:caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client-v2.php:      <C:comp-filter name="VEVENT">
+Privileges:caldav-client-v2.php:      </C:comp-filter>
+Privileges:caldav-client-v2.php:    </C:comp-filter>
+Privileges:caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client-v2.php:                <C:time-range start="$start" end="$finish"/>
+Privileges:caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client-v2.php:          <C:comp-filter name="VTODO">
+Privileges:caldav-client-v2.php:                <C:prop-filter name="STATUS">
+Privileges:caldav-client-v2.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+Privileges:caldav-client-v2.php:                </C:prop-filter>
+Privileges:caldav-client-v2.php:                <C:prop-filter name="STATUS">
+Privileges:caldav-client-v2.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+Privileges:caldav-client-v2.php:                </C:prop-filter>$time_range
+Privileges:caldav-client-v2.php:          </C:comp-filter>
+Privileges:caldav-client-v2.php:    </C:comp-filter>
+Privileges:caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+Privileges:caldav-client-v2.php:          <C:comp-filter name="VEVENT">
+Privileges:caldav-client-v2.php:                <C:prop-filter name="UID">
+Privileges:caldav-client-v2.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+Privileges:caldav-client-v2.php:                </C:prop-filter>
+Privileges:caldav-client-v2.php:          </C:comp-filter>
+Privileges:caldav-client-v2.php:    </C:comp-filter>
+Privileges:caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+Privileges:caldav-client-v2.php:    return $this->DoGETRequest( $href );
+Privileges:caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+Privileges:caldav-client-v2.php:*   $cal->SetDepth(1);
+Privileges:caldav-client-v2.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+Privileges:caldav-client-v2.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+Privileges:caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+Privileges:caldav-client-v2.php:* <?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-client-v2.php:* //  $cal->SetDepth(1);
+Privileges:caldav-client-v2.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+Privileges:caldav-client-v2.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+Privileges:caldav-client-v2.php:*     print "\n---------------------------------------------\n";
+Privileges:caldav-DELETE.php:* CalDAV Server - handle DELETE method
+Privileges:caldav-DELETE.php:$dav_resource = new DAVResource($request->path);
+Privileges:caldav-DELETE.php:$container = $dav_resource->FetchParentContainer();
+Privileges:caldav-DELETE.php:$container->NeedPrivilege('DAV::unbind');
+Privileges:caldav-DELETE.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-DELETE.php:  if ( $qry->Exec('DELETE',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-DELETE.php:    while( $row = $qry->Fetch() ) {
+Privileges:caldav-DELETE.php:      delete_collection($row->child_id);
+Privileges:caldav-DELETE.php:  if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params ) ) {
+Privileges:caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+Privileges:caldav-DELETE.php:if ( !$dav_resource->Exists() )$request->DoResponse( 404 );
+Privileges:caldav-DELETE.php:if ( ! ( $dav_resource->resource_id() > 0 ) ) {
+Privileges:caldav-DELETE.php:  $request->DoResponse( 403 );
+Privileges:caldav-DELETE.php:$qry->Begin();
+Privileges:caldav-DELETE.php:if ( $dav_resource->IsBinding() ) {
+Privileges:caldav-DELETE.php:  $params = array( ':dav_name' => $dav_resource->dav_name() );
+Privileges:caldav-DELETE.php:  if ( $qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params )
+Privileges:caldav-DELETE.php:    && $qry->Commit() ) {
+Privileges:caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+Privileges:caldav-DELETE.php:    $request->DoResponse( 204 );
+Privileges:caldav-DELETE.php:else if ( $dav_resource->IsCollection() ) {
+Privileges:caldav-DELETE.php:  if ( delete_collection( $dav_resource->resource_id() ) && $qry->Commit() ) {
+Privileges:caldav-DELETE.php:    $request->DoResponse( 204 );
+Privileges:caldav-DELETE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag() ) ) {
+Privileges:caldav-DELETE.php:    $request->DoResponse( 412, translate("Resource has changed on server - not deleted") );
+Privileges:caldav-DELETE.php:  $params = array( ':dav_id' => $dav_resource->resource_id() );
+Privileges:caldav-DELETE.php:  if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE dav_id = :dav_id", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM property WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM locks WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params )
+Privileges:caldav-DELETE.php:    && $qry->QDo("DELETE FROM caldav_data WHERE dav_id = :dav_id", $params )
+Privileges:caldav-DELETE.php:    && $qry->Commit() ) {
+Privileges:caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+Privileges:caldav-DELETE.php:      log_caldav_action( 'DELETE', $dav_resource->GetProperty('uid'), $dav_resource->GetProperty('user_no'), $dav_resource->GetProperty('collection_id'), $request->path );
+Privileges:caldav-DELETE.php:    $request->DoResponse( 204 );
+Privileges:caldav-DELETE.php:$request->DoResponse( 500 );
+Privileges:caldav-DELTICKET.php:* CalDAV Server - handle DELTICKET method in line with defunct proposed RFC
+Privileges:caldav-DELTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+Privileges:caldav-DELTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-DELTICKET.php:if ( ! $request->HavePrivilegeTo('DAV::unbind') && $request->ticket->owner() != $session->principal_id ) {
+Privileges:caldav-DELTICKET.php:  $request->NeedPrivilege('DAV::unbind');
+Privileges:caldav-DELTICKET.php:if ( ! isset($request->ticket) ) {
+Privileges:caldav-DELTICKET.php:    $r = new DAVResource($request->path);
+Privileges:caldav-DELTICKET.php:    if ( ! $r->Exists() ) {
+Privileges:caldav-DELTICKET.php:      $request->PreconditionFailed(404,'not-found');
+Privileges:caldav-DELTICKET.php:      $request->PreconditionFailed(412,'ticket-does-not-exist','The specified ticket does not exist');
+Privileges:caldav-DELTICKET.php:    $request->MalformedRequest('No ticket specified');
+Privileges:caldav-DELTICKET.php:$qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=:ticket_id', array( ':ticket_id'   => $request->ticket->id() ) );
+Privileges:caldav-DELTICKET.php:if ( $qry->Exec('DELTICKET', __LINE__, __FILE__) ) {
+Privileges:caldav-DELTICKET.php:  $request->DoResponse( 204 );
+Privileges:caldav-DELTICKET.php:$request->DoResponse( 500 );
+Privileges:caldav-GET.php:* CalDAV Server - handle GET method
+Privileges:caldav-GET.php:$dav_resource = new DAVResource($request->path);
+Privileges:caldav-GET.php:$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
+Privileges:caldav-GET.php:if ( ! $dav_resource->Exists() ) {
+Privileges:caldav-GET.php:  $request->DoResponse( 404, translate("Resource Not Found.") );
+Privileges:caldav-GET.php:  // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
+Privileges:caldav-GET.php:  $confidential->SetType($icalendar->GetType());
+Privileges:caldav-GET.php:  $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+Privileges:caldav-GET.php:  $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTSTART'), 'DTSTART' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('RRULE'), 'RRULE' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DURATION'), 'DURATION' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTEND'), 'DTEND' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('UID'), 'UID' );
+Privileges:caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('CREATED'), 'CREATED' );
+Privileges:caldav-GET.php:if ( $dav_resource->IsCollection() ) {
+Privileges:caldav-GET.php:  if ( ! $dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections) ) {
+Privileges:caldav-GET.php:    $request->DoResponse( 405, translate("GET requests on collections are only supported for calendars.") );
+Privileges:caldav-GET.php:  if ( isset($c->get_includes_subcollections) && $c->get_includes_subcollections ) {
+Privileges:caldav-GET.php:    $params = array( ':path_match' => '^'.$request->path );
+Privileges:caldav-GET.php:    $params = array( ':collection_id' => $dav_resource->resource_id() );
+Privileges:caldav-GET.php:  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+Privileges:caldav-GET.php:  if ( !$qry->Exec("GET",__LINE__,__FILE__) ) {
+Privileges:caldav-GET.php:    $request->DoResponse( 500, translate("Database Error") );
+Privileges:caldav-GET.php:  $vcal->VCalendar();
+Privileges:caldav-GET.php:  $displayname = $dav_resource->GetProperty('displayname');
+Privileges:caldav-GET.php:    $vcal->AddProperty("X-WR-CALNAME", $displayname);
+Privileges:caldav-GET.php:  while( $event = $qry->Fetch() ) {
+Privileges:caldav-GET.php:    $ical = new iCalComponent( $event->caldav_data );
+Privileges:caldav-GET.php:    $event_zones = $ical->GetComponents('VTIMEZONE',true);
+Privileges:caldav-GET.php:      $tzid = $tz->GetPValue('TZID');
+Privileges:caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-GET.php:      $tzid = $comp->GetPParamValue('DTSTART', 'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+Privileges:caldav-GET.php:      $tzid = $comp->GetPParamValue('DUE',     'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+Privileges:caldav-GET.php:      $tzid = $comp->GetPParamValue('DTEND',   'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+Privileges:caldav-GET.php:      if ( $dav_resource->HavePrivilegeTo('all',false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user
+Privileges:caldav-GET.php:            || ( $c->allow_get_email_visibility && $comp->IsAttendee($session->email) ) ) {
+Privileges:caldav-GET.php:        $vcal->AddComponent($comp);
+Privileges:caldav-GET.php:      if ( $event->class == 'PRIVATE' ) continue;
+Privileges:caldav-GET.php:      if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL' ) {
+Privileges:caldav-GET.php:       $vcal->AddComponent(obfuscated_event($comp));
+Privileges:caldav-GET.php:      elseif ( isset($c->hide_alarm) && $c->hide_alarm ) {
+Privileges:caldav-GET.php:        $comp->ClearComponents('VALARM');
+Privileges:caldav-GET.php:        $vcal->AddComponent($comp);
+Privileges:caldav-GET.php:        $vcal->AddComponent($comp);
+Privileges:caldav-GET.php:    if ( isset($timezones[$tzid]) ) $vcal->AddComponent($timezones[$tzid]);
+Privileges:caldav-GET.php:  $response = $vcal->Render();
+Privileges:caldav-GET.php:  header( 'Content-Length: '.strlen($response) );
+Privileges:caldav-GET.php:  header( 'Etag: '.$dav_resource->unique_tag() );
+Privileges:caldav-GET.php:  $request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $response), 'text/calendar; charset="utf-8"' );
+Privileges:caldav-GET.php:$resource = $dav_resource->resource();
+Privileges:caldav-GET.php:$ic = new iCalComponent( $resource->caldav_data );
+Privileges:caldav-GET.php:if ( $dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $resource->user_no || $session->user_no == $resource->logged_user
+Privileges:caldav-GET.php:      || ( $c->allow_get_email_visibility && $ic->IsAttendee($session->email) ) ) {
+Privileges:caldav-GET.php:else if ( $resource->class != 'PRIVATE' ) {
+Privileges:caldav-GET.php:  if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || ( $resource->class == 'CONFIDENTIAL' && ! $request->HavePrivilegeTo('DAV::write-content') ) ) {
+Privileges:caldav-GET.php:    $ical = new iCalComponent( $resource->caldav_data );
+Privileges:caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-GET.php:    $ical->SetComponents( array($confidential), $resource->caldav_type );
+Privileges:caldav-GET.php:    $resource->caldav_data = $ical->Render();
+Privileges:caldav-GET.php:// else $resource->class == 'PRIVATE' and this person may not see it.
+Privileges:caldav-GET.php:  $request->DoResponse( 403, translate("Forbidden") );
+Privileges:caldav-GET.php:header( 'Etag: "'.$resource->dav_etag.'"' );
+Privileges:caldav-GET.php:header( 'Content-Length: '.strlen($resource->caldav_data) );
+Privileges:caldav-GET.php:switch( $resource->caldav_type ) {
+Privileges:caldav-GET.php:    $contenttype = 'text/x-vcard';
+Privileges:caldav-GET.php:$request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $resource->caldav_data), $contenttype.'; charset="utf-8"' );
+Privileges:caldav-LOCK.php:if ( ! $request->AllowedTo('write') ) {
+Privileges:caldav-LOCK.php:  $request->NeedPrivilege( 'write', $request->path );
+Privileges:caldav-LOCK.php:if ( ! isset($request->xml_tags) ) {
+Privileges:caldav-LOCK.php:  if ( isset($request->lock_token) ) {
+Privileges:caldav-LOCK.php:    $request->xml_tags = array();
+Privileges:caldav-LOCK.php:    $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+Privileges:caldav-LOCK.php:foreach( $request->xml_tags AS $k => $v ) {
+Privileges:caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+Privileges:caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+Privileges:caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+Privileges:caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+Privileges:caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+Privileges:caldav-LOCK.php:$request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff.
+Privileges:caldav-LOCK.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-LOCK.php:if ( $request->method == "LOCK" ) {
+Privileges:caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to lock resource '%s'", $request->path);
+Privileges:caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+Privileges:caldav-LOCK.php:        ':dav_name'      => $request->path,
+Privileges:caldav-LOCK.php:        ':request_depth' => $request->depth,
+Privileges:caldav-LOCK.php:        ':timeout'       => $request->timeout.' seconds'
+Privileges:caldav-LOCK.php:    header( "Lock-Token: <opaquelocktoken:$lock_token>" );
+Privileges:caldav-LOCK.php:  $qry->Exec("LOCK",__LINE__,__FILE__);
+Privileges:caldav-LOCK.php:  $lock_row = $request->GetLockRow($lock_token);
+Privileges:caldav-LOCK.php:      new XMLElement( 'locktype',  new XMLElement( $lock_row->type )),
+Privileges:caldav-LOCK.php:      new XMLElement( 'lockscope', new XMLElement( $lock_row->scope )),
+Privileges:caldav-LOCK.php:      new XMLElement( 'depth',     $request->GetDepthName() ),
+Privileges:caldav-LOCK.php:      new XMLElement( 'owner',     new XMLElement( 'href', $lock_row->owner )),
+Privileges:caldav-LOCK.php:      new XMLElement( 'timeout',   'Second-'.$request->timeout),
+Privileges:caldav-LOCK.php:elseif (  $request->method == "UNLOCK" ) {
+Privileges:caldav-LOCK.php:  * @TODO: respond with preconditionfailed(409,'lock-token-matches-request-uri') if
+Privileges:caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to unlock resource '%s'", $request->path);
+Privileges:caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+Privileges:caldav-LOCK.php:    $qry->Exec("LOCK",__LINE__,__FILE__);
+Privileges:caldav-LOCK.php:  $request->DoResponse( 204 );
+Privileges:caldav-LOCK.php:$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+Privileges:caldav-LOCK.php:$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:caldav-MKCOL.php:* CalDAV Server - handle MKCOL and MKCALENDAR method
+Privileges:caldav-MKCOL.php:* @copyright Catalyst IT Ltd, Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-MKCOL.php:$request->NeedPrivilege('DAV::bind');
+Privileges:caldav-MKCOL.php:$displayname = $request->path;
+Privileges:caldav-MKCOL.php:if ( ! preg_match( '#/$#', $request->path ) ) {
+Privileges:caldav-MKCOL.php:  dbg_error_log( 'MKCOL', 'Add trailling "/" to "%s"', $request->path);
+Privileges:caldav-MKCOL.php:  $request->path .= '/';
+Privileges:caldav-MKCOL.php:if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
+Privileges:caldav-MKCOL.php:if ( $parent->IsSchedulingCollection( 'inbox' ) ) {
+Privileges:caldav-MKCOL.php:  $request->PreconditionFailed(403, 'urn:ietf:params:xml:ns:caldav:no-mkcol-in-inbox' );
+Privileges:caldav-MKCOL.php:$request_type = $request->method;
+Privileges:caldav-MKCOL.php:if ( isset($request->xml_tags) ) {
+Privileges:caldav-MKCOL.php:  $xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-MKCOL.php:  if ( $xmltree->GetTag() == 'DAV::mkcol' ) $request_type = 'extended-mkcol';
+Privileges:caldav-MKCOL.php:  if ( $xmltree->GetTag() != 'urn:ietf:params:xml:ns:caldav:mkcalendar' && $request_type != 'extended-mkcol' ) {
+Privileges:caldav-MKCOL.php:    $request->DoResponse( 406, sprintf('The XML is not a "DAV::mkcol" or "urn:ietf:params:xml:ns:caldav:mkcalendar" document (%s)', $xmltree->GetTag()) );
+Privileges:caldav-MKCOL.php:  $setprops = $xmltree->GetContent();   // <set>
+Privileges:caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // <prop>
+Privileges:caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // the array of properties.
+Privileges:caldav-MKCOL.php:    $tag = $setting->GetTag();
+Privileges:caldav-MKCOL.php:    $content = $setting->RenderContent();
+Privileges:caldav-MKCOL.php:        $is_addressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+Privileges:caldav-MKCOL.php:        $is_calendar    = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+Privileges:caldav-MKCOL.php:          $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-MKCOL.php:              new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+Privileges:caldav-MKCOL.php:          $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+Privileges:caldav-MKCOL.php:          dbg_error_log( 'MKCOL', 'Displayname is "/" to "%s"', $request->path);
+Privileges:caldav-MKCOL.php:          $parent_container = $request->path;
+Privileges:caldav-MKCOL.php:          $request->path .= $content . '/';
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':  /** Ignored, since we will support all component types */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-resource-size':  /** Ignored, since we will support arbitrary size */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:min-date-time':  /** Ignored, since we will support arbitrary time */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-date-time':  /** Ignored, since we will support arbitrary time */
+Privileges:caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-instances':  /** Ignored, since we will support arbitrary instances */
+Privileges:caldav-MKCOL.php:      * The following properties are read-only, so they will cause the request to fail
+Privileges:caldav-MKCOL.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-MKCOL.php:            new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+Privileges:caldav-MKCOL.php:            new XMLElement('responsedescription', translate('Property is read-only') )
+Privileges:caldav-MKCOL.php:      $props[] = new XMLElement($reply->Tag($tag));
+Privileges:caldav-MKCOL.php:    if ( $request_type == 'extended-mkcol' ) {
+Privileges:caldav-MKCOL.php:      $request->DoResponse( $failure_code, $reply->Render('mkcol-response', array_merge( $status, $failure ), 'text/xml; charset="utf-8"' ) );
+Privileges:caldav-MKCOL.php:      array_unshift( $failure, $reply->href( ConstructURL($request->path) ) );
+Privileges:caldav-MKCOL.php:      $request->DoResponse( 207, $reply->Render('multistatus', new XMLElement( 'response', $failure )), 'text/xml; charset="utf-8"' );
+Privileges:caldav-MKCOL.php:$qry = new AwlQuery( $sql, array( ':dav_name' => $request->path) );
+Privileges:caldav-MKCOL.php:if ( ! $qry->Exec('MKCOL',__LINE__,__FILE__) ) {
+Privileges:caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error querying database.') );
+Privileges:caldav-MKCOL.php:if ( $qry->rows() != 0 ) {
+Privileges:caldav-MKCOL.php:  $request->DoResponse( 405, translate('A collection already exists at that location.') );
+Privileges:caldav-MKCOL.php:$qry->Begin();
+Privileges:caldav-MKCOL.php:if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
+Privileges:caldav-MKCOL.php:              ':user_no'          => $request->user_no,
+Privileges:caldav-MKCOL.php:              ':dav_name'         => $request->path,
+Privileges:caldav-MKCOL.php:              ':dav_etag'         => md5($request->user_no. $request->path),
+Privileges:caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+Privileges:caldav-MKCOL.php:  if ( ! $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text )',
+Privileges:caldav-MKCOL.php:             array( ':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v) ) ) {
+Privileges:caldav-MKCOL.php:    $request->DoResponse( 500, translate('Error writing calendar properties to database.') );
+Privileges:caldav-MKCOL.php:if ( !$qry->Commit() ) {
+Privileges:caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+Privileges:caldav-MKCOL.php:dbg_error_log( 'MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
+Privileges:caldav-MKCOL.php:header('Cache-Control: no-cache'); /** RFC4791 mandates this at 5.3.1 */
+Privileges:caldav-MKCOL.php:$request->DoResponse( 201, '' );
+Privileges:caldav-MKCOL.php:*   <?xml version="1.0" encoding="utf-8" ?>
+Privileges:caldav-MKCOL.php:*         <C:calendar-description xml:lang="en">Calendar restricted to events.</C:calendar-description>
+Privileges:caldav-MKCOL.php:*         <C:supported-calendar-component-set>
+Privileges:caldav-MKCOL.php:*         </C:supported-calendar-component-set>
+Privileges:caldav-MKCOL.php:*         <C:calendar-timezone><![CDATA[BEGIN:VCALENDAR
+Privileges:caldav-MKCOL.php:*   PRODID:-//Example Corp.//CalDAV Client//EN
+Privileges:caldav-MKCOL.php:*   TZID:US-Eastern
+Privileges:caldav-MKCOL.php:*   LAST-MODIFIED:19870101T000000Z
+Privileges:caldav-MKCOL.php:*   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+Privileges:caldav-MKCOL.php:*   TZOFFSETFROM:-0400
+Privileges:caldav-MKCOL.php:*   TZOFFSETTO:-0500
+Privileges:caldav-MKCOL.php:*   TZOFFSETFROM:-0500
+Privileges:caldav-MKCOL.php:*   TZOFFSETTO:-0400
+Privileges:caldav-MKCOL.php:*   ]]></C:calendar-timezone>
+Privileges:caldav-MKTICKET.php:* CalDAV Server - handle MKTICKET method in line with defunct proposed RFC
+Privileges:caldav-MKTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+Privileges:caldav-MKTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-MKTICKET.php:$request->NeedPrivilege('DAV::bind');
+Privileges:caldav-MKTICKET.php:$target = new DAVResource( $request->path );
+Privileges:caldav-MKTICKET.php:if ( ! $target->Exists() ) {
+Privileges:caldav-MKTICKET.php:  $request->XMLResponse( 404, new XMLElement( 'error', new XMLElement('resource-must-not-be-null'), $reply->GetXmlNsArray() ) );
+Privileges:caldav-MKTICKET.php:if ( ! isset($request->xml_tags) ) {
+Privileges:caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+Privileges:caldav-MKTICKET.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-MKTICKET.php:if ( $xmltree->GetTag() != 'http://www.xythos.com/namespaces/StorageServer:ticketinfo' &&
+Privileges:caldav-MKTICKET.php:     $xmltree->GetTag() != 'DAV::ticketinfo' ) {
+Privileges:caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml-for-request'), $reply->GetXmlNsArray() ) );
+Privileges:caldav-MKTICKET.php:$ticket_timeout = 'Seconds-3600';
+Privileges:caldav-MKTICKET.php:$ticket_privs_array = array('read-free-busy');
+Privileges:caldav-MKTICKET.php:foreach( $xmltree->GetContent() AS $k => $v ) {
+Privileges:caldav-MKTICKET.php:  switch( $v->GetTag() ) {
+Privileges:caldav-MKTICKET.php:      $ticket_timeout = $v->GetContent();
+Privileges:caldav-MKTICKET.php:      $ticket_privs_array = $v->GetElements(); // Ensure we always get an array back
+Privileges:caldav-MKTICKET.php:        $ticket_privileges |= privilege_to_bits( $v1->GetTag() );
+Privileges:caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read') )           $ticket_privileges |= privilege_to_bits( array('read-free-busy', 'read-current-user-privilege-set') );
+Privileges:caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read-free-busy') ) $ticket_privileges |= privilege_to_bits( 'schedule-query-freebusy');
+Privileges:caldav-MKTICKET.php:else if ( preg_match( '{^([a-z]+)-(\d+)$}i', $ticket_timeout, $matches ) ) {
+Privileges:caldav-MKTICKET.php:  /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */
+Privileges:caldav-MKTICKET.php:$collection_id = $target->GetProperty('collection_id');
+Privileges:caldav-MKTICKET.php:$resource_id   = $target->GetProperty('dav_id');
+Privileges:caldav-MKTICKET.php:      ':owner'       => $session->principal_id,
+Privileges:caldav-MKTICKET.php:  $result = $qry->Exec('MKTICKET', __LINE__, __FILE__);
+Privileges:caldav-MKTICKET.php:  $reply->NSElement($privs, $v);
+Privileges:caldav-MKTICKET.php:      new XMLElement( 'owner', $reply->href( ConstructURL('/'.$session->username.'/') ) ),
+Privileges:caldav-MKTICKET.php:$prop = new XMLElement( "prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray() );
+Privileges:caldav-MKTICKET.php:$request->XMLResponse( 200, $prop );
+Privileges:caldav-MOVE.php:* CalDAV Server - handle MOVE method
+Privileges:caldav-MOVE.php:$request->NeedPrivilege('DAV::unbind');
+Privileges:caldav-MOVE.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['move']) && $c->dbg['move'])) ) {
+Privileges:caldav-MOVE.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-MOVE.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-MOVE.php:$dest = new DAVResource($request->destination);
+Privileges:caldav-MOVE.php:if ( $dest->dav_name() == '/' || $dest->IsPrincipal() ) {
+Privileges:caldav-MOVE.php:  $dest->NeedPrivilege('DAV::bind');
+Privileges:caldav-MOVE.php:if ( ! $dest->ContainerExists() ) {
+Privileges:caldav-MOVE.php:  $request->DoResponse( 409, translate('Destination collection does not exist') );
+Privileges:caldav-MOVE.php:if ( ! $request->overwrite && $dest->Exists() ) {
+Privileges:caldav-MOVE.php:  $request->DoResponse( 412, translate('Not overwriting existing destination resource') );
+Privileges:caldav-MOVE.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' ) {
+Privileges:caldav-MOVE.php:  $request->DoResponse( 412 );  /** request to move, but only if there is no source? WTF! */
+Privileges:caldav-MOVE.php:$src  = new DAVResource($request->path);
+Privileges:caldav-MOVE.php:if ( ! $src->Exists() ) {
+Privileges:caldav-MOVE.php:  $request->DoResponse( 412, translate('Source resource does not exist.') );
+Privileges:caldav-MOVE.php:if ( $src->IsCollection() ) {
+Privileges:caldav-MOVE.php:  switch( $dest->ContainerType() ) {
+Privileges:caldav-MOVE.php:    case 'schedule-inbox':
+Privileges:caldav-MOVE.php:    case 'schedule-outbox':
+Privileges:caldav-MOVE.php:      $request->DoResponse( 412, translate('Special collections may not contain a calendar or other special collection.') );
+Privileges:caldav-MOVE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != '' )
+Privileges:caldav-MOVE.php:        || ( isset($request->etag_none_match) && $request->etag_none_match != '') ) {
+Privileges:caldav-MOVE.php:    * (without the If-None-Match header) on that resource, or if "*" is
+Privileges:caldav-MOVE.php:    if ( isset($request->etag_if_match) && $request->etag_if_match != $src->unique_tag() ) {
+Privileges:caldav-MOVE.php:      $error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
+Privileges:caldav-MOVE.php:    else if ( isset($request->etag_none_match) && $request->etag_none_match != '' && $request->etag_none_match == $src->unique_tag() ) {
+Privileges:caldav-MOVE.php:      $error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
+Privileges:caldav-MOVE.php:    if ( $error != '' ) $request->DoResponse( 412, $error );
+Privileges:caldav-MOVE.php:$src->NeedPrivilege('DAV::unbind');
+Privileges:caldav-MOVE.php:$dest->NeedPrivilege('DAV::write-content');
+Privileges:caldav-MOVE.php:if ( ! $dest->Exists() ) $dest->NeedPrivilege('DAV::bind');
+Privileges:caldav-MOVE.php:  $qry->Exec('move'); // Just in case
+Privileges:caldav-MOVE.php:  $request->DoResponse( $response_code );
+Privileges:caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:$src_name = $src->dav_name();
+Privileges:caldav-MOVE.php:$dst_name = $dest->dav_name();
+Privileges:caldav-MOVE.php:$src_collection = $src->GetProperty('collection_id');
+Privileges:caldav-MOVE.php:$dst_collection = $dest->GetProperty('collection_id');
+Privileges:caldav-MOVE.php:$src_user_no = $src->GetProperty('user_no');
+Privileges:caldav-MOVE.php:$dst_user_no = $dest->GetProperty('user_no');
+Privileges:caldav-MOVE.php:if ( $src->IsCollection()  ) {
+Privileges:caldav-MOVE.php:  if ( $dest->Exists() ) {
+Privileges:caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:  if ( $dest->Exists() ) {
+Privileges:caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:    log_caldav_action( 'DELETE', $src->GetProperty('uid'), $src_user_no, $src_collection, $src_name );
+Privileges:caldav-MOVE.php:    ':sync_type' => ( $dest->Exists() ? 200 : 201 )
+Privileges:caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:    log_caldav_action( ( $dest->Exists() ? 'UPDATE' : 'INSERT' ), $src->GetProperty('uid'), $dst_user_no, $dst_collection, $dst_name );
+Privileges:caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-MOVE.php:$request->DoResponse( 200 );
+Privileges:caldav-OPTIONS.php:* CalDAV Server - handle OPTIONS method
+Privileges:caldav-OPTIONS.php:$resource = new DAVResource($request->path);
+Privileges:caldav-OPTIONS.php://$resource->NeedPrivilege( 'DAV::read', true );
+Privileges:caldav-OPTIONS.php:if ( !$resource->Exists() ) {
+Privileges:caldav-OPTIONS.php:  $request->DoResponse( 404, translate("No collection found at that location.") );
+Privileges:caldav-OPTIONS.php:$allowed = implode( ', ', array_keys($resource->FetchSupportedMethods()) );
+Privileges:caldav-OPTIONS.php:$request->DoResponse( 200, "" );
+Privileges:caldav-POST.php:* CalDAV Server - handle PUT method
+Privileges:caldav-POST.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-POST.php:include_once('caldav-PUT-functions.php');
+Privileges:caldav-POST.php:include_once('freebusy-functions.php');
+Privileges:caldav-POST.php:if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
+Privileges:caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-invite")
+Privileges:caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
+Privileges:caldav-POST.php:  // $request->DoResponse(403);
+Privileges:caldav-POST.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['post'])) ) {
+Privileges:caldav-POST.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-POST.php:  $fbq_start = $ic->GetPValue('DTSTART');
+Privileges:caldav-POST.php:  $fbq_end   = $ic->GetPValue('DTEND');
+Privileges:caldav-POST.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
+Privileges:caldav-POST.php:  $attendees = $ic->GetProperties('ATTENDEE');
+Privileges:caldav-POST.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+Privileges:caldav-POST.php:    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+Privileges:caldav-POST.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+Privileges:caldav-POST.php:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email );
+Privileges:caldav-POST.php:    if ( !$qry->Exec('POST',__LINE__,__FILE__) ) $request->DoResponse( 501, 'Database error');
+Privileges:caldav-POST.php:    if ( $qry->rows() > 1 ) {
+Privileges:caldav-POST.php:      if ( !$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params ) )
+Privileges:caldav-POST.php:        $request->DoResponse( 501, 'Database error');
+Privileges:caldav-POST.php:      if ( $qry->rows() == 0 ) {
+Privileges:caldav-POST.php:        /** Sigh... Go back to the original case-insensitive match */
+Privileges:caldav-POST.php:        $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
+Privileges:caldav-POST.php:    $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
+Privileges:caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+Privileges:caldav-POST.php:    if ( $qry->rows() == 0 ) {
+Privileges:caldav-POST.php:      $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
+Privileges:caldav-POST.php:      $reply->CalDAVElement($response, "calendar-data" );
+Privileges:caldav-POST.php:    if ( ! $attendee_usr = $qry->Fetch() ) $request->DoResponse( 501, 'Database error');
+Privileges:caldav-POST.php:    if ( (privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0 ) {
+Privileges:caldav-POST.php:      $reply->CalDAVElement($response, "request-status", "3.8;No authority" );
+Privileges:caldav-POST.php:      $reply->CalDAVElement($response, "calendar-data" );
+Privileges:caldav-POST.php:    $attendee_path_match = '^/'.$attendee_usr->username.'/';
+Privileges:caldav-POST.php:    $fb = get_freebusy( $attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p) );
+Privileges:caldav-POST.php:    $fb->AddProperty( 'UID',       $ic->GetPValue('UID') );
+Privileges:caldav-POST.php:    $fb->SetProperties( $ic->GetProperties('ORGANIZER'), 'ORGANIZER');
+Privileges:caldav-POST.php:    $fb->AddProperty( $attendee );
+Privileges:caldav-POST.php:    $vcal->VCalendar( array('METHOD' => 'REPLY') );
+Privileges:caldav-POST.php:    $vcal->AddComponent( $fb );
+Privileges:caldav-POST.php:    $response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+Privileges:caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+Privileges:caldav-POST.php:    $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+Privileges:caldav-POST.php:    $reply->CalDAVElement($response, "calendar-data", $vcal->Render() );
+Privileges:caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav' );
+Privileges:caldav-POST.php:  $request->XMLResponse( 200, $response );
+Privileges:caldav-POST.php:  $responses[] = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+Privileges:caldav-POST.php:  $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+Privileges:caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-POST.php:  $request->XMLResponse( 200, $response );
+Privileges:caldav-POST.php:$ical = new iCalComponent( $request->raw_post );
+Privileges:caldav-POST.php:$method =  $ical->GetPValue('METHOD');
+Privileges:caldav-POST.php:$resources = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REQUEST" method with "%s" component.', $method, $first->GetType() );
+Privileges:caldav-POST.php:    if ( $first->GetType() == 'VFREEBUSY' )
+Privileges:caldav-POST.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+Privileges:caldav-POST.php:      dbg_error_log('POST', 'Ignoring iTIP "REQUEST" with "%s" component.', $first->GetType() );
+Privileges:caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REPLY" with "%s" component.', $first->GetType() );
+Privileges:CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+Privileges:CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+Privileges:CalDAVPrincipal.php:  * @var Whether or not we are using an e-mail address based URL.
+Privileges:CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read permissions on.
+Privileges:CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read-write prmissions for.
+Privileges:CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have read permissions on this one.
+Privileges:CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have write permissions on this one.
+Privileges:CalDAVPrincipal.php:   * @var The dav_name for this principal - a partial path
+Privileges:CalDAVPrincipal.php:    $this->exists = null;
+Privileges:CalDAVPrincipal.php:    $this->url = null;
+Privileges:CalDAVPrincipal.php:    $this->by_email = false;
+Privileges:CalDAVPrincipal.php:      dbg_error_log( 'principal', 'Principal: record for %s', $parameters->username );
+Privileges:CalDAVPrincipal.php:      $this->user_no = $parameters['user_no'];
+Privileges:CalDAVPrincipal.php:        $this->username = $parameters['username'];
+Privileges:CalDAVPrincipal.php:        $this->user_no = $parameters['user_no'];
+Privileges:CalDAVPrincipal.php:        if ( $username = $this->UsernameFromEMail($parameters['email']) ) {
+Privileges:CalDAVPrincipal.php:          $this->username = $username;
+Privileges:CalDAVPrincipal.php:        if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
+Privileges:CalDAVPrincipal.php:          $this->username = $username;
+Privileges:CalDAVPrincipal.php:      else if ( isset($parameters['principal-property-search']) ) {
+Privileges:CalDAVPrincipal.php:        $usr = $this->PropertySearch($parameters['principal-property-search']);
+Privileges:CalDAVPrincipal.php:      $this->exists = false;
+Privileges:CalDAVPrincipal.php:    $this->exists = true;
+Privileges:CalDAVPrincipal.php:    $this->InitialiseRecord($usr);
+Privileges:CalDAVPrincipal.php:      $this->url = $parameters['path'];
+Privileges:CalDAVPrincipal.php:      $this->dav_name = $parameters['path'];
+Privileges:CalDAVPrincipal.php:      $this->{$k} = $v;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->modified) ) $this->modified = $this->updated;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->created) )  $this->created  = $this->joined;
+Privileges:CalDAVPrincipal.php:    $this->dav_etag = md5($this->username . $this->updated);
+Privileges:CalDAVPrincipal.php:    $this->_is_group = (isset($usr->type_id) && $usr->type_id == 3);
+Privileges:CalDAVPrincipal.php:    $this->principal_url = ConstructURL( '/'.$this->username.'/', true );
+Privileges:CalDAVPrincipal.php:    $this->url = $this->principal_url;
+Privileges:CalDAVPrincipal.php:    $this->principal_address = $this->principal_url . 'principal.vcf';
+Privileges:CalDAVPrincipal.php:    $this->user_address_set = array(
+Privileges:CalDAVPrincipal.php:       'mailto:'.$this->email,
+Privileges:CalDAVPrincipal.php:       ConstructURL( '/'.$this->username.'/', true ),
+Privileges:CalDAVPrincipal.php://       ConstructURL( '/~'.$this->username.'/', true ),
+Privileges:CalDAVPrincipal.php://       ConstructURL( '/__uuids__/'.$this->username.'/', true ),
+Privileges:CalDAVPrincipal.php:    $this->schedule_inbox_url = sprintf( '%s.in/', $this->url);
+Privileges:CalDAVPrincipal.php:    $this->schedule_outbox_url = sprintf( '%s.out/', $this->url);
+Privileges:CalDAVPrincipal.php:    $this->dropbox_url = sprintf( '%s.drop/', $this->url);
+Privileges:CalDAVPrincipal.php:    $this->notifications_url = sprintf( '%s.notify/', $this->url);
+Privileges:CalDAVPrincipal.php:    if ( isset ( $c->notifications_server ) ) {
+Privileges:CalDAVPrincipal.php:      $this->xmpp_uri = 'xmpp:pubsub.'.$c->notifications_server['host'].'?pubsub;node=/davical-'.$this->principal_id;
+Privileges:CalDAVPrincipal.php:      $this->xmpp_server = $c->notifications_server['host'];
+Privileges:CalDAVPrincipal.php:    if ( $this->_is_group ) {
+Privileges:CalDAVPrincipal.php:      $this->group_member_set = array();
+Privileges:CalDAVPrincipal.php:      $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=member_id) JOIN usr USING(user_no) WHERE group_id = :group_id ORDER BY principal.principal_id ', array( ':group_id' => $this->principal_id) );
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:        while( $member = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:          $this->group_member_set[] = ConstructURL( '/'. $member->username . '/', true);
+Privileges:CalDAVPrincipal.php:    $this->group_membership = array();
+Privileges:CalDAVPrincipal.php:    $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=group_id) JOIN usr USING(user_no) WHERE member_id = :member_id UNION SELECT usr.username FROM group_member LEFT JOIN grants ON (to_principal=group_id) JOIN principal ON (principal_id=by_principal) JOIN usr USING(user_no) WHERE member_id = :member_id and by_principal != member_id ORDER BY 1', array( ':member_id' => $this->principal_id ) );
+Privileges:CalDAVPrincipal.php:    if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:      while( $group = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:        $this->group_membership[] = ConstructURL( '/'. $group->username . '/', true);
+Privileges:CalDAVPrincipal.php:    $this->read_proxy_group = null;
+Privileges:CalDAVPrincipal.php:    $this->write_proxy_group = null;
+Privileges:CalDAVPrincipal.php:    $this->write_proxy_for = null;
+Privileges:CalDAVPrincipal.php:    $this->read_proxy_for = null;
+Privileges:CalDAVPrincipal.php:    dbg_error_log( 'principal', ' User: %s (%d) URL: %s, Home: %s, By Email: %d', $this->username, $this->user_no, $this->url, $this->by_email );
+Privileges:CalDAVPrincipal.php:    $this->read_proxy_group = array();
+Privileges:CalDAVPrincipal.php:    $this->write_proxy_group = array();
+Privileges:CalDAVPrincipal.php:    $this->write_proxy_for = array();
+Privileges:CalDAVPrincipal.php:    $this->read_proxy_for = array();
+Privileges:CalDAVPrincipal.php:    if ( !isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy === false ) {
+Privileges:CalDAVPrincipal.php:      $params = array( ':request_principal' => $this->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:          if ( (bindec($relationship->pprivs) & $write_priv) != 0 ) {
+Privileges:CalDAVPrincipal.php:            $this->write_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+Privileges:CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-write/', true);
+Privileges:CalDAVPrincipal.php:            $this->read_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+Privileges:CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-read/', true);
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:          if ( bindec($relationship->pprivs) & $write_priv ) {
+Privileges:CalDAVPrincipal.php:            $this->write_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+Privileges:CalDAVPrincipal.php:            $this->read_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+Privileges:CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-for:    %s', implode(',',$this->read_proxy_for) );
+Privileges:CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-for:   %s', implode(',',$this->write_proxy_for) );
+Privileges:CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-group:  %s', implode(',',$this->read_proxy_group) );
+Privileges:CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-group: %s', implode(',',$this->write_proxy_group) );
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+Privileges:CalDAVPrincipal.php:    return $this->read_proxy_group;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->write_proxy_group) ) $this->FetchProxyGroups();
+Privileges:CalDAVPrincipal.php:    return $this->write_proxy_group;
+Privileges:CalDAVPrincipal.php:  * @param string read/write - which sort of proxy list is requested.
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->read_proxy_for) ) $this->FetchProxyGroups();
+Privileges:CalDAVPrincipal.php:    if ( $type == 'write' ) return $this->write_proxy_for;
+Privileges:CalDAVPrincipal.php:    return $this->read_proxy_for;
+Privileges:CalDAVPrincipal.php:  * Accessor for the group membership - the groups this principal is a member of
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+Privileges:CalDAVPrincipal.php:    return $this->group_membership;
+Privileges:CalDAVPrincipal.php:  * Accessor for the group member set - the members of this group
+Privileges:CalDAVPrincipal.php:    if ( ! $this->_is_group ) return null;
+Privileges:CalDAVPrincipal.php:    return $this->group_member_set;
+Privileges:CalDAVPrincipal.php:  * @param array $options The request options, controlling whether e-mail paths are allowed.
+Privileges:CalDAVPrincipal.php:      return $session->username;
+Privileges:CalDAVPrincipal.php:      $username = $this->UsernameFromEMail($username);
+Privileges:CalDAVPrincipal.php:  * Work out the username, based on the given e-mail
+Privileges:CalDAVPrincipal.php:    @dbg_error_log( 'principal', 'Retrieving username from e-mail address "%s" ', $email );
+Privileges:CalDAVPrincipal.php:    if ( $qry->Exec('principal') && $user = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:      $username = $user->username;
+Privileges:CalDAVPrincipal.php:      $this->by_email = true;
+Privileges:CalDAVPrincipal.php:    return $this->exists;
+Privileges:CalDAVPrincipal.php:    return $this->_is_group;
+Privileges:CalDAVPrincipal.php:    return (isset($this->username)?$this->username:'username not set');
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->dav_name) ) {
+Privileges:CalDAVPrincipal.php:      if ( !isset($this->username) ) $this->dav_name = '';
+Privileges:CalDAVPrincipal.php:      else $this->dav_name = '/'.$this->username.'/';
+Privileges:CalDAVPrincipal.php:    return $this->dav_name;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->calendar_home_set) ) {
+Privileges:CalDAVPrincipal.php:      $this->calendar_home_set = array();
+Privileges:CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_calendar AND user_no = :user_no', array( ':user_no' => $this->user_no));
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+Privileges:CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:          while( $calendar = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:            $this->calendar_home_set[] = ConstructURL($calendar->parent_container, true);
+Privileges:CalDAVPrincipal.php:          $this->calendar_home_set[] = $this->principal_url;
+Privileges:CalDAVPrincipal.php:    return $this->calendar_home_set;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->addressbook_home_set) ) {
+Privileges:CalDAVPrincipal.php:     $this->addressbook_home_set = array();
+Privileges:CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_addressbook AND user_no = :user_no', array( ':user_no' => $this->user_no));
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+Privileges:CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+Privileges:CalDAVPrincipal.php:          while( $addressbook = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:            $this->addressbook_home_set[] = ConstructURL($addressbook->parent_container, true);
+Privileges:CalDAVPrincipal.php:          $this->addressbook_home_set[] = $this->principal_url;
+Privileges:CalDAVPrincipal.php:    return $this->addressbook_home_set;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->calendar_free_busy_set) ) {
+Privileges:CalDAVPrincipal.php:      * calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV
+Privileges:CalDAVPrincipal.php:      $this->calendar_free_busy_set = array();
+Privileges:CalDAVPrincipal.php:                        array( ':user_no' => $this->user_no) );
+Privileges:CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+Privileges:CalDAVPrincipal.php:        while( $calendar = $qry->Fetch() ) {
+Privileges:CalDAVPrincipal.php:          $this->calendar_free_busy_set[] = ConstructURL($calendar->dav_name, true);
+Privileges:CalDAVPrincipal.php:    return $this->calendar_free_busy_set;
+Privileges:CalDAVPrincipal.php:    if ( !isset($this->privileges) ) $this->privileges = 0;
+Privileges:CalDAVPrincipal.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+Privileges:CalDAVPrincipal.php:    return $this->privileges;
+Privileges:CalDAVPrincipal.php:                            'collection_id' => (isset($this->principal_id) ? $this->principal_id : 0),
+Privileges:CalDAVPrincipal.php:                            'type'     => 'principal' . (substr($this->dav_name(), 0, 12) == '/principals/'?'_link':''),
+Privileges:CalDAVPrincipal.php:                            'user_no'  => (isset($this->user_no)  ? $this->user_no : 0),
+Privileges:CalDAVPrincipal.php:                            'username' => $this->username(),
+Privileges:CalDAVPrincipal.php:                            'dav_name' => $this->dav_name,
+Privileges:CalDAVPrincipal.php:                            'email'    => (isset($this->email)    ? $this->email : ''),
+Privileges:CalDAVPrincipal.php:                            'created'  => (isset($this->created)  ? $this->created : date('Ymd\THis')),
+Privileges:CalDAVPrincipal.php:                            'updated'  => (isset($this->updated)  ? $this->updated : date('Ymd\THis'))
+Privileges:CalDAVPrincipal.php:    $collection->dav_etag = (isset($this->dav_etag) ? $this->dav_etag : md5($collection->username . $collection->updated));
+Privileges:CalDAVPrincipal.php:    $collection->dav_displayname =  (isset($this->dav_displayname) ? $this->dav_displayname : (isset($this->fullname) ? $this->fullname : $collection->username));
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('getcontenttype', 'httpd/unix-directory' );
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('displayname', $this->fullname );
+Privileges:CalDAVPrincipal.php:      case 'DAV::principal-URL':
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('principal-URL', $reply->href($this->principal_url) );
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('getlastmodified', ISODateToHTTPDate($this->modified) );
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('creationdate', DateToISODate($this->created) );
+Privileges:CalDAVPrincipal.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+Privileges:CalDAVPrincipal.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('getcontentlanguage', $locale );
+Privileges:CalDAVPrincipal.php:      case 'DAV::group-member-set':
+Privileges:CalDAVPrincipal.php:        if ( ! $this->_is_group ) return false;
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
+Privileges:CalDAVPrincipal.php:      case 'DAV::group-membership':
+Privileges:CalDAVPrincipal.php:        $prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+Privileges:CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+Privileges:CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
+Privileges:CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:dropbox-home-URL':
+Privileges:CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
+Privileges:CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-server':
+Privileges:CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+Privileges:CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
+Privileges:CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-uri':
+Privileges:CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+Privileges:CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:carddav:addressbook-home-set':
+Privileges:CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->addressbook_home_set() ) );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+Privileges:CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->calendar_home_set() ) );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+Privileges:CalDAVPrincipal.php:        $reply->CalDAVElement( $prop, 'calendar-free-busy-set', $reply->href( $this->calendar_free_busy_set() ) );
+Privileges:CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+Privileges:CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
+Privileges:CalDAVPrincipal.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+Privileges:CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url ) );
+Privileges:CalDAVPrincipal.php:      case 'DAV::principal-collection-set':
+Privileges:CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
+Privileges:CalDAVPrincipal.php:      case 'DAV::alternate-URI-set':
+Privileges:CalDAVPrincipal.php:        $prop->NewElement( $reply->Tag($tag));
+Privileges:CalDAVPrincipal.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+Privileges:CalDAVPrincipal.php:        $denied[] = $reply->Tag($tag);
+Privileges:CalDAVPrincipal.php:    dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
+Privileges:CalDAVPrincipal.php:      if ( ! $this->PrincipalProperty( $tag, $prop, $reply, $denied ) ) {
+Privileges:CalDAVPrincipal.php:        dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
+Privileges:CalDAVPrincipal.php:        $not_found[] = $reply->Tag($tag);
+Privileges:CalDAVPrincipal.php:    $href = $reply->href($this->url );
+Privileges:CalDAVPrincipal.php:        $noprop->NewElement( $v );
+Privileges:CalDAVPrincipal.php:        $noprop->NewElement( $v );
+Privileges:caldav-PROPFIND.php:* CalDAV Server - handle PROPFIND method
+Privileges:caldav-PROPFIND.php://$request->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read-current-user-privilege-set') );
+Privileges:caldav-PROPFIND.php:if ( !isset($request->xml_tags) ) {
+Privileges:caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body contains no XML data!") );
+Privileges:caldav-PROPFIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body is not valid XML data!") );
+Privileges:caldav-PROPFIND.php:$allprop    = $xmltree->GetPath('/DAV::propfind/*');
+Privileges:caldav-PROPFIND.php:  switch ( $propwrap->GetTag() ) {
+Privileges:caldav-PROPFIND.php:      $subprop = $propwrap->GetElements();
+Privileges:caldav-PROPFIND.php:        $property_list[] = $v->GetTag();
+Privileges:caldav-PROPFIND.php: * Add the calendar-proxy-read/write pseudocollections
+Privileges:caldav-PROPFIND.php:  if ($parent_path != $request->principal->dav_name()) {
+Privileges:caldav-PROPFIND.php:    dbg_error_log( 'PROPFIND', 'Not returning proxy response since "%s" != "%s"', $parent_path, $request->principal->dav_name() );
+Privileges:caldav-PROPFIND.php:    $proxy_group = $request->principal->ReadProxyGroup();
+Privileges:caldav-PROPFIND.php:    $proxy_group = $request->principal->WriteProxyGroup();
+Privileges:caldav-PROPFIND.php:  $collection->parent_container = $parent_path;
+Privileges:caldav-PROPFIND.php:  $collection->dav_name = $parent_path.'calendar-proxy-'.$which.'/';
+Privileges:caldav-PROPFIND.php:  $collection->is_calendar    = 'f';
+Privileges:caldav-PROPFIND.php:  $collection->is_addressbook = 'f';
+Privileges:caldav-PROPFIND.php:  $collection->is_principal   = 't';
+Privileges:caldav-PROPFIND.php:  $collection->is_proxy       = 't';
+Privileges:caldav-PROPFIND.php:  $collection->proxy_type     = $which;
+Privileges:caldav-PROPFIND.php:  $collection->type           = 'proxy';
+Privileges:caldav-PROPFIND.php:  $collection->dav_displayname = $collection->dav_name;
+Privileges:caldav-PROPFIND.php:  $collection->collection_id = 0;
+Privileges:caldav-PROPFIND.php:  $collection->user_no = $session->user_no;
+Privileges:caldav-PROPFIND.php:  $collection->username = $session->username;
+Privileges:caldav-PROPFIND.php:  $collection->email = $session->email;
+Privileges:caldav-PROPFIND.php:  $collection->created = date('Ymd\THis');
+Privileges:caldav-PROPFIND.php:  $collection->dav_etag = md5($c->system_name . $collection->dav_name . implode($proxy_group) );
+Privileges:caldav-PROPFIND.php:  $collection->proxy_for = $proxy_group;
+Privileges:caldav-PROPFIND.php:  $collection->resourcetypes  = sprintf('<DAV::collection/><http://calendarserver.org/ns/:calendar-proxy-%s/>', $which);
+Privileges:caldav-PROPFIND.php:  $collection->in_freebusy_set = 'f';
+Privileges:caldav-PROPFIND.php:  $collection->schedule_transp = 'transp';
+Privileges:caldav-PROPFIND.php:  $collection->timezone        = null;
+Privileges:caldav-PROPFIND.php:  $collection->description     = '';
+Privileges:caldav-PROPFIND.php:  $resource->FetchPrincipal();
+Privileges:caldav-PROPFIND.php:  return $resource->RenderAsXML($property_list, $reply);
+Privileges:caldav-PROPFIND.php:  $bound_from = $collection->bound_from();
+Privileges:caldav-PROPFIND.php:  $bound_to   = $collection->dav_name();
+Privileges:caldav-PROPFIND.php:  if ( !isset($parent_path) ) $parent_path = $collection->dav_name();
+Privileges:caldav-PROPFIND.php:                                                              $depth, $collection->dav_name(), $bound_from, $bound_to );
+Privileges:caldav-PROPFIND.php:  if ( ! $collection->IsCalendar() &&  ! $collection->IsAddressbook() ) {
+Privileges:caldav-PROPFIND.php:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:caldav-PROPFIND.php:      if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-PROPFIND.php:        while( $binding = $qry->Fetch() ) {
+Privileges:caldav-PROPFIND.php:          $resource = new DAVResource($binding->dav_name);
+Privileges:caldav-PROPFIND.php:          //if ( $resource->HavePrivilegeTo('DAV::read', false) ) {
+Privileges:caldav-PROPFIND.php:            $resource->set_bind_location( str_replace($bound_from,$bound_to,$binding->dav_name));
+Privileges:caldav-PROPFIND.php:            $responses[] = $resource->RenderAsXML($property_list, $reply);
+Privileges:caldav-PROPFIND.php:              $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource, $binding->dav_name ) );
+Privileges:caldav-PROPFIND.php:    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-PROPFIND.php:      while( $subcollection = $qry->Fetch() ) {
+Privileges:caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$subcollection->dav_name));
+Privileges:caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply);
+Privileges:caldav-PROPFIND.php:          $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource,
+Privileges:caldav-PROPFIND.php:                                                   str_replace($resource->parent_path(), $parent_path, $resource->dav_name() ) ) );
+Privileges:caldav-PROPFIND.php:    if ( $collection->IsPrincipal() ) {
+Privileges:caldav-PROPFIND.php:      // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
+Privileges:caldav-PROPFIND.php:      dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $bound_from );
+Privileges:caldav-PROPFIND.php:  //if ( $collection->HavePrivilegeTo('DAV::read', false) ) {
+Privileges:caldav-PROPFIND.php:    if ( $collection->IsCalendar() ) {
+Privileges:caldav-PROPFIND.php:      if ( ! $collection->HavePrivilegeTo('all', false) ) {
+Privileges:caldav-PROPFIND.php:      if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+Privileges:caldav-PROPFIND.php:        $time_limit_clause = " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+Privileges:caldav-PROPFIND.php:    if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY caldav_data.dav_id";
+Privileges:caldav-PROPFIND.php:    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-PROPFIND.php:      while( $item = $qry->Fetch() ) {
+Privileges:caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->dav_name));
+Privileges:caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+Privileges:caldav-PROPFIND.php:if ( $request->IsProxyRequest() ) {
+Privileges:caldav-PROPFIND.php:  $response = add_proxy_response($request->proxy_type, $request->principal->dav_name() );
+Privileges:caldav-PROPFIND.php:  $resource = new DAVResource($request->path);
+Privileges:caldav-PROPFIND.php:  if ( ! $resource->Exists() ) {
+Privileges:caldav-PROPFIND.php:    $request->PreconditionFailed( 404, 'must-exist', translate('That resource is not present on this server.') );
+Privileges:caldav-PROPFIND.php:  //$resource->NeedPrivilege('DAV::read');
+Privileges:caldav-PROPFIND.php:  if ( $resource->IsCollection() ) {
+Privileges:caldav-PROPFIND.php:    dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name() );
+Privileges:caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+Privileges:caldav-PROPFIND.php:    if ( $request->depth > 0 ) {
+Privileges:caldav-PROPFIND.php:      $responses = array_merge($responses, get_collection_contents( $request->depth - 1, $resource ) );
+Privileges:caldav-PROPFIND.php:  //elseif ( $request->HavePrivilegeTo('DAV::read',false) ) 
+Privileges:caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+Privileges:caldav-PROPFIND.php:$xmldoc = $reply->Render('multistatus', $responses);
+Privileges:caldav-PROPFIND.php:$request->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:caldav-PROPPATCH.php:* CalDAV Server - handle PROPPATCH method
+Privileges:caldav-PROPPATCH.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-PROPPATCH.php:$dav_resource = new DAVResource($request->path);
+Privileges:caldav-PROPPATCH.php:if ( ! ($dav_resource->HavePrivilegeTo('DAV::write-properties') || $dav_resource->IsBinding() ) ) {
+Privileges:caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+Privileges:caldav-PROPPATCH.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-PROPPATCH.php:// echo $xmltree->Render();
+Privileges:caldav-PROPPATCH.php:if ( $xmltree->GetTag() != "DAV::propertyupdate" ) {
+Privileges:caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+Privileges:caldav-PROPPATCH.php:$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
+Privileges:caldav-PROPPATCH.php:$rmprops  = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
+Privileges:caldav-PROPPATCH.php:* the special-case stuff as needed and falling through to a default which
+Privileges:caldav-PROPPATCH.php:$qry->Begin();
+Privileges:caldav-PROPPATCH.php:$setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+Privileges:caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+Privileges:caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+Privileges:caldav-PROPPATCH.php:      * Can't set displayname on resources - only collections or principals
+Privileges:caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() || $dav_resource->IsPrincipal() ) {
+Privileges:caldav-PROPPATCH.php:        if ( $dav_resource->IsBinding() ) {
+Privileges:caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name',
+Privileges:caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+Privileges:caldav-PROPPATCH.php:        else if ( $dav_resource->IsPrincipal() ) {
+Privileges:caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no',
+Privileges:caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':user_no' => $request->user_no) );
+Privileges:caldav-PROPPATCH.php:          $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name',
+Privileges:caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+Privileges:caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:      $setcollection  = count($setting->GetPath('DAV::resourcetype/DAV::collection'));
+Privileges:caldav-PROPPATCH.php:      $setaddressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+Privileges:caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal()
+Privileges:caldav-PROPPATCH.php:                            && ! $dav_resource->IsBinding() && ! ($setaddressbook && $setcalendar) ) {
+Privileges:caldav-PROPPATCH.php:        $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+Privileges:caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET is_calendar = :is_calendar::boolean, is_addressbook = :is_addressbook::boolean,
+Privileges:caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':resourcetypes' => $resourcetypes,
+Privileges:caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-calendar-transp':
+Privileges:caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && ( $dav_resource->IsCalendar() || $setcalendar ) && !$dav_resource->IsBinding() ) {
+Privileges:caldav-PROPPATCH.php:        $transparency = $setting->GetPath('urn:ietf:params:xml:ns:caldav:schedule-calendar-transp/*');
+Privileges:caldav-PROPPATCH.php:        $transparency = preg_replace( '{^.*:}', '', $transparency[0]->GetTag());
+Privileges:caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET schedule_transp = :transparency WHERE dav_name = :dav_name',
+Privileges:caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':transparency' => $transparency ) );
+Privileges:caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                                new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:                                translate("The CalDAV:schedule-calendar-transp property may only be set on calendars.") )
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+Privileges:caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:          new XMLElement( 'responsedescription', translate("The calendar-free-busy-set is superseded by the schedule-transp property of a calendar collection.") )
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+Privileges:caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+Privileges:caldav-PROPPATCH.php:        $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone');
+Privileges:caldav-PROPPATCH.php:        $tzstring = $tzcomponent[0]->GetContent();
+Privileges:caldav-PROPPATCH.php:        $timezones = $calendar->component->GetComponents('VTIMEZONE');
+Privileges:caldav-PROPPATCH.php:        $tzid = $tz->GetPValue('TZID');
+Privileges:caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name',
+Privileges:caldav-PROPPATCH.php:                                       array( ':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()) );
+Privileges:caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+Privileges:caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+Privileges:caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+Privileges:caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                               new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:                               translate("Property is read-only") )
+Privileges:caldav-PROPPATCH.php:      $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text)',
+Privileges:caldav-PROPPATCH.php:            array( ':dav_name' => $dav_resource->dav_name(), ':user_no' => $request->user_no, ':tag' => $tag, ':value' => $content) );
+Privileges:caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+Privileges:caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+Privileges:caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+Privileges:caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+Privileges:caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = NULL WHERE dav_name = :dav_name', array( ':dav_name' => $dav_resource->dav_name()) );
+Privileges:caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+Privileges:caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+Privileges:caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+Privileges:caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:CALENDAR-USER-ADDRESS-SET':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+Privileges:caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+Privileges:caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+Privileges:caldav-PROPPATCH.php:          new XMLElement('responsedescription', translate("Property is read-only") )
+Privileges:caldav-PROPPATCH.php:      $qry->QDo('DELETE FROM property WHERE dav_name=:dav_name AND property_name=:property_name',
+Privileges:caldav-PROPPATCH.php:                  array( ':dav_name' => $dav_resource->dav_name(), ':property_name' => $tag) );
+Privileges:caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+Privileges:caldav-PROPPATCH.php:  $qry->Rollback();
+Privileges:caldav-PROPPATCH.php:  $request->DoResponse( 207, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+Privileges:caldav-PROPPATCH.php:if ( $qry->Commit() ) {
+Privileges:caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+Privileges:caldav-PROPPATCH.php:  $request->DoResponse( 200, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+Privileges:caldav-PROPPATCH.php:$request->DoResponse( 500 );
+Privileges:caldav-PUT-default.php:* CalDAV Server - handle PUT method on unknown (arbitrary) content-types
+Privileges:caldav-PUT-default.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+Privileges:caldav-PUT-default.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-PUT-default.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-PUT-default.php:$dest = new DAVResource($request->path);
+Privileges:caldav-PUT-default.php:$container = $dest->FetchParentContainer();
+Privileges:caldav-PUT-default.php:if ( $container->IsCalendar() ) {
+Privileges:caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:caldav:supported-calendar-data',
+Privileges:caldav-PUT-default.php:                  translate('Incorrect content type for calendar: ') . $request->content_type );
+Privileges:caldav-PUT-default.php:else if ( $container->IsAddressbook() ) {
+Privileges:caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:carddav:supported-address-data',
+Privileges:caldav-PUT-default.php:                  translate('Incorrect content type for addressbook: ') . $request->content_type );
+Privileges:caldav-PUT-default.php:if ( ! $dest->Exists() ) {
+Privileges:caldav-PUT-default.php:  if ( $container->IsPrincipal() ) {
+Privileges:caldav-PUT-default.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+Privileges:caldav-PUT-default.php:  if ( ! $container->Exists() ) {
+Privileges:caldav-PUT-default.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+Privileges:caldav-PUT-default.php:  $container->NeedPrivilege('DAV::bind');
+Privileges:caldav-PUT-default.php:  if ( $dest->IsCollection() ) {
+Privileges:caldav-PUT-default.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+Privileges:caldav-PUT-default.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+Privileges:caldav-PUT-default.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+Privileges:caldav-PUT-default.php:  $dest->NeedPrivilege('DAV::write-content');
+Privileges:caldav-PUT-default.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+Privileges:caldav-PUT-default.php:  $request->DoResponse(412);
+Privileges:caldav-PUT-default.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+Privileges:caldav-PUT-default.php:  $request->DoResponse(412);
+Privileges:caldav-PUT-default.php:$collection_id = $container->GetProperty('collection_id');
+Privileges:caldav-PUT-default.php:$qry->Begin();
+Privileges:caldav-PUT-default.php:$etag = md5($request->raw_post);
+Privileges:caldav-PUT-default.php:    ':user_no' => $dest->GetProperty('user_no'),
+Privileges:caldav-PUT-default.php:    ':dav_name' => $dest->bound_from(),
+Privileges:caldav-PUT-default.php:    ':dav_data' => $request->raw_post,
+Privileges:caldav-PUT-default.php:    ':session_user' => $session->user_no
+Privileges:caldav-PUT-default.php:if ( $dest->Exists() ) {
+Privileges:caldav-PUT-default.php:$qry->QDo( $sql, $params );
+Privileges:caldav-PUT-default.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+Privileges:caldav-PUT-default.php:if ( !$qry->Exec('move') ) rollback(500);
+Privileges:caldav-PUT-default.php:$request->DoResponse( $response_code );
+Privileges:caldav-PUT-functions.php:* CalDAV Server - handle PUT method
+Privileges:caldav-PUT-functions.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-PUT-functions.php:$tz_regex = ':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z_]+$:i';
+Privileges:caldav-PUT-functions.php:  $qry->Rollback();
+Privileges:caldav-PUT-functions.php:    if ( isset($bad_events) && isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+Privileges:caldav-PUT-functions.php:      $request->DoResponse( $error_no, $message );
+Privileges:caldav-PUT-functions.php:  $c->messages[] = sprintf(translate('Status: %d, Message: %s, User: %d, Path: %s'), $error_no, $message, $user_no, $path);
+Privileges:caldav-PUT-functions.php:  if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+Privileges:caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) {
+Privileges:caldav-PUT-functions.php:    if ( !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+Privileges:caldav-PUT-functions.php:      if ( $qry->rows() == 0 ) {
+Privileges:caldav-PUT-functions.php:        $request->DoResponse( 405 ); // Method not allowed
+Privileges:caldav-PUT-functions.php:    if ( $qry->rows() == 0 ) {
+Privileges:caldav-PUT-functions.php:      $qry->QDo( $sql, $params );
+Privileges:caldav-PUT-functions.php:      $collection = $qry->Fetch();
+Privileges:caldav-PUT-functions.php:      $params = array( ':is_public' => ($public?'t':'f'), ':collection_id' => $collection->collection_id );
+Privileges:caldav-PUT-functions.php:      if ( ! $qry->QDo($sql,$params) ) {
+Privileges:caldav-PUT-functions.php:  if( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+Privileges:caldav-PUT-functions.php:    $collection = $qry->Fetch();
+Privileges:caldav-PUT-functions.php:    if ($collection->public_events_only == 't') {
+Privileges:caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+Privileges:caldav-PUT-functions.php:  $attendees = $ic->GetProperties('ATTENDEE');
+Privileges:caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+Privileges:caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+Privileges:caldav-PUT-functions.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+Privileges:caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+Privileges:caldav-PUT-functions.php:    if ( $attendee->GetParameterValue ( 'PARTSTAT' ) != 'NEEDS-ACTION' || preg_match ( '/^[35]\.[3-9]/',  $attendee->GetParameterValue ( 'SCHEDULE-STATUS' ) ) ) {
+Privileges:caldav-PUT-functions.php:      $attendee->SetParameterValue ('SCHEDULE-STATUS','3.7;Invalid Calendar User');
+Privileges:caldav-PUT-functions.php:    $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+Privileges:caldav-PUT-functions.php:    $priv =  $ar->HavePrivilegeTo('schedule-deliver-invite' );
+Privileges:caldav-PUT-functions.php:    if ( ! $ar->HavePrivilegeTo('schedule-deliver-invite' ) ){
+Privileges:caldav-PUT-functions.php:      $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+Privileges:caldav-PUT-functions.php:      $reply->NSElement( $privnodes[1], 'schedule-deliver-invite' );
+Privileges:caldav-PUT-functions.php:      $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+Privileges:caldav-PUT-functions.php:      $xmldoc = $reply->Render('error',$xml);
+Privileges:caldav-PUT-functions.php:      $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"');
+Privileges:caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+Privileges:caldav-PUT-functions.php:    $ncal->VCalendar ();
+Privileges:caldav-PUT-functions.php:    $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+Privileges:caldav-PUT-functions.php:    $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+Privileges:caldav-PUT-functions.php:    $content = $ncal->Render();
+Privileges:caldav-PUT-functions.php:    $cid = $ar->GetProperty('collection_id');
+Privileges:caldav-PUT-functions.php:    dbg_error_log('DELIVER', 'to user: %s, to path: %s, collection: %s, from user: %s, caldata %s', $attendee_principal->user_no, $deliver_path, $cid, $request->user_no, $content );
+Privileges:caldav-PUT-functions.php:    write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+Privileges:caldav-PUT-functions.php:      $content , $ar->GetProperty('collection_id'), $request->user_no,
+Privileges:caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+Privileges:caldav-PUT-functions.php:  $ncal->VCalendar ();
+Privileges:caldav-PUT-functions.php:  $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+Privileges:caldav-PUT-functions.php:  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+Privileges:caldav-PUT-functions.php:  $content = $ncal->Render();
+Privileges:caldav-PUT-functions.php: 	$deliver_path = preg_replace ( '/^.*caldav.php/','', $request->principal->schedule_inbox_url );
+Privileges:caldav-PUT-functions.php:  write_resource( $request->user_no, $deliver_path . $etag . '.ics' ,
+Privileges:caldav-PUT-functions.php:    $content , $ar->GetProperty('collection_id'), $request->user_no,
+Privileges:caldav-PUT-functions.php:  header('Schedule-Tag: "'.$etag . '"' );
+Privileges:caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+Privileges:caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+Privileges:caldav-PUT-functions.php:	$organizer = $ic->GetProperties('ORGANIZER');
+Privileges:caldav-PUT-functions.php:  $attendees = array_merge($organizer,$ic->GetProperties('ATTENDEE'));
+Privileges:caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+Privileges:caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+Privileges:caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+Privileges:caldav-PUT-functions.php:	  $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+Privileges:caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+Privileges:caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+Privileges:caldav-PUT-functions.php:	  if ( ! $ar->HavePrivilegeTo('schedule-deliver-reply' ) ){
+Privileges:caldav-PUT-functions.php:	     $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+Privileges:caldav-PUT-functions.php:	     $reply->NSElement( $privnodes[1], 'schedule-deliver-reply' );
+Privileges:caldav-PUT-functions.php:	     $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+Privileges:caldav-PUT-functions.php:	     $xmldoc = $reply->Render('error',$xml);
+Privileges:caldav-PUT-functions.php:			 $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:caldav-PUT-functions.php:	  $ncal->VCalendar ();
+Privileges:caldav-PUT-functions.php:	  $ncal->AddProperty ( 'METHOD', 'REPLY' );
+Privileges:caldav-PUT-functions.php:	  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+Privileges:caldav-PUT-functions.php:	  $content = $ncal->Render();
+Privileges:caldav-PUT-functions.php:	  write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+Privileges:caldav-PUT-functions.php:	    $content , $ar->GetProperty('collection_id'), $request->user_no,
+Privileges:caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+Privileges:caldav-PUT-functions.php:* @return float The result of the scheduling request, per caldav-sched #3.5.4
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+Privileges:caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+Privileges:caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+Privileges:caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+Privileges:caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+Privileges:caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+Privileges:caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+Privileges:caldav-PUT-functions.php:  if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put'])) ) {
+Privileges:caldav-PUT-functions.php:    $fh = fopen('/tmp/PUT-2.txt','w');
+Privileges:caldav-PUT-functions.php:  $timezones = $calendar->GetComponents('VTIMEZONE',true);
+Privileges:caldav-PUT-functions.php:  $components = $calendar->GetComponents('VTIMEZONE',false);
+Privileges:caldav-PUT-functions.php:  $displayname = $calendar->GetPValue('X-WR-CALNAME');
+Privileges:caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+Privileges:caldav-PUT-functions.php:    $tz_ids[$tz->GetPValue('TZID')] = $k;
+Privileges:caldav-PUT-functions.php:    $uid = $comp->GetPValue('UID');
+Privileges:caldav-PUT-functions.php:    $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
+Privileges:caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $comp->GetPParamValue('DUE','TZID');
+Privileges:caldav-PUT-functions.php:  if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+Privileges:caldav-PUT-functions.php:  if ( ! $qry->rows() == 1 ) {
+Privileges:caldav-PUT-functions.php:  $collection = $qry->Fetch();
+Privileges:caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) $qry->Begin();
+Privileges:caldav-PUT-functions.php:  $base_params = array( ':collection_id' => $collection->collection_id );
+Privileges:caldav-PUT-functions.php:    if ( !$qry->QDo('DELETE FROM calendar_item WHERE collection_id = :collection_id', $base_params)
+Privileges:caldav-PUT-functions.php:      || !$qry->QDo('DELETE FROM caldav_data WHERE collection_id = :collection_id', $base_params) )
+Privileges:caldav-PUT-functions.php:      rollback_on_error( $caldav_context, $user_no, $collection->collection_id );
+Privileges:caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Begin();
+Privileges:caldav-PUT-functions.php:    $vcal->VCalendar();
+Privileges:caldav-PUT-functions.php:    $vcal->SetComponents($resource);
+Privileges:caldav-PUT-functions.php:    $icalendar = $vcal->Render();
+Privileges:caldav-PUT-functions.php:    $dav_data_params[':caldav_type'] = $first->GetType();
+Privileges:caldav-PUT-functions.php:    $dav_data_params[':session_user'] = $session->user_no;
+Privileges:caldav-PUT-functions.php:    if ( !$qry->QDo($dav_data_insert,$dav_data_params) ) rollback_on_error( $caldav_context, $user_no, $path );
+Privileges:caldav-PUT-functions.php:    $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $dav_data_params[':dav_name']));
+Privileges:caldav-PUT-functions.php:    if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+Privileges:caldav-PUT-functions.php:      $dav_id = $row->dav_id;
+Privileges:caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DTSTART');
+Privileges:caldav-PUT-functions.php:    if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+Privileges:caldav-PUT-functions.php:      $dtstart = $first->GetPValue('DUE');
+Privileges:caldav-PUT-functions.php:    $dtend = $first->GetPValue('DTEND');
+Privileges:caldav-PUT-functions.php:      dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+Privileges:caldav-PUT-functions.php:      if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+Privileges:caldav-PUT-functions.php:        $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+Privileges:caldav-PUT-functions.php:      elseif ( $first->GetType() == 'VEVENT' ) {
+Privileges:caldav-PUT-functions.php:        * non-inclusive end is the end of the calendar date specified by the
+Privileges:caldav-PUT-functions.php:        * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+Privileges:caldav-PUT-functions.php:        $value_type = $first->GetPParamValue('DTSTART','VALUE');
+Privileges:caldav-PUT-functions.php:    $last_modified = $first->GetPValue('LAST-MODIFIED');
+Privileges:caldav-PUT-functions.php:    $dtstamp = $first->GetPValue('DTSTAMP');
+Privileges:caldav-PUT-functions.php:    $class = ($collection->public_events_only == 't' ? 'PUBLIC' : $first->GetPValue('CLASS') );
+Privileges:caldav-PUT-functions.php:    $tzid = $first->GetPParamValue('DTSTART','TZID');
+Privileges:caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+Privileges:caldav-PUT-functions.php:        $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+Privileges:caldav-PUT-functions.php:          $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+Privileges:caldav-PUT-functions.php:      if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+Privileges:caldav-PUT-functions.php:        $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+Privileges:caldav-PUT-functions.php:        $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+Privileges:caldav-PUT-functions.php:    $calitem_params[':uid'] = $first->GetPValue('UID');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':location'] = $first->GetPValue('LOCATION');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':url'] = $first->GetPValue('URL');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':due'] = $first->GetPValue('DUE');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+Privileges:caldav-PUT-functions.php:    $calitem_params[':status'] = $first->GetPValue('STATUS');
+Privileges:caldav-PUT-functions.php:    $created = $first->GetPValue('CREATED');
+Privileges:caldav-PUT-functions.php:    if ( !$qry->QDo($sql,$calitem_params) ) rollback_on_error( $caldav_context, $user_no, $path);
+Privileges:caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Commit();
+Privileges:caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) {
+Privileges:caldav-PUT-functions.php:    if ( ! $qry->Commit() ) rollback_on_error( $caldav_context, $user_no, $path);
+Privileges:caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+Privileges:caldav-PUT-functions.php:  $alarms = $ical->GetComponents('VALARM');
+Privileges:caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_alarm ( dav_id, action, trigger, summary, description, component, next_trigger )
+Privileges:caldav-PUT-functions.php:  $qry->Prepare();
+Privileges:caldav-PUT-functions.php:    $trigger = array_merge($v->GetProperties('TRIGGER'));
+Privileges:caldav-PUT-functions.php:    $trigger_type = $trigger->GetParameterValue('VALUE');
+Privileges:caldav-PUT-functions.php:      switch ( $trigger->GetParameterValue('RELATED') ) {
+Privileges:caldav-PUT-functions.php:        case 'DTEND':  $related = $ical->GetPValue('DTEND'); break;
+Privileges:caldav-PUT-functions.php:        case 'DUE':    $related = $ical->GetPValue('DUE');   break;
+Privileges:caldav-PUT-functions.php:        default:       $related = $ical->GetPValue('DTSTART');
+Privileges:caldav-PUT-functions.php:      $duration = $trigger->Value();
+Privileges:caldav-PUT-functions.php:      $minus = (substr($duration,0,1) == '-');
+Privileges:caldav-PUT-functions.php:      $related_trigger = trim(preg_replace( '#[PT-]#', ' ', $duration ));
+Privileges:caldav-PUT-functions.php:        $related_trigger = preg_replace( '{(\d+[WDHMS])}', '-$1 ', $related_trigger );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':action', $v->GetPValue('ACTION'));
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':trigger', $trigger->Render());
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':summary', $v->GetPValue('SUMMARY'));
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':description', $v->GetPValue('DESCRIPTION'));
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':component', $v->Render());
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':related', $related );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':related_trigger', $related_trigger );
+Privileges:caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+Privileges:caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+Privileges:caldav-PUT-functions.php:  $attendees = $ical->GetProperties('ATTENDEE');
+Privileges:caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_attendee ( dav_id, status, partstat, cn, attendee, role, rsvp, property )
+Privileges:caldav-PUT-functions.php:  $qry->Prepare();
+Privileges:caldav-PUT-functions.php:    $attendee = $v->Value();
+Privileges:caldav-PUT-functions.php:      dbg_error_log( 'LOG', 'Duplicate: "%s"', $v->Render() );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':attendee', $attendee );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':status',   $v->GetParameterValue('STATUS') );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':partstat', $v->GetParameterValue('PARTSTAT') );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':cn',       $v->GetParameterValue('CN') );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':role',     $v->GetParameterValue('ROLE') );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':rsvp',     $v->GetParameterValue('RSVP') );
+Privileges:caldav-PUT-functions.php:    $qry->Bind(':property', $v->Render() );
+Privileges:caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+Privileges:caldav-PUT-functions.php:    $processed[$attendee] = $v->Render();
+Privileges:caldav-PUT-functions.php:  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+Privileges:caldav-PUT-functions.php:    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+Privileges:caldav-PUT-functions.php:    $resource_type = $first->GetType();
+Privileges:caldav-PUT-functions.php:  $qry->Begin();
+Privileges:caldav-PUT-functions.php:  if ( !$qry->QDo($sql,$params) ) {
+Privileges:caldav-PUT-functions.php:  $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
+Privileges:caldav-PUT-functions.php:  if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+Privileges:caldav-PUT-functions.php:    $dav_id = $row->dav_id;
+Privileges:caldav-PUT-functions.php:  $dtstart = $first->GetPValue('DTSTART');
+Privileges:caldav-PUT-functions.php:  if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+Privileges:caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DUE');
+Privileges:caldav-PUT-functions.php:  $dtend = $first->GetPValue('DTEND');
+Privileges:caldav-PUT-functions.php:    dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+Privileges:caldav-PUT-functions.php:    if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+Privileges:caldav-PUT-functions.php:      $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+Privileges:caldav-PUT-functions.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+Privileges:caldav-PUT-functions.php:      * non-inclusive end is the end of the calendar date specified by the
+Privileges:caldav-PUT-functions.php:      * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+Privileges:caldav-PUT-functions.php:      $value_type = $first->GetPParamValue('DTSTART','VALUE');
+Privileges:caldav-PUT-functions.php:  $last_modified = $first->GetPValue('LAST-MODIFIED');
+Privileges:caldav-PUT-functions.php:  $dtstamp = $first->GetPValue('DTSTAMP');
+Privileges:caldav-PUT-functions.php:  $class = $first->GetPValue('CLASS');
+Privileges:caldav-PUT-functions.php:  $tzid = $first->GetPParamValue('DTSTART','TZID');
+Privileges:caldav-PUT-functions.php:  if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+Privileges:caldav-PUT-functions.php:  $timezones = $ic->GetComponents('VTIMEZONE');
+Privileges:caldav-PUT-functions.php:    if ( $tz->GetPValue('TZID') != $tzid ) {
+Privileges:caldav-PUT-functions.php:      dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
+Privileges:caldav-PUT-functions.php:      $tzid = $tz->GetPValue('TZID');
+Privileges:caldav-PUT-functions.php:    $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+Privileges:caldav-PUT-functions.php:        $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+Privileges:caldav-PUT-functions.php:    if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+Privileges:caldav-PUT-functions.php:      $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+Privileges:caldav-PUT-functions.php:      $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+Privileges:caldav-PUT-functions.php:  $created = $first->GetPValue('CREATED');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':uid'] = $first->GetPValue('UID');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':location'] = $first->GetPValue('LOCATION');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':url'] = $first->GetPValue('URL');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':due'] = $first->GetPValue('DUE');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+Privileges:caldav-PUT-functions.php:  $calitem_params[':status'] = $first->GetPValue('STATUS');
+Privileges:caldav-PUT-functions.php:    log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+Privileges:caldav-PUT-functions.php:            $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+Privileges:caldav-PUT-functions.php:  if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
+Privileges:caldav-PUT-functions.php:  $qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
+Privileges:caldav-PUT-functions.php:  $qry->Commit();
+Privileges:caldav-PUT-functions.php:  if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+Privileges:caldav-PUT-functions.php:    $collection = $qry->Fetch();
+Privileges:caldav-PUT-functions.php:    $user_no = $collection->user_no;
+Privileges:caldav-PUT-functions.php:    return write_resource( $user_no, $path, $caldav_data, $collection->collection_id, $user_no, $etag, $ic, $put_action_type, false, $write_action_log );
+Privileges:caldav-PUT.php:* CalDAV Server - handle PUT method
+Privileges:caldav-PUT.php:$dav_resource = new DAVResource($request->path);
+Privileges:caldav-PUT.php:if ( ! $dav_resource->HavePrivilegeTo('DAV::write-content') ) {
+Privileges:caldav-PUT.php:  $request->DoResponse(403);
+Privileges:caldav-PUT.php:if ( ! $dav_resource->Exists() && ! $dav_resource->HavePrivilegeTo('DAV::bind') ) {
+Privileges:caldav-PUT.php:  $request->DoResponse(403);
+Privileges:caldav-PUT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+Privileges:caldav-PUT.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-PUT.php:include_once('caldav-PUT-functions.php');
+Privileges:caldav-PUT.php:controlRequestContainer( $dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
+Privileges:caldav-PUT.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-PUT.php:if ( $dav_resource->IsCollection()  ) {
+Privileges:caldav-PUT.php:  if ( $dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+Privileges:caldav-PUT.php:    $request->DoResponse( 405 ); // Method not allowed
+Privileges:caldav-PUT.php:  * as an import. The code is in caldav-PUT-functions.php
+Privileges:caldav-PUT.php:  import_collection($request->raw_post,$request->user_no,$request->path,true, $appending);
+Privileges:caldav-PUT.php:  $request->DoResponse( 200 );
+Privileges:caldav-PUT.php:$etag = md5($request->raw_post);
+Privileges:caldav-PUT.php:$ic = new iCalComponent( $request->raw_post );
+Privileges:caldav-PUT.php:if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
+Privileges:caldav-PUT.php:  $request->PreconditionFailed(412,'if-match');
+Privileges:caldav-PUT.php:if ( $dav_resource->Exists() ) {
+Privileges:caldav-PUT.php:  if ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $dav_resource->unique_tag() ) {
+Privileges:caldav-PUT.php:    $request->PreconditionFailed(412,'if-match', translate( 'Existing resource does not match "If-Match" header - not accepted.'));
+Privileges:caldav-PUT.php:  else if ( isset($request->etag_none_match) && $request->etag_none_match != ''
+Privileges:caldav-PUT.php:               && ($request->etag_none_match == $dav_resource->unique_tag() || $request->etag_none_match == '*') ) {
+Privileges:caldav-PUT.php:    * (without the If-None-Match header) on that resource, or if "*" is
+Privileges:caldav-PUT.php:    $request->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
+Privileges:caldav-PUT.php:$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
+Privileges:caldav-PUT.php:write_resource( $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), $request->raw_post, $dav_resource->GetProperty('collection_id'),
+Privileges:caldav-PUT.php:                                $session->user_no, $etag, $ic, $put_action_type, true, true );
+Privileges:caldav-PUT.php:$request->DoResponse( ($dav_resource->Exists() ? 204 : 201) );
+Privileges:caldav-PUT-vcard.php:* CalDAV Server - handle PUT method on VCARD content-types
+Privileges:caldav-PUT-vcard.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+Privileges:caldav-PUT-vcard.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-PUT-vcard.php:$lock_opener = $request->FailIfLocked();
+Privileges:caldav-PUT-vcard.php:$dest = new DAVResource($request->path);
+Privileges:caldav-PUT-vcard.php:$container = $dest->FetchParentContainer();
+Privileges:caldav-PUT-vcard.php:if ( ! $dest->Exists() ) {
+Privileges:caldav-PUT-vcard.php:  if ( $container->IsPrincipal() ) {
+Privileges:caldav-PUT-vcard.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+Privileges:caldav-PUT-vcard.php:  if ( ! $container->Exists() ) {
+Privileges:caldav-PUT-vcard.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+Privileges:caldav-PUT-vcard.php:  $container->NeedPrivilege('DAV::bind');
+Privileges:caldav-PUT-vcard.php:  if ( $dest->IsCollection() ) {
+Privileges:caldav-PUT-vcard.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+Privileges:caldav-PUT-vcard.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+Privileges:caldav-PUT-vcard.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+Privileges:caldav-PUT-vcard.php:  $dest->NeedPrivilege('DAV::write-content');
+Privileges:caldav-PUT-vcard.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+Privileges:caldav-PUT-vcard.php:  $request->DoResponse(412);
+Privileges:caldav-PUT-vcard.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+Privileges:caldav-PUT-vcard.php:  $request->DoResponse(412);
+Privileges:caldav-PUT-vcard.php:$collection_id = $container->GetProperty('collection_id');
+Privileges:caldav-PUT-vcard.php:$qry->Begin();
+Privileges:caldav-PUT-vcard.php:$etag = md5($request->raw_post);
+Privileges:caldav-PUT-vcard.php:    ':user_no' => $dest->GetProperty('user_no'),
+Privileges:caldav-PUT-vcard.php:    ':dav_name' => $dest->bound_from(),
+Privileges:caldav-PUT-vcard.php:    ':dav_data' => $request->raw_post,
+Privileges:caldav-PUT-vcard.php:    ':session_user' => $session->user_no
+Privileges:caldav-PUT-vcard.php:if ( $dest->Exists() ) {
+Privileges:caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+Privileges:caldav-PUT-vcard.php:  $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']) );
+Privileges:caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+Privileges:caldav-PUT-vcard.php:  $qry->QDo("SELECT currval('dav_id_seq') AS dav_id" );
+Privileges:caldav-PUT-vcard.php:$row = $qry->Fetch();
+Privileges:caldav-PUT-vcard.php:$vcard = new vCard( $request->raw_post );
+Privileges:caldav-PUT-vcard.php:$vcard->Write( $row->dav_id, $dest->Exists() );
+Privileges:caldav-PUT-vcard.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+Privileges:caldav-PUT-vcard.php:if ( !$qry->QDo('COMMIT') ) rollback(500);
+Privileges:caldav-PUT-vcard.php:$request->DoResponse( $response_code );
+Privileges:caldav-REPORT-calquery.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+Privileges:caldav-REPORT-calquery.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+Privileges:caldav-REPORT-calquery.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+Privileges:caldav-REPORT-calquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-query');
+Privileges:caldav-REPORT-calquery.php:$proptype = $qry_content[0]->GetTag();
+Privileges:caldav-REPORT-calquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/'.$proptype.'/*');
+Privileges:caldav-REPORT-calquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-calquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+Privileges:caldav-REPORT-calquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+Privileges:caldav-REPORT-calquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+Privileges:caldav-REPORT-calquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-calquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+Privileges:caldav-REPORT-calquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+Privileges:caldav-REPORT-calquery.php: * There can only be *one* FILTER element, and it must contain *one* COMP-FILTER
+Privileges:caldav-REPORT-calquery.php: * element.  In every case I can see this contained COMP-FILTER element will be a
+Privileges:caldav-REPORT-calquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/urn:ietf:params:xml:ns:caldav:filter/*');
+Privileges:caldav-REPORT-calquery.php:  if ( $qry_filters->GetTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $qry_filters->GetAttribute("name") == "VCALENDAR" )
+Privileges:caldav-REPORT-calquery.php:    $qry_filters = $qry_filters->GetContent();  // Everything is inside a VCALENDAR AFAICS
+Privileges:caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("name") );
+Privileges:caldav-REPORT-calquery.php:  dbg_error_log("calquery","Applying filter for item '%s'", $item->dav_name );
+Privileges:caldav-REPORT-calquery.php:  $ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
+Privileges:caldav-REPORT-calquery.php:  return $ical->TestFilter($filters);
+Privileges:caldav-REPORT-calquery.php:    $tag = $v->GetTag();
+Privileges:caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
+Privileges:caldav-REPORT-calquery.php:        $not_defined = "not-"; // then fall through to IS-DEFINED case
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-defined':
+Privileges:caldav-REPORT-calquery.php:          dbg_error_log("calquery", "Could not handle 'is-%sdefined' on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
+Privileges:caldav-REPORT-calquery.php:            case 'completed':  /** @todo when it can be handled in the SQL - see around line 200 below */
+Privileges:caldav-REPORT-calquery.php:              if ( ! $target_collection->IsSchedulingCollection() ) {
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:time-range':
+Privileges:caldav-REPORT-calquery.php:        $start_column = ($components[sizeof($components)-1] == 'VTODO' ? "due" : 'dtend');     // The column we compare against the START attribute
+Privileges:caldav-REPORT-calquery.php:        $start = $v->GetAttribute("start");
+Privileges:caldav-REPORT-calquery.php:        $finish = $v->GetAttribute("end");
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:text-match':
+Privileges:caldav-REPORT-calquery.php:        $search = $v->GetContent();
+Privileges:caldav-REPORT-calquery.php:        $negate = $v->GetAttribute("negate-condition");
+Privileges:caldav-REPORT-calquery.php:        $collation = $v->GetAttribute("collation");
+Privileges:caldav-REPORT-calquery.php:          case 'i;ascii-casemap':
+Privileges:caldav-REPORT-calquery.php:        dbg_error_log("calquery", " text-match: (%s IS NULL OR %s%s %s '%s') ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:comp-filter':
+Privileges:caldav-REPORT-calquery.php:        $comp_filter_name = $v->GetAttribute("name");
+Privileges:caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:prop-filter':
+Privileges:caldav-REPORT-calquery.php:        $propertyname = $v->GetAttribute("name");
+Privileges:caldav-REPORT-calquery.php:          case 'PERCENT-COMPLETE':
+Privileges:caldav-REPORT-calquery.php:            dbg_error_log("calquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+Privileges:caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+Privileges:caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:param-filter':
+Privileges:caldav-REPORT-calquery.php:        return false; // Can't handle PARAM-FILTER conditions in the SQL
+Privileges:caldav-REPORT-calquery.php:        $parameter = $v->GetAttribute("name");
+Privileges:caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+Privileges:caldav-REPORT-calquery.php:$target_collection = new DAVResource($request->path);
+Privileges:caldav-REPORT-calquery.php:$bound_from = $target_collection->bound_from();
+Privileges:caldav-REPORT-calquery.php:if ( !$target_collection->Exists() ) {
+Privileges:caldav-REPORT-calquery.php:  $request->DoResponse( 404 );
+Privileges:caldav-REPORT-calquery.php:if ( ! ($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection()) ) {
+Privileges:caldav-REPORT-calquery.php:  $request->DoResponse( 403, translate('The calendar-query report must be run against a calendar or a scheduling collection') );
+Privileges:caldav-REPORT-calquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+Privileges:caldav-REPORT-calquery.php:if ( $target_collection->Privileges() != privilege_to_bits('DAV::all') ) {
+Privileges:caldav-REPORT-calquery.php:if ( isset($c->hide_TODO) && $c->hide_TODO && ! $target_collection->HavePrivilegeTo('DAV::write-content') ) {
+Privileges:caldav-REPORT-calquery.php:if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+Privileges:caldav-REPORT-calquery.php:  $where .= " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+Privileges:caldav-REPORT-calquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+Privileges:caldav-REPORT-calquery.php:if ( $qry->Exec("calquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-REPORT-calquery.php:  while( $calendar_object = $qry->Fetch() ) {
+Privileges:caldav-REPORT-calquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+Privileges:caldav-REPORT-calquery.php:        $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+Privileges:caldav-REPORT-calquery.php:        $vResource = new vComponent($calendar_object->caldav_data);
+Privileges:caldav-REPORT-calquery.php:        $calendar_object->caldav_data = $expanded->Render();
+Privileges:caldav-REPORT-calquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-calquery.php:$request->XMLResponse( 207, $multistatus );
+Privileges:caldav-REPORT-cardquery.php:  $expansion = $address_data_xml->GetElements();
+Privileges:caldav-REPORT-cardquery.php:    $address_data_properties[strtoupper($v->GetAttribute('name'))] = true;
+Privileges:caldav-REPORT-cardquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-query');
+Privileges:caldav-REPORT-cardquery.php:$proptype = $qry_content[0]->GetTag();
+Privileges:caldav-REPORT-cardquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/'.$proptype.'/*');
+Privileges:caldav-REPORT-cardquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-cardquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+Privileges:caldav-REPORT-cardquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+Privileges:caldav-REPORT-cardquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+Privileges:caldav-REPORT-cardquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-cardquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+Privileges:caldav-REPORT-cardquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+Privileges:caldav-REPORT-cardquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/urn:ietf:params:xml:ns:carddav:filter/*');
+Privileges:caldav-REPORT-cardquery.php:  dbg_error_log("cardquery","Applying filter for item '%s'", $item->dav_name );
+Privileges:caldav-REPORT-cardquery.php:  $vcard = new vComponent( $item->caldav_data );
+Privileges:caldav-REPORT-cardquery.php:  return $vcard->TestFilter($filters);
+Privileges:caldav-REPORT-cardquery.php:    $tag = $v->GetTag();
+Privileges:caldav-REPORT-cardquery.php:    dbg_error_log("cardquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+Privileges:caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:text-match':
+Privileges:caldav-REPORT-cardquery.php:        $search = $v->GetContent();
+Privileges:caldav-REPORT-cardquery.php:        $negate = $v->GetAttribute("negate-condition");
+Privileges:caldav-REPORT-cardquery.php:        $collation = $v->GetAttribute("collation");
+Privileges:caldav-REPORT-cardquery.php:          case 'i;ascii-casemap':
+Privileges:caldav-REPORT-cardquery.php:          case 'i;unicode-casemap':
+Privileges:caldav-REPORT-cardquery.php:        dbg_error_log("cardquery", " text-match: (%s%s %s '%s') ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+Privileges:caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:prop-filter':
+Privileges:caldav-REPORT-cardquery.php:        $propertyname = $v->GetAttribute("name");
+Privileges:caldav-REPORT-cardquery.php:            dbg_error_log("cardquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+Privileges:caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+Privileges:caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:param-filter':
+Privileges:caldav-REPORT-cardquery.php:        return false; /** Figure out how to handle PARAM-FILTER conditions in the SQL */
+Privileges:caldav-REPORT-cardquery.php:        $parameter = $v->GetAttribute("name");
+Privileges:caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+Privileges:caldav-REPORT-cardquery.php:$target_collection = new DAVResource($request->path);
+Privileges:caldav-REPORT-cardquery.php:$bound_from = $target_collection->bound_from();
+Privileges:caldav-REPORT-cardquery.php:if ( !$target_collection->Exists() ) {
+Privileges:caldav-REPORT-cardquery.php:  $request->DoResponse( 404 );
+Privileges:caldav-REPORT-cardquery.php:if ( ! ($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection()) ) {
+Privileges:caldav-REPORT-cardquery.php:  $request->DoResponse( 403, translate('The addressbook-query report must be run against an addressbook collection') );
+Privileges:caldav-REPORT-cardquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+Privileges:caldav-REPORT-cardquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+Privileges:caldav-REPORT-cardquery.php:if ( $qry->Exec("cardquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-REPORT-cardquery.php:  while( $address_object = $qry->Fetch() ) {
+Privileges:caldav-REPORT-cardquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+Privileges:caldav-REPORT-cardquery.php:        $address_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $address_object->dav_name);
+Privileges:caldav-REPORT-cardquery.php:        $vcard = new VCard($address_object->caldav_data);
+Privileges:caldav-REPORT-cardquery.php:        $vcard->MaskProperties($address_data_properties);
+Privileges:caldav-REPORT-cardquery.php:        $address_object->caldav_data = $vcard->Render();
+Privileges:caldav-REPORT-cardquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-cardquery.php:$request->XMLResponse( 207, $multistatus );
+Privileges:caldav-REPORT-expand-property.php:  $propstat_set = $multistatus_response->GetElements('propstat');
+Privileges:caldav-REPORT-expand-property.php:    $status = $v->GetElements('status');
+Privileges:caldav-REPORT-expand-property.php:    if ( preg_match( '{^HTTP/\S+\s+200}', $status[0]->GetContent() ) ) {
+Privileges:caldav-REPORT-expand-property.php:    $props = $propstat_200->GetElements('prop');
+Privileges:caldav-REPORT-expand-property.php:      $properties = array_merge($properties,$p->GetElements());
+Privileges:caldav-REPORT-expand-property.php://      dbg_error_log('REPORT',' get_href_containers: Checking property "%s" for hrefs.', $property->GetNSTag() );
+Privileges:caldav-REPORT-expand-property.php:      $hrefs = $property->GetElements('href');
+Privileges:caldav-REPORT-expand-property.php:      $pname = $property->GetAttribute('name');
+Privileges:caldav-REPORT-expand-property.php:      $pns = $property->GetAttribute('namespace');
+Privileges:caldav-REPORT-expand-property.php:      $subtrees[$pname] = $property->GetElements();
+Privileges:caldav-REPORT-expand-property.php:    $part_response = $resource->RenderAsXML( $props, $reply );
+Privileges:caldav-REPORT-expand-property.php:            $hrefs = $property->GetElements();
+Privileges:caldav-REPORT-expand-property.php:            $pname = $property->GetTag();
+Privileges:caldav-REPORT-expand-property.php:            $pns = $property->GetAttribute('xmlns');
+Privileges:caldav-REPORT-expand-property.php:              $content = $v->GetContent();
+Privileges:caldav-REPORT-expand-property.php:            $property->SetContent( expand_properties($paths, $subtrees[$pname], $reply, false) );
+Privileges:caldav-REPORT-expand-property.php:$property_tree = $xmltree->GetPath('/DAV::expand-property/DAV::property');
+Privileges:caldav-REPORT-expand-property.php:$multistatus = new XMLElement( "multistatus", expand_properties( $request->path, $property_tree, $reply), $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-expand-property.php:$request->XMLResponse( 207, $multistatus );
+Privileges:caldav-REPORT-freebusy.php: * Handle the FREE-BUSY-QUERY variant of REPORT
+Privileges:caldav-REPORT-freebusy.php:include_once("freebusy-functions.php");
+Privileges:caldav-REPORT-freebusy.php:$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
+Privileges:caldav-REPORT-freebusy.php:$fbq_start = $fbq_content[0]->GetAttribute('start');
+Privileges:caldav-REPORT-freebusy.php:$fbq_end   = $fbq_content[0]->GetAttribute('end');
+Privileges:caldav-REPORT-freebusy.php:  $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+Privileges:caldav-REPORT-freebusy.php:$freebusy = get_freebusy( '^'.$request->path.$request->DepthRegexTail(), $range_start, $range_end );
+Privileges:caldav-REPORT-freebusy.php:$result->VCalendar();
+Privileges:caldav-REPORT-freebusy.php:$result->AddComponent($freebusy);
+Privileges:caldav-REPORT-freebusy.php:$request->DoResponse( 200, $result->Render(), 'text/calendar' );
+Privileges:caldav-REPORT-multiget.php:* Handle the calendar-multiget REPORT request.
+Privileges:caldav-REPORT-multiget.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+Privileges:caldav-REPORT-multiget.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+Privileges:caldav-REPORT-multiget.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+Privileges:caldav-REPORT-multiget.php:$proptype = $qry_content[0]->GetTag();
+Privileges:caldav-REPORT-multiget.php:    $qry_props = $xmltree->GetPath('/*/'.$proptype.'/*');
+Privileges:caldav-REPORT-multiget.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-multiget.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+Privileges:caldav-REPORT-multiget.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+Privileges:caldav-REPORT-multiget.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+Privileges:caldav-REPORT-multiget.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+Privileges:caldav-REPORT-multiget.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+Privileges:caldav-REPORT-multiget.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+Privileges:caldav-REPORT-multiget.php:$collection = new DAVResource($request->path);
+Privileges:caldav-REPORT-multiget.php:$bound_from = $collection->bound_from();
+Privileges:caldav-REPORT-multiget.php:$mg_hrefs = $xmltree->GetPath('/*/DAV::href');
+Privileges:caldav-REPORT-multiget.php:   * anything up to the matching request->path (which will include any http...) and then
+Privileges:caldav-REPORT-multiget.php:  $href = $bound_from . preg_replace( "{^.*\E$request->path\Q}", '', rawurldecode($v->GetContent()) );
+Privileges:caldav-REPORT-multiget.php:$where = " WHERE caldav_data.collection_id = " . $collection->resource_id();
+Privileges:caldav-REPORT-multiget.php:  if ( $collection->Privileges() != privilege_to_bits('DAV::all') ) {
+Privileges:caldav-REPORT-multiget.php:  if ( isset($c->hide_TODO) && $c->hide_TODO && ! $collection->Privileges() == privilege_to_bits('all') ) {
+Privileges:caldav-REPORT-multiget.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $where .= " ORDER BY caldav_data.dav_id";
+Privileges:caldav-REPORT-multiget.php:if ( $qry->Exec('REPORT',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-REPORT-multiget.php:  while( $dav_object = $qry->Fetch() ) {
+Privileges:caldav-REPORT-multiget.php:    if ( $bound_from != $collection->dav_name() ) {
+Privileges:caldav-REPORT-multiget.php:      $dav_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $dav_object->dav_name);
+Privileges:caldav-REPORT-multiget.php:      $vResource = new vComponent($dav_object->caldav_data);
+Privileges:caldav-REPORT-multiget.php:      $dav_object->caldav_data = $expanded->Render();
+Privileges:caldav-REPORT-multiget.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-multiget.php:$request->XMLResponse( 207, $multistatus );
+Privileges:caldav-REPORT.php:* CalDAV Server - handle REPORT method
+Privileges:caldav-REPORT.php:require_once('RRule-v2.php');
+Privileges:caldav-REPORT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
+Privileges:caldav-REPORT.php:    fwrite($fh,$request->raw_post);
+Privileges:caldav-REPORT.php:if ( !isset($request->xml_tags) ) {
+Privileges:caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body contains no XML data!") );
+Privileges:caldav-REPORT.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+Privileges:caldav-REPORT.php:$target = new DAVResource($request->path);
+Privileges:caldav-REPORT.php:if ( $xmltree->GetTag() != 'DAV::principal-property-search'
+Privileges:caldav-REPORT.php:                && $xmltree->GetTag() != 'DAV::principal-property-search-set' ) {
+Privileges:caldav-REPORT.php:  //$target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
+Privileges:caldav-REPORT.php:$reportnum = -1;
+Privileges:caldav-REPORT.php:if ( $xmltree->GetTag() == 'urn:ietf:params:xml:ns:caldav:free-busy-query' ) {
+Privileges:caldav-REPORT.php:  include("caldav-REPORT-freebusy.php");
+Privileges:caldav-REPORT.php:switch( $xmltree->GetTag() ) {
+Privileges:caldav-REPORT.php:  case 'DAV::principal-property-search':
+Privileges:caldav-REPORT.php:    include("caldav-REPORT-principal.php");
+Privileges:caldav-REPORT.php:  case 'DAV::principal-search-property-set':
+Privileges:caldav-REPORT.php:    include("caldav-REPORT-pps-set.php");
+Privileges:caldav-REPORT.php:  case 'DAV::sync-collection':
+Privileges:caldav-REPORT.php:    include("caldav-REPORT-sync-collection.php");
+Privileges:caldav-REPORT.php:  case 'DAV::expand-property':
+Privileges:caldav-REPORT.php:    include("caldav-REPORT-expand-property.php");
+Privileges:caldav-REPORT.php:  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+Privileges:caldav-REPORT.php:  $caldav_data = $item->caldav_data;
+Privileges:caldav-REPORT.php:  $displayname = $item->summary;
+Privileges:caldav-REPORT.php:  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+Privileges:caldav-REPORT.php:    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+Privileges:caldav-REPORT.php:      if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+Privileges:caldav-REPORT.php:        $resources = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-REPORT.php:        $confidential->SetType($first->GetType());
+Privileges:caldav-REPORT.php:        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+Privileges:caldav-REPORT.php:        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+Privileges:caldav-REPORT.php:        $ical->SetComponents(array($confidential),$confidential->GetType());
+Privileges:caldav-REPORT.php:        $caldav_data = $ical->Render();
+Privileges:caldav-REPORT.php:  $url = ConstructURL($item->dav_name);
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, $contentlength );
+Privileges:caldav-REPORT.php:      case 'calendar-data':
+Privileges:caldav-REPORT.php:        $reply->CalDAVElement($prop, $k, $caldav_data );
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, "text/calendar" );
+Privileges:caldav-REPORT.php:      case 'current-user-principal':
+Privileges:caldav-REPORT.php:        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, $displayname );
+Privileges:caldav-REPORT.php:        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+Privileges:caldav-REPORT.php:      case '"current-user-privilege-set"':
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, privileges($request->permissions) );
+Privileges:caldav-REPORT.php:      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+Privileges:caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+Privileges:caldav-REPORT.php:  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+Privileges:caldav-REPORT.php:  $caldav_data = $item->caldav_data;
+Privileges:caldav-REPORT.php:  $displayname = preg_replace( '{^.*/}', '', $item->dav_name );
+Privileges:caldav-REPORT.php:  switch( $item->caldav_type ) {
+Privileges:caldav-REPORT.php:      $displayname = $item->summary;
+Privileges:caldav-REPORT.php:      $displayname = $item->fn;
+Privileges:caldav-REPORT.php:      $contenttype = 'text/x-vcard';
+Privileges:caldav-REPORT.php:  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+Privileges:caldav-REPORT.php:    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+Privileges:caldav-REPORT.php:      if ( $type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+Privileges:caldav-REPORT.php:        $resources = $ical->GetComponents('VTIMEZONE',false);
+Privileges:caldav-REPORT.php:        $confidential->SetType($first->GetType());
+Privileges:caldav-REPORT.php:        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+Privileges:caldav-REPORT.php:        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+Privileges:caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+Privileges:caldav-REPORT.php:        $ical->SetComponents(array($confidential),$confidential->GetType());
+Privileges:caldav-REPORT.php:        $caldav_data = $ical->Render();
+Privileges:caldav-REPORT.php:  $url = ConstructURL($item->dav_name);
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, $contentlength );
+Privileges:caldav-REPORT.php:      case 'calendar-data':
+Privileges:caldav-REPORT.php:        if ( $type == 'calendar' ) $reply->CalDAVElement($prop, $k, $caldav_data );
+Privileges:caldav-REPORT.php:      case 'address-data':
+Privileges:caldav-REPORT.php:        if ( $type == 'vcard' ) $reply->CardDAVElement($prop, $k, $caldav_data );
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, $contenttype );
+Privileges:caldav-REPORT.php:      case 'current-user-principal':
+Privileges:caldav-REPORT.php:        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, $displayname );
+Privileges:caldav-REPORT.php:        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+Privileges:caldav-REPORT.php:      case '"current-user-privilege-set"':
+Privileges:caldav-REPORT.php:        $prop->NewElement($k, privileges($request->permissions) );
+Privileges:caldav-REPORT.php:      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+Privileges:caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+Privileges:caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+Privileges:caldav-REPORT.php:if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
+Privileges:caldav-REPORT.php:  $calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
+Privileges:caldav-REPORT.php:  include("caldav-REPORT-calquery.php");
+Privileges:caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
+Privileges:caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
+Privileges:caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+Privileges:caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-multiget" ) {
+Privileges:caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
+Privileges:caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+Privileges:caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-query" ) {
+Privileges:caldav-REPORT.php:  $cardquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:carddav:addressbook-query/*");
+Privileges:caldav-REPORT.php:  include("caldav-REPORT-cardquery.php");
+Privileges:caldav-REPORT.php:  $request->PreconditionFailed( 403, 'DAV::supported-report', sprintf( '"%s" is not a supported report type') );
+Privileges:caldav-REPORT-pps-set.php:* DAViCal CalDAV Server - handle principal-search-property-set report (RFC3744)
+Privileges:caldav-REPORT-pps-set.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-REPORT-pps-set.php:  $xmldoc->NSElement($prop, $property );
+Privileges:caldav-REPORT-pps-set.php:  return new XMLElement( 'principal-search-property', $prop );
+Privileges:caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-home-set',
+Privileges:caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-user-address-set'
+Privileges:caldav-REPORT-pps-set.php:$report = new XMLElement( 'principal-search-property-set', $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-pps-set.php:$request->XMLResponse( 207, $report );
+Privileges:caldav-REPORT-principal.php:$searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search');
+Privileges:caldav-REPORT-principal.php:$CS_search_test = $xmltree->GetAttribute('test');
+Privileges:caldav-REPORT-principal.php:  $qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*');  // There may be many
+Privileges:caldav-REPORT-principal.php:  $match     = $search->GetPath('/DAV::property-search/DAV::match');   // There may only be one
+Privileges:caldav-REPORT-principal.php:  $match = $match[0]->GetContent();
+Privileges:caldav-REPORT-principal.php:    switch( $v1->GetTag() ) {
+Privileges:caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+Privileges:caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+Privileges:caldav-REPORT-principal.php:        * @todo We should handle a lot more properties here.  principal-URL seems a likely one to be used.
+Privileges:caldav-REPORT-principal.php:        dbg_error_log("principal", "Unhandled tag '%s' to match '%s'\n", $v1->GetTag(), $match );
+Privileges:caldav-REPORT-principal.php:$get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*');
+Privileges:caldav-REPORT-principal.php:  $properties[] = $v1->GetTag();
+Privileges:caldav-REPORT-principal.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:caldav-REPORT-principal.php:  while( $row = $qry->Fetch() ) {
+Privileges:caldav-REPORT-principal.php:    $responses[] = $principal->RenderAsXML( $properties, $reply );
+Privileges:caldav-REPORT-principal.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-principal.php:$request->XMLResponse( 207, $multistatus );
+Privileges:caldav-REPORT-sync-collection.php:* CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
+Privileges:caldav-REPORT-sync-collection.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+Privileges:caldav-REPORT-sync-collection.php:$sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
+Privileges:caldav-REPORT-sync-collection.php:$sync_token = $sync_tokens[0]->GetContent();
+Privileges:caldav-REPORT-sync-collection.php:dbg_error_log( 'sync', " sync-token: %s", $sync_token );
+Privileges:caldav-REPORT-sync-collection.php:$props = $xmltree->GetElements('DAV::prop');
+Privileges:caldav-REPORT-sync-collection.php:$props = $v->GetContent();
+Privileges:caldav-REPORT-sync-collection.php:  $proplist[] = $v->GetTag();
+Privileges:caldav-REPORT-sync-collection.php:$collection = new DAVResource( $request->path );
+Privileges:caldav-REPORT-sync-collection.php:$params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
+Privileges:caldav-REPORT-sync-collection.php:if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
+Privileges:caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+Privileges:caldav-REPORT-sync-collection.php:$row = $qry->Fetch();
+Privileges:caldav-REPORT-sync-collection.php:if ( !isset($row->new_sync_token) ) {
+Privileges:caldav-REPORT-sync-collection.php:  if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
+Privileges:caldav-REPORT-sync-collection.php:    $request->DoResponse( 500, translate("Database error") );
+Privileges:caldav-REPORT-sync-collection.php:  $row = $qry->Fetch();
+Privileges:caldav-REPORT-sync-collection.php:$new_token = $row->new_sync_token;
+Privileges:caldav-REPORT-sync-collection.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
+Privileges:caldav-REPORT-sync-collection.php:  while( $object = $qry->Fetch() ) {
+Privileges:caldav-REPORT-sync-collection.php:    if ( $object->dav_name == $last_dav_name ) {
+Privileges:caldav-REPORT-sync-collection.php:      if ( $object->sync_status == 404 ) {
+Privileges:caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+Privileges:caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+Privileges:caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+Privileges:caldav-REPORT-sync-collection.php:      else if ( $object->sync_status == 201 && $first_status == 404 ) {
+Privileges:caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+Privileges:caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+Privileges:caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+Privileges:caldav-REPORT-sync-collection.php:        new XMLElement( 'href', ConstructURL($object->dav_name) ),
+Privileges:caldav-REPORT-sync-collection.php:        new XMLElement( 'status', display_status($object->sync_status) )
+Privileges:caldav-REPORT-sync-collection.php:      if ( $object->sync_status != 404 ) {
+Privileges:caldav-REPORT-sync-collection.php:        $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) );
+Privileges:caldav-REPORT-sync-collection.php:      if ( isset($c->use_old_sync_response_tag) && $c->use_old_sync_response_tag ) $response_tag = 'sync-response';
+Privileges:caldav-REPORT-sync-collection.php:      $first_status = $object->sync_status;
+Privileges:caldav-REPORT-sync-collection.php:      $last_dav_name  = $object->dav_name;
+Privileges:caldav-REPORT-sync-collection.php:  $responses[] = new XMLElement( 'sync-token', $new_token );
+Privileges:caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+Privileges:caldav-REPORT-sync-collection.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+Privileges:caldav-REPORT-sync-collection.php:$request->XMLResponse( 207, $multistatus );
+Privileges:CalDAVRequest.php:*  - Ascertaining the paths
+Privileges:CalDAVRequest.php:*  - Ascertaining the current user's permission to those paths.
+Privileges:CalDAVRequest.php:*  - Utility functions which we can use to decide whether this
+Privileges:CalDAVRequest.php:  * The 'current_user_principal_xml' the DAV:current-user-principal answer. An
+Privileges:CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+Privileges:CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+Privileges:CalDAVRequest.php:    $this->supported_privileges = array(
+Privileges:CalDAVRequest.php:          'write-content' => translate('Write content'),
+Privileges:CalDAVRequest.php:          'write-properties' => translate('Write properties')
+Privileges:CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:read-free-busy' => translate('Read the free/busy information for a calendar collection'),
+Privileges:CalDAVRequest.php:        'read-acl' => translate('Read ACLs for a resource or collection'),
+Privileges:CalDAVRequest.php:        'read-current-user-privilege-set' => translate('Read the details of the current user\'s access control to this resource.'),
+Privileges:CalDAVRequest.php:        'write-acl' => translate('Write ACLs for a resource or collection'),
+Privileges:CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-deliver' => array(
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite'=> translate('Deliver scheduling invitations from an organiser to this scheduling inbox'),
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply' => translate('Deliver scheduling replies from an attendee to this scheduling inbox'),
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy' => translate('Allow free/busy enquiries targeted at the owner of this scheduling inbox')
+Privileges:CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-send' => array(
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-invite' => translate('Send scheduling invitations as an organiser from the owner of this scheduling outbox.'),
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-reply' => translate('Send scheduling replies as an attendee from the owner of this scheduling outbox.'),
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy' => translate('Send free/busy enquiries')
+Privileges:CalDAVRequest.php:    $this->options = $options;
+Privileges:CalDAVRequest.php:    if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false;
+Privileges:CalDAVRequest.php:    $this->principal = (object) array( 'username' => $session->username, 'user_no' => $session->user_no );
+Privileges:CalDAVRequest.php:    $this->raw_post = file_get_contents ( 'php://input');
+Privileges:CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['request']) && $c->dbg['request']) ) {
+Privileges:CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s: %s", $k, $v );
+Privileges:CalDAVRequest.php:      $lines = preg_split( '#[\r\n]+#', $this->raw_post);
+Privileges:CalDAVRequest.php:        dbg_error_log( "LOG request", "-->%s", $v );
+Privileges:CalDAVRequest.php:    $this->method = $_SERVER['REQUEST_METHOD'];
+Privileges:CalDAVRequest.php:      $this->content_type = (isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null);
+Privileges:CalDAVRequest.php:      if ( preg_match( '{^(\S+/\S+)\s*(;.*)?$}', $this->content_type, $matches ) ) {
+Privileges:CalDAVRequest.php:        $this->content_type = $matches[1];
+Privileges:CalDAVRequest.php:      if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' ) {
+Privileges:CalDAVRequest.php:        if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
+Privileges:CalDAVRequest.php:          dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
+Privileges:CalDAVRequest.php:                                                 $request->method, $this->content_type );
+Privileges:CalDAVRequest.php:          $this->content_type = 'text/xml';
+Privileges:CalDAVRequest.php:    $this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
+Privileges:CalDAVRequest.php:      $this->depth = $_SERVER['HTTP_DEPTH'];
+Privileges:CalDAVRequest.php:      * is not present then a reasonable request-type-dependent default should be
+Privileges:CalDAVRequest.php:      switch( $this->method ) {
+Privileges:CalDAVRequest.php:          $this->depth = 'infinity';
+Privileges:CalDAVRequest.php:          $this->depth = 0;
+Privileges:CalDAVRequest.php:    if ( $this->depth == 'infinity' ) $this->depth = DEPTH_INFINITY;
+Privileges:CalDAVRequest.php:    $this->depth = intval($this->depth);
+Privileges:CalDAVRequest.php:    if ( isset($_SERVER['HTTP_DESTINATION']) ) $this->destination = $_SERVER['HTTP_DESTINATION'];
+Privileges:CalDAVRequest.php:    $this->overwrite = ( isset($_SERVER['HTTP_OVERWRITE']) && ($_SERVER['HTTP_OVERWRITE'] == 'F') ? false : true ); // RFC4918, 9.8.4 says default True.
+Privileges:CalDAVRequest.php:    * LOCK things use an "If" header to hold the lock in some cases, and "Lock-token" in others
+Privileges:CalDAVRequest.php:    if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
+Privileges:CalDAVRequest.php:      $this->lock_token = $matches[1];
+Privileges:CalDAVRequest.php:      $this->ticket = new DAVTicket($_GET['ticket']);
+Privileges:CalDAVRequest.php:      $this->ticket = new DAVTicket($_SERVER['HTTP_TICKET']);
+Privileges:CalDAVRequest.php:          $this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
+Privileges:CalDAVRequest.php:        elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
+Privileges:CalDAVRequest.php:          $this->timeout = min( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
+Privileges:CalDAVRequest.php:      if ( ! isset($this->timeout) || $this->timeout == 0 ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
+Privileges:CalDAVRequest.php:    $this->path = (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : "/");
+Privileges:CalDAVRequest.php:    $this->path = rawurldecode($this->path);
+Privileges:CalDAVRequest.php:    if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) {
+Privileges:CalDAVRequest.php:      $this->path = $matches[1]. '/';
+Privileges:CalDAVRequest.php:    // dbg_error_log( "caldav", "Sanitising path '%s'", $this->path );
+Privileges:CalDAVRequest.php:    if ( preg_match( $bad_chars_regex, $this->path ) ) {
+Privileges:CalDAVRequest.php:      $this->DoResponse( 400, translate("The calendar path contains illegal characters.") );
+Privileges:CalDAVRequest.php:    if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path);
+Privileges:CalDAVRequest.php:    $this->user_no = $session->user_no;
+Privileges:CalDAVRequest.php:    $this->username = $session->username;
+Privileges:CalDAVRequest.php:    if ( $session->user_no > 0 ) {
+Privileges:CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('href', ConstructURL('/'.$session->username.'/') );
+Privileges:CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('unauthenticated' );
+Privileges:CalDAVRequest.php:    * we SHOULD return a Content-location header with the correction...
+Privileges:CalDAVRequest.php:    *  - The exact request.
+Privileges:CalDAVRequest.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+Privileges:CalDAVRequest.php:    *  - The request URL truncated to the last '/'
+Privileges:CalDAVRequest.php:    $params = array( ':exact_name' => $this->path );
+Privileges:CalDAVRequest.php:    if ( !preg_match( '#/$#', $this->path ) ) {
+Privileges:CalDAVRequest.php:      $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $this->path);
+Privileges:CalDAVRequest.php:      $params[':trailing_slash_name'] = $this->path."/";
+Privileges:CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+Privileges:CalDAVRequest.php:      if ( $row->dav_name == $this->path."/" ) {
+Privileges:CalDAVRequest.php:        $this->path = $row->dav_name;
+Privileges:CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+Privileges:CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+Privileges:CalDAVRequest.php:      $this->collection_id = $row->collection_id;
+Privileges:CalDAVRequest.php:      $this->collection_path = $row->dav_name;
+Privileges:CalDAVRequest.php:      $this->collection_type = ($row->is_calendar == 't' ? 'calendar' : 'collection');
+Privileges:CalDAVRequest.php:      $this->collection = $row;
+Privileges:CalDAVRequest.php:      if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
+Privileges:CalDAVRequest.php:        $this->collection_type = 'schedule-'. $matches[3]. 'box';
+Privileges:CalDAVRequest.php:      $this->collection->type = $this->collection_type;
+Privileges:CalDAVRequest.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->path, $matches ) ) {
+Privileges:CalDAVRequest.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+Privileges:CalDAVRequest.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+Privileges:CalDAVRequest.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+Privileges:CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+Privileges:CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+Privileges:CalDAVRequest.php:        $this->collection_id = $row->collection_id;
+Privileges:CalDAVRequest.php:        $this->collection_path = $matches[1];
+Privileges:CalDAVRequest.php:        $this->collection = $row;
+Privileges:CalDAVRequest.php:        $this->collection->type = $this->collection_type;
+Privileges:CalDAVRequest.php:    else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) {
+Privileges:CalDAVRequest.php:      $this->collection_type = 'proxy';
+Privileges:CalDAVRequest.php:      $this->_is_proxy_request = true;
+Privileges:CalDAVRequest.php:      $this->proxy_type = $matches[3];
+Privileges:CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+Privileges:CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+Privileges:CalDAVRequest.php:        $this->path .= '/';
+Privileges:CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a (proxy) collection - sending Content-Location header." );
+Privileges:CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+Privileges:CalDAVRequest.php:    else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) {
+Privileges:CalDAVRequest.php:      $this->collection_id = -1;
+Privileges:CalDAVRequest.php:      $this->collection_type = 'email';
+Privileges:CalDAVRequest.php:      $this->collection_path = $this->path;
+Privileges:CalDAVRequest.php:      $this->_is_principal = true;
+Privileges:CalDAVRequest.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+Privileges:CalDAVRequest.php:      $this->collection_id = -1;
+Privileges:CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+Privileges:CalDAVRequest.php:      $this->collection_type = 'principal';
+Privileges:CalDAVRequest.php:      $this->_is_principal = true;
+Privileges:CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+Privileges:CalDAVRequest.php:        $this->path .= '/';
+Privileges:CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+Privileges:CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+Privileges:CalDAVRequest.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+Privileges:CalDAVRequest.php:        $this->depth = 0;
+Privileges:CalDAVRequest.php:    else if ( $this->path == '/' ) {
+Privileges:CalDAVRequest.php:      $this->collection_id = -1;
+Privileges:CalDAVRequest.php:      $this->collection_path = '/';
+Privileges:CalDAVRequest.php:      $this->collection_type = 'root';
+Privileges:CalDAVRequest.php:    if ( $this->collection_path == $this->path ) $this->_is_collection = true;
+Privileges:CalDAVRequest.php:    dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
+Privileges:CalDAVRequest.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
+Privileges:CalDAVRequest.php:    if ( isset($this->principal->user_no) ) $this->user_no  = $this->principal->user_no;
+Privileges:CalDAVRequest.php:    if ( isset($this->principal->username)) $this->username = $this->principal->username;
+Privileges:CalDAVRequest.php:    if ( isset($this->principal->by_email) && $this->principal->by_email) $this->by_email = true;
+Privileges:CalDAVRequest.php:    if ( isset($this->principal->principal_id)) $this->principal_id = $this->principal->principal_id;
+Privileges:CalDAVRequest.php:    if ( $this->collection_type == 'principal' || $this->collection_type == 'email' || $this->collection_type == 'proxy' ) {
+Privileges:CalDAVRequest.php:      $this->collection = $this->principal->AsCollection();
+Privileges:CalDAVRequest.php:      if( $this->collection_type == 'proxy' ) {
+Privileges:CalDAVRequest.php:        $this->collection = $this->principal->AsCollection();
+Privileges:CalDAVRequest.php:        $this->collection->is_proxy = 't';
+Privileges:CalDAVRequest.php:        $this->collection->type = 'proxy';
+Privileges:CalDAVRequest.php:        $this->collection->proxy_type = $this->proxy_type;
+Privileges:CalDAVRequest.php:        $this->collection->dav_displayname = sprintf('Proxy %s for %s', $this->proxy_type, $this->principal->username() );
+Privileges:CalDAVRequest.php:    elseif( $this->collection_type == 'root' ) {
+Privileges:CalDAVRequest.php:      $this->collection = (object) array(
+Privileges:CalDAVRequest.php:                            'dav_etag' => md5($c->system_name),
+Privileges:CalDAVRequest.php:                            'dav_displayname' => $c->system_name,
+Privileges:CalDAVRequest.php:    $this->setPermissions();
+Privileges:CalDAVRequest.php:    $this->supported_methods = array(
+Privileges:CalDAVRequest.php:    if ( $this->IsCollection() ) {
+Privileges:CalDAVRequest.php:      switch ( $this->collection_type ) {
+Privileges:CalDAVRequest.php:          $this->supported_methods = array(
+Privileges:CalDAVRequest.php:        case 'schedule-inbox':
+Privileges:CalDAVRequest.php:        case 'schedule-outbox':
+Privileges:CalDAVRequest.php:          $this->supported_methods = array_merge(
+Privileges:CalDAVRequest.php:            $this->supported_methods,
+Privileges:CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['MKCOL'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['MKCALENDAR'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['PROPPATCH'] = '';
+Privileges:CalDAVRequest.php:          $this->supported_methods['BIND'] = '';
+Privileges:CalDAVRequest.php:      $this->supported_methods = array_merge(
+Privileges:CalDAVRequest.php:        $this->supported_methods,
+Privileges:CalDAVRequest.php:    /**** comentado por motivos de incmpatibilidade do sync-collection
+Privileges:CalDAVRequest.php:    $this->supported_reports = array(
+Privileges:CalDAVRequest.php:      'DAV::principal-property-search' => '',
+Privileges:CalDAVRequest.php:      'DAV::expand-property' => '',
+Privileges:CalDAVRequest.php:      'DAV::sync-collection' => ''
+Privileges:CalDAVRequest.php:    $this->supported_reports = array(
+Privileges:CalDAVRequest.php:      'DAV::principal-property-search' => '',
+Privileges:CalDAVRequest.php:      'DAV::expand-property' => ''
+Privileges:CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_calendar ) {
+Privileges:CalDAVRequest.php:      $this->supported_reports = array_merge(
+Privileges:CalDAVRequest.php:        $this->supported_reports,
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+Privileges:CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_addressbook ) {
+Privileges:CalDAVRequest.php:      $this->supported_reports = array_merge(
+Privileges:CalDAVRequest.php:        $this->supported_reports,
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+Privileges:CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+Privileges:CalDAVRequest.php:    if ( isset($this->content_type) && preg_match( '#(application|text)/xml#', $this->content_type ) ) {
+Privileges:CalDAVRequest.php:      $xml_parser = xml_parser_create_ns('UTF-8');
+Privileges:CalDAVRequest.php:      $this->xml_tags = array();
+Privileges:CalDAVRequest.php:      $rc = xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
+Privileges:CalDAVRequest.php:        $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml'), array( 'xmlns' => 'DAV:') ) );
+Privileges:CalDAVRequest.php:      if ( count($this->xml_tags) ) {
+Privileges:CalDAVRequest.php:        $this->xml_tags = null;
+Privileges:CalDAVRequest.php:        dbg_error_log( "ERROR", "Incoming request sent content-type XML with no XML request body." );
+Privileges:CalDAVRequest.php:    * Look out for If-None-Match or If-Match headers
+Privileges:CalDAVRequest.php:      $this->etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
+Privileges:CalDAVRequest.php:      if ( $this->etag_none_match == '' ) unset($this->etag_none_match);
+Privileges:CalDAVRequest.php:      $this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
+Privileges:CalDAVRequest.php:      if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
+Privileges:CalDAVRequest.php:    $this->user_no = $session->user_no;
+Privileges:CalDAVRequest.php:    $this->username = $session->username;
+Privileges:CalDAVRequest.php:    $this->principal_id = $session->principal_id;
+Privileges:CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+Privileges:CalDAVRequest.php:    $path_split = explode('/', $this->path );
+Privileges:CalDAVRequest.php:    $this->username = $path_split[1];
+Privileges:CalDAVRequest.php:    if ( $this->username == 'principals' ) $this->username = $path_split[3];
+Privileges:CalDAVRequest.php:    if ( isset($this->options['allow_by_email']) && preg_match( '#/(\S+@\S+[.]\S+)/?$#', $this->path, $matches) ) {
+Privileges:CalDAVRequest.php:      $this->by_email = $matches[1];
+Privileges:CalDAVRequest.php:                          array(':email' => $this->by_email ) );
+Privileges:CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $user = $qry->Fetch() ) {
+Privileges:CalDAVRequest.php:        $this->user_no = $user->user_no;
+Privileges:CalDAVRequest.php:        $this->username = $user->username;
+Privileges:CalDAVRequest.php:        $this->principal_id = $user->principal_id;
+Privileges:CalDAVRequest.php:    elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)) {
+Privileges:CalDAVRequest.php:      $this->principal = $user;
+Privileges:CalDAVRequest.php:      $this->user_no = $user->user_no;
+Privileges:CalDAVRequest.php:      $this->principal_id = $user->principal_id;
+Privileges:CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+Privileges:CalDAVRequest.php:      $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+Privileges:CalDAVRequest.php:    else if ( $session->AllowedTo("Admin") || $session->user_no == $this->user_no ) {
+Privileges:CalDAVRequest.php:      $this->privileges = privilege_to_bits('all');
+Privileges:CalDAVRequest.php:      dbg_error_log( "caldav", "Full permissions for %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator") );
+Privileges:CalDAVRequest.php:      $this->privileges = 0;
+Privileges:CalDAVRequest.php:      if ( $this->IsPublic() ) {
+Privileges:CalDAVRequest.php:        $this->privileges = privilege_to_bits(array('read','read-free-busy'));
+Privileges:CalDAVRequest.php:      else if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
+Privileges:CalDAVRequest.php:        $this->privileges = privilege_to_bits('read-free-busy');
+Privileges:CalDAVRequest.php:      $params = array( ':session_principal_id' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:CalDAVRequest.php:      if ( isset($this->by_email) && $this->by_email ) {
+Privileges:CalDAVRequest.php:        $params[':request_principal_id'] = $this->principal_id;
+Privileges:CalDAVRequest.php:        $params[':request_path'] = $this->path;
+Privileges:CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $permission_result = $qry->Fetch() )
+Privileges:CalDAVRequest.php:        $this->privileges |= bindec($permission_result->perm);
+Privileges:CalDAVRequest.php:      dbg_error_log( 'caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges) );
+Privileges:CalDAVRequest.php:      if ( isset($this->ticket) && $this->ticket->MatchesPath($this->path) ) {
+Privileges:CalDAVRequest.php:        $this->privileges |= $this->ticket->privileges();
+Privileges:CalDAVRequest.php:        dbg_error_log( 'caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges) );
+Privileges:CalDAVRequest.php:    $this->permissions = array();
+Privileges:CalDAVRequest.php:    $privs = bits_to_privilege($this->privileges);
+Privileges:CalDAVRequest.php:      $this->permissions[$v] = $type;
+Privileges:CalDAVRequest.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+Privileges:CalDAVRequest.php:    if ( !isset($this->_locks_found) ) {
+Privileges:CalDAVRequest.php:      $this->_locks_found = array();
+Privileges:CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+Privileges:CalDAVRequest.php:      $qry = new AwlQuery($sql, array( ':dav_name' => $this->path, ':pattern_end_match' => ($this->IsInfiniteDepth() ? '' : '$') ) );
+Privileges:CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+Privileges:CalDAVRequest.php:        while( $lock_row = $qry->Fetch() ) {
+Privileges:CalDAVRequest.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+Privileges:CalDAVRequest.php:        $this->DoResponse(500,translate("Database Error"));
+Privileges:CalDAVRequest.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+Privileges:CalDAVRequest.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
+Privileges:CalDAVRequest.php:    if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
+Privileges:CalDAVRequest.php:    if ( isset($this->path) ) return $this->path;
+Privileges:CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return 'infinity';
+Privileges:CalDAVRequest.php:    return $this->depth;
+Privileges:CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return '';
+Privileges:CalDAVRequest.php:    if ( $this->depth == 0 ) return '$';
+Privileges:CalDAVRequest.php:    if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
+Privileges:CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+Privileges:CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+Privileges:CalDAVRequest.php:      $lock_row = $qry->Fetch();
+Privileges:CalDAVRequest.php:      $this->_locks_found = array( $lock_token => $lock_row );
+Privileges:CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+Privileges:CalDAVRequest.php:      $this->DoResponse( 500, translate("Database Error") );
+Privileges:CalDAVRequest.php:    if ( isset($this->lock_token) && $this->lock_token == $lock_token ) {
+Privileges:CalDAVRequest.php:    if ( isset($this->if_clause) ) {
+Privileges:CalDAVRequest.php:      dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $this->if_clause );
+Privileges:CalDAVRequest.php:      $tokens = preg_split( '/[<>]/', $this->if_clause );
+Privileges:CalDAVRequest.php:      @dbg_error_log( "caldav", "Invalid lock token '%s' - not in Lock-token (%s) or If headers (%s) ", $lock_token, $this->lock_token, $this->if_clause );
+Privileges:CalDAVRequest.php:    if ( !isset($this->_locks_found) && false === $this->IsLocked() ) return false;
+Privileges:CalDAVRequest.php:    if ( isset($this->_locks_found[$lock_token]) ) return $this->_locks_found[$lock_token];
+Privileges:CalDAVRequest.php:    if ( $existing_lock = $this->IsLocked() ) { // NOTE Assignment in if() is expected here.
+Privileges:CalDAVRequest.php:      dbg_error_log( "caldav", "There is a lock on '%s'", $this->path);
+Privileges:CalDAVRequest.php:      if ( ! $this->ValidateLockToken($existing_lock) ) {
+Privileges:CalDAVRequest.php:        $lock_row = $this->GetLockRow($existing_lock);
+Privileges:CalDAVRequest.php:        * Already locked - deny it
+Privileges:CalDAVRequest.php:            new XMLElement( 'href',   $lock_row->dav_name ),
+Privileges:CalDAVRequest.php:        if ( $lock_row->dav_name != $this->path ) {
+Privileges:CalDAVRequest.php:              new XMLElement( 'href',   $this->path ),
+Privileges:CalDAVRequest.php:        $xmldoc = $response->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+Privileges:CalDAVRequest.php:        $this->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:CalDAVRequest.php:  * Coerces the Content-type of the request into something valid/appropriate
+Privileges:CalDAVRequest.php:    if ( isset($this->content_type) ) {
+Privileges:CalDAVRequest.php:      $type = explode( '/', $this->content_type, 2);
+Privileges:CalDAVRequest.php:    /** Null (or peculiar) content-type supplied so we have to try and work it out... */
+Privileges:CalDAVRequest.php:    $first_word = trim(substr( $this->raw_post, 0, 30));
+Privileges:CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/xml"',
+Privileges:CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+Privileges:CalDAVRequest.php:        $this->content_type = 'text/xml';
+Privileges:CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/calendar"',
+Privileges:CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+Privileges:CalDAVRequest.php:        $this->content_type = 'text/calendar';
+Privileges:CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/vcard"',
+Privileges:CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+Privileges:CalDAVRequest.php:        $this->content_type = 'text/vcard';
+Privileges:CalDAVRequest.php:        dbg_error_log( 'LOG NOTICE', 'Unusual content-type of "%s" and first word of content is "%s"',
+Privileges:CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)'), $first_word );
+Privileges:CalDAVRequest.php:    if ( !isset($this->_is_collection) ) {
+Privileges:CalDAVRequest.php:      $this->_is_collection = preg_match( '#/$#', $this->path );
+Privileges:CalDAVRequest.php:    return $this->_is_collection;
+Privileges:CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+Privileges:CalDAVRequest.php:    return $this->collection->is_calendar == 't';
+Privileges:CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+Privileges:CalDAVRequest.php:    return $this->collection->is_addressbook == 't';
+Privileges:CalDAVRequest.php:    if ( !isset($this->_is_principal) ) {
+Privileges:CalDAVRequest.php:      $this->_is_principal = preg_match( '#^/[^/]+/$#', $this->path );
+Privileges:CalDAVRequest.php:    return $this->_is_principal;
+Privileges:CalDAVRequest.php:    if ( !isset($this->_is_proxy_request) ) {
+Privileges:CalDAVRequest.php:      $this->_is_proxy_request = preg_match( '#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->path );
+Privileges:CalDAVRequest.php:    return $this->_is_proxy_request;
+Privileges:CalDAVRequest.php:    return ($this->depth == DEPTH_INFINITY);
+Privileges:CalDAVRequest.php:    return $this->collection_id;
+Privileges:CalDAVRequest.php:    if ( $privs === null ) $privs = $this->supported_privileges;
+Privileges:CalDAVRequest.php:      $reply->NSElement($privilege,$k);
+Privileges:CalDAVRequest.php:        dbg_error_log( 'caldav', '"%s" is a container of sub-privileges.', $k );
+Privileges:CalDAVRequest.php:        $privset = array_merge($privset, $this->BuildSupportedPrivileges($reply,$v));
+Privileges:CalDAVRequest.php:      $privileges[] = new XMLElement('supported-privilege',$privset);
+Privileges:CalDAVRequest.php:  * +------------+------------------------------------------------------+
+Privileges:CalDAVRequest.php:  * +------------+------------------------------------------------------+
+Privileges:CalDAVRequest.php:  * | REPORT     | DAV:read or CALDAV:read-free-busy (on all referenced |
+Privileges:CalDAVRequest.php:  * +------------+------------------------------------------------------+
+Privileges:CalDAVRequest.php:    dbg_error_log('caldav', 'Checking whether "%s" is allowed to "%s"', $session->username, $activity);
+Privileges:CalDAVRequest.php:    if ( isset($this->permissions['all']) ) return true;
+Privileges:CalDAVRequest.php:      case "CALDAV:schedule-send-freebusy":
+Privileges:CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+Privileges:CalDAVRequest.php:      case "CALDAV:schedule-send-invite":
+Privileges:CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+Privileges:CalDAVRequest.php:      case "CALDAV:schedule-send-reply":
+Privileges:CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+Privileges:CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+Privileges:CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['unbind']);
+Privileges:CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
+Privileges:CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-content']);
+Privileges:CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['bind']);
+Privileges:CalDAVRequest.php:        if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
+Privileges:CalDAVRequest.php:        if ( $this->is_principal ) return false;
+Privileges:CalDAVRequest.php:        if ( $this->path == '/' ) return false;
+Privileges:CalDAVRequest.php://             decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+Privileges:CalDAVRequest.php:        return (($this->privileges & $test_bits) > 0 );
+Privileges:CalDAVRequest.php:    return $this->privileges;
+Privileges:CalDAVRequest.php://              decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+Privileges:CalDAVRequest.php:    return ($this->privileges & $test_bits) > 0;
+Privileges:CalDAVRequest.php:  * Sometimes it's a perfectly formed request, but we just don't do that :-(
+Privileges:CalDAVRequest.php:        dbg_error_log("ERROR", " %s: Support for $v:$k properties is not implemented yet", $this->method );
+Privileges:CalDAVRequest.php:        $badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
+Privileges:CalDAVRequest.php:      $this->XMLResponse( 422, $error );
+Privileges:CalDAVRequest.php:  * Send a need-privileges error response.  This function will only return
+Privileges:CalDAVRequest.php:      if ( $this->HavePrivilegeTo($privileges) ) return;
+Privileges:CalDAVRequest.php:      $href = $this->path;
+Privileges:CalDAVRequest.php:    $privnodes = array( $reply->href(ConstructURL($href)), new XMLElement( 'privilege' ) );
+Privileges:CalDAVRequest.php:    $reply->NSElement( $privnodes[1], $privileges[0] );
+Privileges:CalDAVRequest.php:    $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+Privileges:CalDAVRequest.php:    $xmldoc = $reply->Render('error',$xml);
+Privileges:CalDAVRequest.php:    $this->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:CalDAVRequest.php:    $xmldoc = sprintf('<?xml version="1.0" encoding="utf-8" ?>
+Privileges:CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:CalDAVRequest.php:    $this->DoResponse( 400, $text );
+Privileges:CalDAVRequest.php:    $xmldoc = $xmltree->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+Privileges:CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+Privileges:CalDAVRequest.php:  * Utility function we call when we have a simple status-based response to
+Privileges:CalDAVRequest.php:  function DoResponse( $status, $message="", $content_type="text/plain; charset=\"utf-8\"" ) {
+Privileges:CalDAVRequest.php:    @header( sprintf("X-DAViCal-Version: DAViCal/%d.%d.%d; DB/%d.%d.%d", $c->code_major, $c->code_minor, $c->code_patch, $c->schema_major, $c->schema_minor, $c->schema_patch) );
+Privileges:CalDAVRequest.php:    @header( "Content-type: ".$content_type );
+Privileges:CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['response']) && $c->dbg['response']) || $status > 399 ) {
+Privileges:CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s", $v );
+Privileges:CalDAVRequest.php:        dbg_error_log( "LOG response", "-->%s", $v );
+Privileges:CalDAVRequest.php:    header( "Content-Length: ".strlen($message) );
+Privileges:CalDAVRequest.php:    if ( isset($c->dbg['caldav']) && $c->dbg['caldav'] ) {
+Privileges:CalDAVRequest.php:      dbg_error_log("caldav", "Status: %d, Message: %s, User: %d, Path: %s", $status, $message, $session->user_no, $this->path);
+Privileges:CalDAVRequest.php:    if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+Privileges:CalDAVRequest.php:      $script_time = microtime(true) - $c->script_start_time;
+Privileges:CalDAVRequest.php:                         $this->method, $status, $script_time, $c->total_query_time, $this->path);
+Privileges:check_UTF8.php: * http://iki.fi/hsivonen/php-utf8/
+Privileges:check_UTF8.php: * Version 1.0, 2003-05-30
+Privileges:check_UTF8.php: * Takes an UTF-8 string and returns an array of ints representing the
+Privileges:check_UTF8.php: * Returns false if the input string isn't a valid UTF-8 octet sequence.
+Privileges:check_UTF8.php:      // When mState is zero we expect either a US-ASCII character or a
+Privileges:check_UTF8.php:      // multi-octet sequence.
+Privileges:check_UTF8.php:        // US-ASCII, pass straight through.
+Privileges:check_UTF8.php:         * (b) outside the Unicode range of 0-0x10FFFF.
+Privileges:check_UTF8.php:        /* Current octet is neither in the US-ASCII range nor a legal first
+Privileges:check_UTF8.php:         * octet of a multi-octet sequence.
+Privileges:check_UTF8.php:      // When mState is non-zero, we expect a continuation of the multi-octet
+Privileges:check_UTF8.php:        $shift = ($mState - 1) * 6;
+Privileges:check_UTF8.php:        if (0 == --$mState) {
+Privileges:check_UTF8.php:          /* End of the multi-octet sequence. mUcs4 now contains the final
+Privileges:check_UTF8.php:          // From Unicode 3.1, non-shortest form is illegal
+Privileges:check_UTF8.php:         * Incomplete multi-octet sequence.
+Privileges:check_UTF8.php: * a UTF-8 string. Astral planes are supported ie. the ints in the
+Privileges:check_UTF8.php:      // nop -- zap the BOM
+Privileges:davical_configuration_missing.php:include("page-header.php");
+Privileges:davical_configuration_missing.php:<p>There is no configuration file present in <b>/etc/davical/$_SERVER[SERVER_NAME]-conf.php</b> so
+Privileges:davical_configuration_missing.php:<p>Well, you're seeing this! At least you have DAViCal <i>installed</i> :-) You also have Apache and PHP working
+Privileges:davical_configuration_missing.php:   or later so keep downloading new versions and trying again.  Or make some guesses.  Or bug Andrew :-)</p>
+Privileges:davical_configuration_missing.php://  \$c->domain_name  = 'davical.example.com';
+Privileges:davical_configuration_missing.php://  \$c->sysabbr     = 'davical';
+Privileges:davical_configuration_missing.php://  \$c->system_name = 'DAViCal CalDAV Server';
+Privileges:davical_configuration_missing.php:  \$c->admin_email  = 'admin@example.com';
+Privileges:davical_configuration_missing.php:  \$c->pg_connect[] = 'dbname=davical user=davical_app';
+Privileges:davical_configuration_missing.php:configuring someone for the admin e-mail is a really good idea.</p>
+Privileges:davical_configuration_missing.php:include("page-footer.php");
+Privileges:DAViCalSession.php:// the records related to the logged-on user...  (the where clause gets added).
+Privileges:DAViCalSession.php:    $this->Session($sid);
+Privileges:DAViCalSession.php:    if ( !isset($u->principal_id) ) {
+Privileges:DAViCalSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+Privileges:DAViCalSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+Privileges:DAViCalSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+Privileges:DAViCalSession.php:        $u = $qry->Fetch();
+Privileges:DAViCalSession.php:    $this->GetRoles();
+Privileges:DAViCalSession.php:    if ( function_exists('awl_set_locale') && isset($this->locale) && $this->locale != '' ) {
+Privileges:DAViCalSession.php:      awl_set_locale($this->locale);
+Privileges:DAViCalSession.php:    $this->roles = array();
+Privileges:DAViCalSession.php:    $sql = 'SELECT role_name FROM roles JOIN role_member ON roles.role_no=role_member.role_no WHERE user_no = '.$this->user_no;
+Privileges:DAViCalSession.php:    if ( $qry->Exec('DAViCalSession') && $qry->rows() > 0 ) {
+Privileges:DAViCalSession.php:      while( $role = $qry->Fetch() ) {
+Privileges:DAViCalSession.php:        $this->roles[$role->role_name] = 1;
+Privileges:DAViCalSession.php:    if ( (isset($c->restrict_admin_domain) && $c->restrict_admin_domain != $current_domain)
+Privileges:DAViCalSession.php:      || (isset($c->restrict_admin_port) && $c->restrict_admin_port != $_SERVER['SERVER_PORT'] ) ) {
+Privileges:DAViCalSession.php:    if ( isset($c->restrict_admin_roles) && $roles == '' ) $roles = $c->restrict_admin_roles;
+Privileges:DAViCalSession.php:    if ( $this->logged_in && $roles == '' ) return;
+Privileges:DAViCalSession.php:    if ( ! $this->logged_in ) {
+Privileges:DAViCalSession.php:      $c->messages[] = i18n('You must log in to use this system.');
+Privileges:DAViCalSession.php:      include_once('page-header.php');
+Privileges:DAViCalSession.php:        if ( $this->login_failed ) {
+Privileges:DAViCalSession.php:          $c->messages[] = i18n('Invalid user name or password.');
+Privileges:DAViCalSession.php:                  .' '.translate($c->system_name).' '
+Privileges:DAViCalSession.php:        echo '<p>'.translate('If you would like to request access, please e-mail').' '.$c->admin_email."</p>\n";
+Privileges:DAViCalSession.php:        echo $this->RenderLoginPanel();
+Privileges:DAViCalSession.php:        if ( $this->AllowedTo($v) ) return;
+Privileges:DAViCalSession.php:      $c->messages[] = i18n('You are not authorised to use this function.');
+Privileges:DAViCalSession.php:      include_once('page-header.php');
+Privileges:DAViCalSession.php:    include('page-footer.php');
+Privileges:DAViCalSession.php:$session->_CheckLogin();
+Privileges:DAVResource.php:  * @var True if this resource is a principal-URL
+Privileges:DAVResource.php:    $this->exists        = null;
+Privileges:DAVResource.php:    $this->bound_from    = null;
+Privileges:DAVResource.php:    $this->dav_name      = null;
+Privileges:DAVResource.php:    $this->unique_tag    = null;
+Privileges:DAVResource.php:    $this->resource      = null;
+Privileges:DAVResource.php:    $this->collection    = null;
+Privileges:DAVResource.php:    $this->principal     = null;
+Privileges:DAVResource.php:    $this->parent        = null;
+Privileges:DAVResource.php:    $this->resourcetypes = null;
+Privileges:DAVResource.php:    $this->contenttype   = null;
+Privileges:DAVResource.php:    $this->privileges    = null;
+Privileges:DAVResource.php:    $this->dead_properties   = null;
+Privileges:DAVResource.php:    $this->supported_methods = null;
+Privileges:DAVResource.php:    $this->supported_reports = null;
+Privileges:DAVResource.php:    $this->_is_collection    = false;
+Privileges:DAVResource.php:    $this->_is_principal     = false;
+Privileges:DAVResource.php:    $this->_is_calendar      = false;
+Privileges:DAVResource.php:    $this->_is_binding       = false;
+Privileges:DAVResource.php:    $this->_is_addressbook   = false;
+Privileges:DAVResource.php:    $this->_is_proxy_request = false;
+Privileges:DAVResource.php:      $this->FromRow($parameters);
+Privileges:DAVResource.php:        $this->FromPath($parameters['path']);
+Privileges:DAVResource.php:      $this->FromPath($parameters);
+Privileges:DAVResource.php:    $this->exists = true;
+Privileges:DAVResource.php:    $this->dav_name = $row->dav_name;
+Privileges:DAVResource.php:    $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+Privileges:DAVResource.php:    $this->_is_collection = preg_match( '{/$}', $this->dav_name );
+Privileges:DAVResource.php:    if ( $this->_is_collection ) {
+Privileges:DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+Privileges:DAVResource.php:      $this->collection = (object) array();
+Privileges:DAVResource.php:      $this->resource_id = $row->collection_id;
+Privileges:DAVResource.php:      $this->_is_principal = preg_match( '{^/[^/]+/$}', $this->dav_name );
+Privileges:DAVResource.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
+Privileges:DAVResource.php:        $this->collection->dav_name = $matches[1].'/';
+Privileges:DAVResource.php:        $this->collection->type = 'principal_link';
+Privileges:DAVResource.php:        $this->_is_principal = true;
+Privileges:DAVResource.php:      $this->resource = (object) array();
+Privileges:DAVResource.php:      if ( isset($row->dav_id) ) $this->resource_id = $row->dav_id;
+Privileges:DAVResource.php:    dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->dav_name, ($this->_is_collection?'':' not') );
+Privileges:DAVResource.php:      if ( $this->_is_collection )
+Privileges:DAVResource.php:        $this->collection->{$k} = $v;
+Privileges:DAVResource.php:        $this->resource->{$k} = $v;
+Privileges:DAVResource.php:          $this->{$k} = $v;
+Privileges:DAVResource.php:          $this->unique_tag = '"'.$v.'"';
+Privileges:DAVResource.php:    if ( $this->_is_collection ) {
+Privileges:DAVResource.php:      if ( !isset( $this->collection->type ) || $this->collection->type == 'collection' ) {
+Privileges:DAVResource.php:        if ( $this->_is_principal )
+Privileges:DAVResource.php:          $this->collection->type = 'principal';
+Privileges:DAVResource.php:        else if ( $row->is_calendar == 't' ) {
+Privileges:DAVResource.php:          $this->collection->type = 'calendar';
+Privileges:DAVResource.php:        else if ( $row->is_addressbook == 't' ) {
+Privileges:DAVResource.php:          $this->collection->type = 'addressbook';
+Privileges:DAVResource.php:        else if ( isset($row->is_proxy) && $row->is_proxy == 't' ) {
+Privileges:DAVResource.php:          $this->collection->type = 'proxy';
+Privileges:DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+Privileges:DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+Privileges:DAVResource.php:        else if ( $this->dav_name == '/' )
+Privileges:DAVResource.php:          $this->collection->type = 'root';
+Privileges:DAVResource.php:          $this->collection->type = 'collection';
+Privileges:DAVResource.php:      $this->_is_calendar     = ($this->collection->is_calendar == 't');
+Privileges:DAVResource.php:      $this->_is_addressbook  = ($this->collection->is_addressbook == 't');
+Privileges:DAVResource.php:      $this->_is_proxy_request= ($this->collection->type == 'proxy');
+Privileges:DAVResource.php:      if ( $this->_is_principal && !isset($this->resourcetypes) ) {
+Privileges:DAVResource.php:        $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+Privileges:DAVResource.php:      else if ( $this->_is_proxy_request ) {
+Privileges:DAVResource.php:        $this->resourcetypes  = $this->collection->resourcetypes;
+Privileges:DAVResource.php:      if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+Privileges:DAVResource.php:      $this->resourcetypes = '';
+Privileges:DAVResource.php:      if ( isset($this->resource->caldav_data) ) {
+Privileges:DAVResource.php:        if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) $this->contenttype = 'text/calendar';
+Privileges:DAVResource.php:        $this->resource->displayname = $this->resource->summary;
+Privileges:DAVResource.php:    $this->dav_name = DeconstructURL($inpath);
+Privileges:DAVResource.php:    $this->FetchCollection();
+Privileges:DAVResource.php:    if ( $this->_is_collection ) {
+Privileges:DAVResource.php:      if ( $this->_is_principal || $this->collection->type == 'principal' )   $this->FetchPrincipal();
+Privileges:DAVResource.php:      $this->FetchResource();
+Privileges:DAVResource.php:               $this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
+Privileges:DAVResource.php:    *    - also discussed at more length in 5.2
+Privileges:DAVResource.php:    *  - The exact request.
+Privileges:DAVResource.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+Privileges:DAVResource.php:    *  - The request URL truncated to the last '/'
+Privileges:DAVResource.php:    dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->dav_name );
+Privileges:DAVResource.php:    $this->collection = (object) array(
+Privileges:DAVResource.php:      'collection_id' => -1,
+Privileges:DAVResource.php:    $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:DAVResource.php:    if ( !preg_match( '#/$#', $this->dav_name ) ) {
+Privileges:DAVResource.php:      $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+Privileges:DAVResource.php:      $params[':plus_slash']  = $this->dav_name.'/';
+Privileges:DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+Privileges:DAVResource.php:      $this->collection = $row;
+Privileges:DAVResource.php:      $this->collection->exists = true;
+Privileges:DAVResource.php:      if ( $row->is_calendar == 't' )
+Privileges:DAVResource.php:        $this->collection->type = 'calendar';
+Privileges:DAVResource.php:      else if ( $row->is_addressbook == 't' )
+Privileges:DAVResource.php:        $this->collection->type = 'addressbook';
+Privileges:DAVResource.php:      else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+Privileges:DAVResource.php:        $this->collection->type = 'schedule-'. $matches[3]. 'box';
+Privileges:DAVResource.php:        $this->collection->type = 'collection';
+Privileges:DAVResource.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->dav_name, $matches ) ) {
+Privileges:DAVResource.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+Privileges:DAVResource.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+Privileges:DAVResource.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+Privileges:DAVResource.php:      $qry->Exec('DAVResource');
+Privileges:DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+Privileges:DAVResource.php:        $this->collection = $row;
+Privileges:DAVResource.php:        $this->collection->exists = true;
+Privileges:DAVResource.php:        $this->collection->type = $this->collection_type;
+Privileges:DAVResource.php:    else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
+Privileges:DAVResource.php:      $this->collection->type = 'proxy';
+Privileges:DAVResource.php:      $this->_is_proxy_request = true;
+Privileges:DAVResource.php:      $this->proxy_type = $matches[3];
+Privileges:DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+Privileges:DAVResource.php:      $this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
+Privileges:DAVResource.php:      $this->collection->exists = true;
+Privileges:DAVResource.php:      $this->collection->parent_container = $matches[1] . '/';
+Privileges:DAVResource.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches)
+Privileges:DAVResource.php:           || preg_match( '#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
+Privileges:DAVResource.php:      $this->_is_principal = true;
+Privileges:DAVResource.php:      $this->FetchPrincipal();
+Privileges:DAVResource.php:    else if ( $this->dav_name == '/' ) {
+Privileges:DAVResource.php:      $this->collection->dav_name = '/';
+Privileges:DAVResource.php:      $this->collection->type = 'root';
+Privileges:DAVResource.php:      $this->collection->exists = true;
+Privileges:DAVResource.php:      $this->collection->displayname = $c->system_name;
+Privileges:DAVResource.php:      $this->collection->default_privileges = (1 | 16 | 32);
+Privileges:DAVResource.php:      $this->collection->parent_container = '/';
+Privileges:DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+Privileges:DAVResource.php:      if ( !preg_match( '#/$#', $this->dav_name ) ) {
+Privileges:DAVResource.php:        $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+Privileges:DAVResource.php:        $params[':plus_slash']  = $this->dav_name.'/';
+Privileges:DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+Privileges:DAVResource.php:        $this->collection = $row;
+Privileges:DAVResource.php:        $this->collection->exists = true;
+Privileges:DAVResource.php:        $this->_is_binding = true;
+Privileges:DAVResource.php:        $this->collection->parent_set = $row->parent_container;
+Privileges:DAVResource.php:        $this->collection->parent_container = $row->bind_parent_container;
+Privileges:DAVResource.php:        $this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
+Privileges:DAVResource.php:        $this->collection->bound_from = $row->dav_name;
+Privileges:DAVResource.php:        $this->collection->dav_name = $row->bound_to;
+Privileges:DAVResource.php:        if ( isset($row->access_ticket_id) ) {
+Privileges:DAVResource.php:          if ( !isset($this->tickets) ) $this->tickets = array();
+Privileges:DAVResource.php:          $this->tickets[] = new DAVTicket($row->access_ticket_id);
+Privileges:DAVResource.php:        if ( $row->is_calendar == 't' )
+Privileges:DAVResource.php:          $this->collection->type = 'calendar';
+Privileges:DAVResource.php:        else if ( $row->is_addressbook == 't' )
+Privileges:DAVResource.php:          $this->collection->type = 'addressbook';
+Privileges:DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+Privileges:DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+Privileges:DAVResource.php:          $this->collection->type = 'collection';
+Privileges:DAVResource.php:        dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
+Privileges:DAVResource.php:        $this->collection->exists = false;
+Privileges:DAVResource.php:        $this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
+Privileges:DAVResource.php:    @dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
+Privileges:DAVResource.php:    $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
+Privileges:DAVResource.php:    if ( $this->_is_collection ) {
+Privileges:DAVResource.php:      $this->dav_name = $this->collection->dav_name;
+Privileges:DAVResource.php:      $this->resource_id = $this->collection->collection_id;
+Privileges:DAVResource.php:      $this->_is_calendar    = ($this->collection->type == 'calendar');
+Privileges:DAVResource.php:      $this->_is_addressbook = ($this->collection->type == 'addressbook');
+Privileges:DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+Privileges:DAVResource.php:      if ( !isset($this->exists) && isset($this->collection->exists) ) {
+Privileges:DAVResource.php:        $this->exists = $this->collection->exists;
+Privileges:DAVResource.php:      if ( $this->exists ) {
+Privileges:DAVResource.php:        if ( isset($this->collection->dav_etag) ) $this->unique_tag = '"'.$this->collection->dav_etag.'"';
+Privileges:DAVResource.php:        if ( isset($this->collection->created) )  $this->created = $this->collection->created;
+Privileges:DAVResource.php:        if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
+Privileges:DAVResource.php:        if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+Privileges:DAVResource.php:        if ( !isset($this->parent) ){ 
+Privileges:DAVResource.php:            $this->FetchParentContainer();
+Privileges:DAVResource.php:        $this->user_no = $this->parent->GetProperty('user_no');
+Privileges:DAVResource.php:      if ( isset($this->collection->resourcetypes) )
+Privileges:DAVResource.php:        $this->resourcetypes = $this->collection->resourcetypes;
+Privileges:DAVResource.php:        $this->resourcetypes = '<DAV::collection/>';
+Privileges:DAVResource.php:        if ( $this->_is_principal )   $this->resourcetypes .= '<DAV::principal/>';
+Privileges:DAVResource.php:        if ( $this->_is_addressbook ) $this->resourcetypes .= '<urn:ietf:params:xml:ns:carddav:addressbook/>';
+Privileges:DAVResource.php:        if ( $this->_is_calendar )    $this->resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+Privileges:DAVResource.php:    if ( isset($this->principal) ) return;
+Privileges:DAVResource.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->bound_from() ) );
+Privileges:DAVResource.php:    if ( $this->_is_principal && $this->principal->Exists() ) {
+Privileges:DAVResource.php:      $this->exists = true;
+Privileges:DAVResource.php:      $this->unique_tag = $this->principal->dav_etag;
+Privileges:DAVResource.php:      $this->created = $this->principal->created;
+Privileges:DAVResource.php:      $this->modified = $this->principal->modified;
+Privileges:DAVResource.php:      $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+Privileges:DAVResource.php:      $this->resource_id = $this->principal->principal_id;
+Privileges:DAVResource.php:      $this->collection = $this->principal->AsCollection();
+Privileges:DAVResource.php:      $this->user_no = $this->principal->user_no;
+Privileges:DAVResource.php:    elseif ( $this->_is_principal ) {
+Privileges:DAVResource.php:      $this->exists = false;
+Privileges:DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+Privileges:DAVResource.php:      $this->collection->type = 'principal';
+Privileges:DAVResource.php:    if ( isset($this->exists) ) return;   // True or false, we've got what we can already
+Privileges:DAVResource.php:    if ( $this->_is_collection ) return;   // We have all we're going to read
+Privileges:DAVResource.php:    $params = array( ':dav_name' => $this->bound_from() );
+Privileges:DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+Privileges:DAVResource.php:      $this->exists = true;
+Privileges:DAVResource.php:      $this->resource = $qry->Fetch();
+Privileges:DAVResource.php:      $this->unique_tag = $this->resource->dav_etag;
+Privileges:DAVResource.php:      $this->created = $this->resource->created;
+Privileges:DAVResource.php:      $this->modified = $this->resource->modified;
+Privileges:DAVResource.php:      $this->resource_id = $this->resource->dav_id;
+Privileges:DAVResource.php:      if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
+Privileges:DAVResource.php:        $this->contenttype = 'text/calendar';
+Privileges:DAVResource.php:      $this->resourcetypes = '';
+Privileges:DAVResource.php:      $this->exists = false;
+Privileges:DAVResource.php:    if ( isset($this->dead_properties) ) return;
+Privileges:DAVResource.php:    $this->dead_properties = array();
+Privileges:DAVResource.php:    $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
+Privileges:DAVResource.php:    if ( $qry->Exec('DAVResource') ) {
+Privileges:DAVResource.php:      while ( $property = $qry->Fetch() ) {
+Privileges:DAVResource.php:        $this->dead_properties[$property->property_name] = $property->property_value;
+Privileges:DAVResource.php:    if ( $this->dav_name == '/' ) return null;
+Privileges:DAVResource.php:    if ( !isset($this->parent) ) {
+Privileges:DAVResource.php:      if ( $this->_is_collection ) {
+Privileges:DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+Privileges:DAVResource.php:        $this->parent = new DAVResource( $this->parent_path() );
+Privileges:DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+Privileges:DAVResource.php:        $this->parent = new DAVResource($this->collection->dav_name);
+Privileges:DAVResource.php:    return $this->parent;
+Privileges:DAVResource.php:    if ( $this->dav_name == '/' || $this->dav_name == '' ) {
+Privileges:DAVResource.php:      $this->privileges = (1 | 16 | 32); // read + read-acl + read-current-user-privilege-set
+Privileges:DAVResource.php:    if ( $session->AllowedTo('Admin') ) {
+Privileges:DAVResource.php:      $this->privileges = privilege_to_bits('all');
+Privileges:DAVResource.php:    if ( $this->IsPrincipal() ) {
+Privileges:DAVResource.php:      if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:      $this->privileges = $this->principal->Privileges();
+Privileges:DAVResource.php:      dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing "%s"', $this->privileges, $this->principal->username() );
+Privileges:DAVResource.php:    if ( ! isset($this->collection) ) $this->FetchCollection();
+Privileges:DAVResource.php:    $this->privileges = 0;
+Privileges:DAVResource.php:    if ( !isset($this->collection->path_privs) ) {
+Privileges:DAVResource.php:      if ( !isset($this->parent) ) $this->FetchParentContainer();
+Privileges:DAVResource.php:      $this->collection->path_privs = $this->parent->Privileges();
+Privileges:DAVResource.php:      $this->collection->user_no = $this->parent->GetProperty('user_no');
+Privileges:DAVResource.php:      $this->collection->principal_id = $this->parent->GetProperty('principal_id');
+Privileges:DAVResource.php:    $this->privileges = $this->collection->path_privs;
+Privileges:DAVResource.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+Privileges:DAVResource.php:                       decbin($this->privileges), $session->username, $this->dav_name() );
+Privileges:DAVResource.php:    if ( isset($request->ticket) && $request->ticket->MatchesPath($this->bound_from()) ) {
+Privileges:DAVResource.php:      $this->privileges |= $request->ticket->privileges();
+Privileges:DAVResource.php:      dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
+Privileges:DAVResource.php:    if ( isset($this->tickets) ) {
+Privileges:DAVResource.php:      if ( !isset($this->resource_id) ) $this->FetchResource();
+Privileges:DAVResource.php:      foreach( $this->tickets AS $k => $ticket ) {
+Privileges:DAVResource.php:        if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
+Privileges:DAVResource.php:          $this->privileges |= $ticket->privileges();
+Privileges:DAVResource.php:          dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
+Privileges:DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+Privileges:DAVResource.php:    return $this->privileges;
+Privileges:DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+Privileges:DAVResource.php:        $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+Privileges:DAVResource.php:      return ($this->privileges & $test_bits) > 0;
+Privileges:DAVResource.php:      return ($this->privileges & $test_bits) == $test_bits;
+Privileges:DAVResource.php:    if ( $this->HavePrivilegeTo($privilege, $any) ) return;
+Privileges:DAVResource.php:    $request->NeedPrivilege( $privilege, $this->dav_name );
+Privileges:DAVResource.php:      if ( !isset($this->privileges) ) $this->FetchPrivileges();
+Privileges:DAVResource.php:      $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type : null ) );
+Privileges:DAVResource.php:    if ( isset($this->supported_methods) ) return $this->supported_methods;
+Privileges:DAVResource.php:    $this->supported_methods = array(
+Privileges:DAVResource.php:    if ( $this->IsCollection() ) {
+Privileges:DAVResource.php:/*      if ( $this->IsPrincipal() ) {
+Privileges:DAVResource.php:        $this->supported_methods['MKCALENDAR'] = '';
+Privileges:DAVResource.php:        $this->supported_methods['MKCOL'] = '';
+Privileges:DAVResource.php:      switch ( $this->collection->type ) {
+Privileges:DAVResource.php:          $this->supported_methods = array(
+Privileges:DAVResource.php:        case 'schedule-outbox':
+Privileges:DAVResource.php:          $this->supported_methods = array_merge(
+Privileges:DAVResource.php:            $this->supported_methods,
+Privileges:DAVResource.php:        case 'schedule-inbox':
+Privileges:DAVResource.php:          $this->supported_methods['GET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['PUT'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['HEAD'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['GET'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['PUT'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['HEAD'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['MKCOL'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['MKCALENDAR'] = '';
+Privileges:DAVResource.php:          $this->supported_methods['PROPPATCH'] = '';
+Privileges:DAVResource.php:      $this->supported_methods = array_merge(
+Privileges:DAVResource.php:        $this->supported_methods,
+Privileges:DAVResource.php:    return $this->supported_methods;
+Privileges:DAVResource.php:    if ( !isset($this->supported_methods) ) $this->FetchSupportedMethods();
+Privileges:DAVResource.php:    foreach( $this->supported_methods AS $k => $v ) {
+Privileges:DAVResource.php:      $methods[] = new XMLElement( 'supported-method', null, array('name' => $k) );
+Privileges:DAVResource.php:    if ( isset($this->supported_reports) ) return $this->supported_reports;
+Privileges:DAVResource.php:   /************* comentado por incompatibilidade do sync-collection 
+Privileges:DAVResource.php:    $this->supported_reports = array(
+Privileges:DAVResource.php:      'DAV::principal-property-search' => '',
+Privileges:DAVResource.php:      'DAV::principal-search-property-set' => '',
+Privileges:DAVResource.php:      'DAV::expand-property' => '',
+Privileges:DAVResource.php:      'DAV::sync-collection' => ''
+Privileges:DAVResource.php:    $this->supported_reports = array(
+Privileges:DAVResource.php:      'DAV::principal-property-search' => '',
+Privileges:DAVResource.php:      'DAV::principal-search-property-set' => '',
+Privileges:DAVResource.php:      'DAV::expand-property' => ''
+Privileges:DAVResource.php:    if ( !isset($this->collection) ) $this->FetchCollection();
+Privileges:DAVResource.php:    if ( $this->collection->is_calendar ) {
+Privileges:DAVResource.php:      $this->supported_reports = array_merge(
+Privileges:DAVResource.php:        $this->supported_reports,
+Privileges:DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+Privileges:DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+Privileges:DAVResource.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+Privileges:DAVResource.php:    if ( $this->collection->is_addressbook ) {
+Privileges:DAVResource.php:      $this->supported_reports = array_merge(
+Privileges:DAVResource.php:        $this->supported_reports,
+Privileges:DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+Privileges:DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+Privileges:DAVResource.php:    return $this->supported_reports;
+Privileges:DAVResource.php:    if ( !isset($this->supported_reports) ) $this->FetchSupportedReports();
+Privileges:DAVResource.php:    foreach( $this->supported_reports AS $k => $v ) {
+Privileges:DAVResource.php:      $reply->NSElement($report, $k );
+Privileges:DAVResource.php:      $reports[] = new XMLElement('supported-report', $report );
+Privileges:DAVResource.php:    if ( isset($this->access_tickets) ) return;
+Privileges:DAVResource.php:    $this->access_tickets = array();
+Privileges:DAVResource.php:        EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds,
+Privileges:DAVResource.php:    $params = array(':collection_id' => $this->collection->collection_id, ':scan_depth' => $c->permission_scan_depth);
+Privileges:DAVResource.php:    if ( $this->IsCollection() ) {
+Privileges:DAVResource.php:      if ( !isset($this->exists) ) $this->FetchResource();
+Privileges:DAVResource.php:      $params[':dav_id'] = $this->resource->dav_id;
+Privileges:DAVResource.php:    if ( isset($this->exists) && !$this->exists ) return;
+Privileges:DAVResource.php:    if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
+Privileges:DAVResource.php:      while( $ticket = $qry->Fetch() ) {
+Privileges:DAVResource.php:        $this->access_tickets[] = $ticket;
+Privileges:DAVResource.php:  * If the current user does not have DAV::read-acl privilege on this resource they
+Privileges:DAVResource.php:    if ( !isset($this->access_tickets) ) $this->FetchTickets();
+Privileges:DAVResource.php:    $show_all = $this->HavePrivilegeTo('DAV::read-acl');
+Privileges:DAVResource.php:    foreach( $this->access_tickets AS $meh => $trow ) {
+Privileges:DAVResource.php:      if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) ) continue;
+Privileges:DAVResource.php:      dbg_error_log( 'DAVResource', ':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
+Privileges:DAVResource.php:      $ticket = new XMLElement( $reply->Tag( 'ticketinfo', 'http://www.xythos.com/namespaces/StorageServer', 'TKT' ) );
+Privileges:DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
+Privileges:DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
+Privileges:DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf( 'Seconds-%d', $trow->seconds) : 'infinity') );
+Privileges:DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:visits', 'infinity' );
+Privileges:DAVResource.php:      foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
+Privileges:DAVResource.php:        $privs[] = $reply->NewXMLElement($v);
+Privileges:DAVResource.php:      $reply->NSElement($ticket, 'DAV::privilege', $privs );
+Privileges:DAVResource.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+Privileges:DAVResource.php:    if ( !isset($this->_locks_found) ) {
+Privileges:DAVResource.php:      $this->_locks_found = array();
+Privileges:DAVResource.php:      $qry = new AwlQuery($sql, array( ':this_path' => $this->dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
+Privileges:DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) ) {
+Privileges:DAVResource.php:        while( $lock_row = $qry->Fetch() ) {
+Privileges:DAVResource.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+Privileges:DAVResource.php:        $this->DoResponse(500,i18n("Database Error"));
+Privileges:DAVResource.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+Privileges:DAVResource.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
+Privileges:DAVResource.php:    return $this->_is_collection;
+Privileges:DAVResource.php:    return $this->_is_collection && $this->_is_principal;
+Privileges:DAVResource.php:    return $this->_is_collection && $this->_is_calendar;
+Privileges:DAVResource.php:    if ( $this->_is_collection && preg_match( '{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
+Privileges:DAVResource.php:    return $this->_is_collection && $this->_is_addressbook;
+Privileges:DAVResource.php:    return $this->_is_binding;
+Privileges:DAVResource.php:    if ( ! isset($this->exists) ) {
+Privileges:DAVResource.php:      if ( $this->IsPrincipal() ) {
+Privileges:DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:        $this->exists = $this->principal->Exists();
+Privileges:DAVResource.php:      else if ( ! $this->IsCollection() ) {
+Privileges:DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php://    dbg_error_log('DAVResource',' Checking whether "%s" exists.  It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
+Privileges:DAVResource.php:    return $this->exists;
+Privileges:DAVResource.php:    if ( $this->collection->dav_name != $this->dav_name ) {
+Privileges:DAVResource.php:      return $this->collection->exists;
+Privileges:DAVResource.php:    $parent = $this->FetchParentContainer();
+Privileges:DAVResource.php:    return $parent->Exists();
+Privileges:DAVResource.php:    if ( isset($this->dav_name) ) return $this->dav_name;
+Privileges:DAVResource.php:    if ( isset($this->bound_from) ) return $this->bound_from;
+Privileges:DAVResource.php:    return $this->dav_name();
+Privileges:DAVResource.php:    if ( !isset($this->bound_from) && isset($this->dav_name) ) {
+Privileges:DAVResource.php:      $this->bound_from = $this->dav_name;
+Privileges:DAVResource.php:    $this->dav_name = $new_dav_name;
+Privileges:DAVResource.php:    return $this->dav_name;
+Privileges:DAVResource.php:    if ( $this->IsCollection() ) {
+Privileges:DAVResource.php:      if ( !isset($this->collection) ) $this->FetchCollection();
+Privileges:DAVResource.php:      if ( !isset($this->collection->parent_container) ) {
+Privileges:DAVResource.php:        $this->collection->parent_container = preg_replace( '{[^/]+/$}', '', $this->bound_from());
+Privileges:DAVResource.php:      return $this->collection->parent_container;
+Privileges:DAVResource.php:    return preg_replace( '{[^/]+$}', '', $this->bound_from());
+Privileges:DAVResource.php:  * Returns the principal-URL for this resource
+Privileges:DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:    if ( $this->principal->Exists() ) {
+Privileges:DAVResource.php:      return $this->principal->principal_url;
+Privileges:DAVResource.php:    if ( !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:    return $this->resource;
+Privileges:DAVResource.php:    if ( isset($this->unique_tag) ) return $this->unique_tag;
+Privileges:DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:    if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
+Privileges:DAVResource.php:    return $this->unique_tag;
+Privileges:DAVResource.php:  * Returns the definitive resource_id for this resource - usually a dav_id
+Privileges:DAVResource.php:    if ( isset($this->resource_id) ) return $this->resource_id;
+Privileges:DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:    if ( $this->exists !== true || !isset($this->resource_id) ) $this->resource_id = null;
+Privileges:DAVResource.php:    return $this->resource_id;
+Privileges:DAVResource.php:    return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' );
+Privileges:DAVResource.php:    if ( $this->IsPrincipal() ) return 'root';
+Privileges:DAVResource.php:    if ( !$this->IsCollection() ) return $this->collection->type;
+Privileges:DAVResource.php:    if ( ! isset($this->collection->parent_container) ) return null;
+Privileges:DAVResource.php:    if ( isset($this->parent_container_type) ) return $this->parent_container_type;
+Privileges:DAVResource.php:    if ( preg_match('#/[^/]+/#', $this->collection->parent_container) ) {
+Privileges:DAVResource.php:      $this->parent_container_type = 'principal';
+Privileges:DAVResource.php:                                array( ':parent_name' => $this->collection->parent_container ) );
+Privileges:DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
+Privileges:DAVResource.php:        if ( $parent->is_calendar == 't' )
+Privileges:DAVResource.php:          $this->parent_container_type = 'calendar';
+Privileges:DAVResource.php:        else if ( $parent->is_addressbook == 't' )
+Privileges:DAVResource.php:          $this->parent_container_type = 'addressbook';
+Privileges:DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+Privileges:DAVResource.php:          $this->parent_container_type = 'schedule-'. $matches[3]. 'box';
+Privileges:DAVResource.php:          $this->parent_container_type = 'collection';
+Privileges:DAVResource.php:        $this->parent_container_type = null;
+Privileges:DAVResource.php:    return $this->parent_container_type;
+Privileges:DAVResource.php:  * BuildACE - construct an XMLElement subtree for a DAV::ace
+Privileges:DAVResource.php:    $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type : 'resource'));
+Privileges:DAVResource.php:        $xmldoc->NSElement($privilege,$k);
+Privileges:DAVResource.php:        $privilege->NewElement($k);
+Privileges:DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:    $default_privs = $this->principal->default_privileges;
+Privileges:DAVResource.php:    if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
+Privileges:DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, pow(2,25) - 1, new XMLElement('property', new XMLElement('owner')) );
+Privileges:DAVResource.php:                                array( ':collection_id' => $this->collection->collection_id, ':principal_id' => $this->principal->principal_id ) );
+Privileges:DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+Privileges:DAVResource.php:      while( $grant = $qry->Fetch() ) {
+Privileges:DAVResource.php:        if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
+Privileges:DAVResource.php:        if ( $by_collection &&  !isset($grant->by_collection) ) break;
+Privileges:DAVResource.php:        $acl[] = $this->BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
+Privileges:DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, $default_privs, new XMLElement('authenticated') );
+Privileges:DAVResource.php:  * Return general server-related properties, in plain form
+Privileges:DAVResource.php:        return $this->collection->collection_id;
+Privileges:DAVResource.php:        if ( isset($this->resourcetypes) ) {
+Privileges:DAVResource.php:          $this->resourcetypes = preg_replace('{^\s*<(.*)/>\s*$}', '$1', $this->resourcetypes);
+Privileges:DAVResource.php:          $type_list = preg_split('{(/>\s*<|\n)}', $this->resourcetypes);
+Privileges:DAVResource.php:        if ( isset($this->{$name}) ) return $this->{$name};
+Privileges:DAVResource.php:        if ( $this->_is_principal ) {
+Privileges:DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+Privileges:DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+Privileges:DAVResource.php:        else if ( $this->_is_collection ) {
+Privileges:DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+Privileges:DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+Privileges:DAVResource.php:          if ( !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:          if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
+Privileges:DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+Privileges:DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+Privileges:DAVResource.php://        dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
+Privileges:DAVResource.php:    if ( isset($this->dead_properties) ) $this->FetchDeadProperties();
+Privileges:DAVResource.php:    $allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()),
+Privileges:DAVResource.php:        'DAV::owner', 'DAV::principal-URL', 'DAV::current-user-principal',
+Privileges:DAVResource.php:        'urn:ietf:params:xml:ns:carddav:max-resource-size', 'urn:ietf:params:xml:ns:carddav:supported-address-data',
+Privileges:DAVResource.php:        'urn:ietf:params:xml:ns:carddav:addressbook-description', 'urn:ietf:params:xml:ns:carddav:addressbook-home-set'
+Privileges:DAVResource.php:  * Return general server-related properties for this URL
+Privileges:DAVResource.php://    dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
+Privileges:DAVResource.php:        $property_list = $this->DAV_AllProperties();
+Privileges:DAVResource.php:          $this->ResourceProperty($v, $prop, $reply, $discarded);
+Privileges:DAVResource.php:        $prop->NewElement('href', ConstructURL($this->dav_name) );
+Privileges:DAVResource.php:      case 'DAV::resource-id':
+Privileges:DAVResource.php:        if ( $this->resource_id > 0 )
+Privileges:DAVResource.php:          $reply->DAVElement( $prop, 'resource-id', $reply->href(ConstructURL('/.resources/'.$this->resource_id) ) );
+Privileges:DAVResource.php:      case 'DAV::parent-set':
+Privileges:DAVResource.php:        $qry = new AwlQuery($sql, array( ':bound_from' => $this->bound_from() ) );
+Privileges:DAVResource.php:        if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:DAVResource.php:          while( $row = $qry->Fetch() ) {
+Privileges:DAVResource.php:            $parents[$row->parent_container] = true;
+Privileges:DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->bound_from())] = true;
+Privileges:DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->dav_name())] = true;
+Privileges:DAVResource.php:        $parent_set = $reply->DAVElement( $prop, 'parent-set' );
+Privileges:DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+Privileges:DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+Privileges:DAVResource.php:        if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:        $prop->NewElement('getcontenttype', $this->contenttype );
+Privileges:DAVResource.php:        $resourcetypes = $prop->NewElement('resourcetype' );
+Privileges:DAVResource.php:        $type_list = $this->GetProperty('resourcetype');
+Privileges:DAVResource.php:          $reply->NSElement( $resourcetypes, $v );
+Privileges:DAVResource.php:        if ( $this->_is_binding ) {
+Privileges:DAVResource.php:          $reply->NSElement( $resourcetypes, 'http://xmlns.davical.org/davical:webdav-binding' );
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
+Privileges:DAVResource.php:        if ( $this->_is_collection ) return false;
+Privileges:DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:        if ( isset($this->resource) ) {
+Privileges:DAVResource.php:          $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
+Privileges:DAVResource.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+Privileges:DAVResource.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $locale );
+Privileges:DAVResource.php:      case 'DAV::acl-restrictions':
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, array( new XMLElement('grant-only'), new XMLElement('no-invert') ) );
+Privileges:DAVResource.php:      case 'DAV::inherited-acl-set':
+Privileges:DAVResource.php:        if ( ! $this->_is_collection ) {
+Privileges:DAVResource.php:          $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $inherited_acls );
+Privileges:DAVResource.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+Privileges:DAVResource.php:        $owner_url = ( isset($this->_is_binding) && $this->_is_binding ? $this->collection->bind_owner_url : $this->principal_url() );
+Privileges:DAVResource.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $owner_url ) );
+Privileges:DAVResource.php:      case 'DAV::alternate-URI-set':
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag );
+Privileges:DAVResource.php:        if ( $this->_is_collection ) return false;
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+Privileges:DAVResource.php:        if ( ! $this->_is_collection ) return false;
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+Privileges:DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
+Privileges:DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
+Privileges:DAVResource.php:        $reply->CalendarserverElement($prop, 'calendar-proxy-'.$proxy_type.'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
+Privileges:DAVResource.php:      case 'DAV::current-user-privilege-set':
+Privileges:DAVResource.php:        //if ( $this->HavePrivilegeTo('DAV::read-current-user-privilege-set') ) {
+Privileges:DAVResource.php:          $reply->NSElement($prop, $tag, $this->BuildPrivileges() );
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
+Privileges:DAVResource.php:        if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() ) return false;
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, 'text/calendar' );
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
+Privileges:DAVResource.php:        if ( ! $this->_is_collection ) return false;
+Privileges:DAVResource.php:        if ( $this->IsCalendar() )
+Privileges:DAVResource.php:        else if ( $this->IsSchedulingCollection() )
+Privileges:DAVResource.php:          $components[] = $reply->NewXMLElement( 'comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
+Privileges:DAVResource.php:        $reply->CalDAVElement($prop, 'supported-calendar-component-set', $components );
+Privileges:DAVResource.php:      case 'DAV::supported-method-set':
+Privileges:DAVResource.php:        $prop->NewElement('supported-method-set', $this->BuildSupportedMethods() );
+Privileges:DAVResource.php:      case 'DAV::supported-report-set':
+Privileges:DAVResource.php:        $prop->NewElement('supported-report-set', $this->BuildSupportedReports( $reply ) );
+Privileges:DAVResource.php:        $prop->NewElement('supportedlock',
+Privileges:DAVResource.php:      case 'DAV::supported-privilege-set':
+Privileges:DAVResource.php:        $prop->NewElement('supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
+Privileges:DAVResource.php:      case 'DAV::current-user-principal':
+Privileges:DAVResource.php:        $prop->NewElement('current-user-principal', $reply->href( $request->principal->principal_url ) );
+Privileges:DAVResource.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+Privileges:DAVResource.php:        $denied[] = $reply->Tag($tag);
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+Privileges:DAVResource.php:        if ( ! $this->_is_collection ) return false;
+Privileges:DAVResource.php:        if ( !isset($this->collection->tz_spec) || $this->collection->tz_spec == '' ) return false;
+Privileges:DAVResource.php:        $cal->VCalendar();
+Privileges:DAVResource.php:        $cal->AddComponent( new iCalComponent($this->collection->tz_spec) );
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $cal->Render() );
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:address-data':
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+Privileges:DAVResource.php:        if ( $this->_is_collection ) return false;
+Privileges:DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $this->resource->caldav_data );
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
+Privileges:DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, 65500 );
+Privileges:DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
+Privileges:DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+Privileges:DAVResource.php:        $address_data = $reply->NewXMLElement( 'address-data', false,
+Privileges:DAVResource.php:                      array( 'content-type' => 'text/vcard', 'version' => '3.0'), 'urn:ietf:params:xml:ns:carddav');
+Privileges:DAVResource.php:        $reply->NSElement($prop, $tag, $address_data );
+Privileges:DAVResource.php:        if ( $this->HavePrivilegeTo('DAV::read-acl') ) {
+Privileges:DAVResource.php:          $reply->NSElement($prop, $tag, $this->GetACL( $reply ) );
+Privileges:DAVResource.php:        $reply->NSElement($prop,'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply) );
+Privileges:DAVResource.php:        $property_value = $this->GetProperty(preg_replace('{^.*:}', '', $tag));
+Privileges:DAVResource.php:          $reply->NSElement($prop, $tag, $property_value );
+Privileges:DAVResource.php:          if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
+Privileges:DAVResource.php:          if ( isset($this->dead_properties[$tag]) ) {
+Privileges:DAVResource.php:            $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
+Privileges:DAVResource.php://            dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
+Privileges:DAVResource.php:    dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
+Privileges:DAVResource.php:        $tag = $tag->GetTag();
+Privileges:DAVResource.php:      $found = $this->ResourceProperty($tag, $prop, $reply, $denied );
+Privileges:DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+Privileges:DAVResource.php:        $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
+Privileges:DAVResource.php://        dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
+Privileges:DAVResource.php:        $not_found[] = $reply->Tag($tag);
+Privileges:DAVResource.php:        $reply->NSElement($noprop, $v);
+Privileges:DAVResource.php:        $noprop->NewElement($v);
+Privileges:DAVResource.php:    dbg_error_log('DAVResource',':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
+Privileges:DAVResource.php:    if ( !$this->Exists() ) return null;
+Privileges:DAVResource.php:    $elements = $this->GetPropStat( $properties, $reply );
+Privileges:DAVResource.php:      $dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
+Privileges:DAVResource.php:      $dav_name = $this->dav_name;
+Privileges:DAVResource.php:    array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
+Privileges:DAVTicket.php:    $this->dav_name             = null;
+Privileges:DAVTicket.php:    $this->target_collection_id = null;
+Privileges:DAVTicket.php:    $this->target_resource_id   = null;
+Privileges:DAVTicket.php:    $this->expiry               = null;
+Privileges:DAVTicket.php:    $this->expired              = true;
+Privileges:DAVTicket.php:    $this->dav_owner_id         = null;
+Privileges:DAVTicket.php:    $this->ticket_id            = $ticket_id;
+Privileges:DAVTicket.php:    $this->privileges           = 0;
+Privileges:DAVTicket.php:    $this->grantor_collection_privileges = 0;
+Privileges:DAVTicket.php:        array(':ticket_id' => $ticket_id, ':scan_depth' => $c->permission_scan_depth)
+Privileges:DAVTicket.php:    if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $t = $qry->Fetch() ) {
+Privileges:DAVTicket.php:      if ( ! $t->expired ) {
+Privileges:DAVTicket.php:          $this->{$k} = $v;
+Privileges:DAVTicket.php:        $this->expired = false;
+Privileges:DAVTicket.php:        $this->privileges = bindec($this->privileges);
+Privileges:DAVTicket.php:        $this->grantor_collection_privileges = bindec($this->grantor_collection_privileges);
+Privileges:DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found a current ticket for "%s"', implode(', ',bits_to_privilege($this->privileges())) );
+Privileges:DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found an expired ticket: %s - %s', $ticket_id, $t->expires );
+Privileges:DAVTicket.php:    if ( isset($this->target_resource_id) ) {
+Privileges:DAVTicket.php:      $qry = new AwlQuery( 'SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id', array(':dav_id' => $this->target_resource_id ) );
+Privileges:DAVTicket.php:      if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $r = $qry->Fetch() ) {
+Privileges:DAVTicket.php:        $this->dav_name = $r->dav_name;
+Privileges:DAVTicket.php:    return $this->dav_name;
+Privileges:DAVTicket.php:    return $this->ticket_id;
+Privileges:DAVTicket.php:    return ($this->privileges & $this->grantor_collection_privileges);
+Privileges:DAVTicket.php:    $length = strlen($this->dav_name);
+Privileges:DAVTicket.php:    return (substr($test_path, 0, $length) == $this->dav_name);
+Privileges:DAVTicket.php:    return ($test_resource_id == $this->target_collection_id || $test_resource_id == $this->target_resource_id);
+Privileges:drivers_ldap.php:require_once("auth-functions.php");
+Privileges:drivers_ldap.php:  /**#@-*/
+Privileges:drivers_ldap.php:      $this->__construct($config);
+Privileges:drivers_ldap.php:          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+Privileges:drivers_ldap.php:          $this->valid=false;
+Privileges:drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
+Privileges:drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_REFERRALS, $config['optReferrals']);
+Privileges:drivers_ldap.php:          $this->connect=ldap_connect($host, $port);
+Privileges:drivers_ldap.php:          $this->connect=ldap_connect($host);
+Privileges:drivers_ldap.php:      if (! $this->connect){
+Privileges:drivers_ldap.php:          $c->messages[] = sprintf(translate( 'drivers_ldap : Unable to connect to LDAP with port %s on host %s'), $port, $host );
+Privileges:drivers_ldap.php:          $this->valid=false;
+Privileges:drivers_ldap.php:        if (!ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+Privileges:drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Failed to set LDAP to use protocol version 3, TLS not supported');
+Privileges:drivers_ldap.php:          $this->valid=false;
+Privileges:drivers_ldap.php:        if (!ldap_start_tls($this->connect)) {
+Privileges:drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Could not start TLS: ldap_start_tls() failed');
+Privileges:drivers_ldap.php:          $this->valid=false;
+Privileges:drivers_ldap.php:        $this->ldap_query_one = 'ldap_read';
+Privileges:drivers_ldap.php:        $this->ldap_query_all = 'ldap_read';
+Privileges:drivers_ldap.php:        $this->ldap_query_one = 'ldap_list';
+Privileges:drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+Privileges:drivers_ldap.php:        $this->ldap_query_one = 'ldap_search';
+Privileges:drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+Privileges:drivers_ldap.php:      if (!ldap_bind($this->connect, (isset($config['bindDN']) ? $config['bindDN'] : null), (isset($config['passDN']) ? $config['passDN'] : null) ) ){
+Privileges:drivers_ldap.php:          $c->messages[] = i18n( 'drivers_ldap : Unable to bind to LDAP - check your configuration for bindDN and passDN, and that your LDAP server is reachable');
+Privileges:drivers_ldap.php:          $this->valid=false;
+Privileges:drivers_ldap.php:      $this->valid = true;
+Privileges:drivers_ldap.php:      $this->baseDNUsers  = is_string($config['baseDNUsers']) ? array($config['baseDNUsers']) : $config['baseDNUsers'];
+Privileges:drivers_ldap.php:      $this->filterUsers  = (isset($config['filterUsers'])  ? $config['filterUsers']  : null);
+Privileges:drivers_ldap.php:      $this->baseDNGroups = is_string($config['baseDNGroups']) ? array($config['baseDNGroups']) : $config['baseDNGroups'];
+Privileges:drivers_ldap.php:      $this->filterGroups = (isset($config['filterGroups']) ? $config['filterGroups'] : null);
+Privileges:drivers_ldap.php:    $query = $this->ldap_query_all;
+Privileges:drivers_ldap.php:    foreach($this->baseDNUsers as $baseDNUsers) {
+Privileges:drivers_ldap.php:      $entry = $query($this->connect,$baseDNUsers,$this->filterUsers,$attributes);
+Privileges:drivers_ldap.php:      if (!ldap_first_entry($this->connect,$entry)) {
+Privileges:drivers_ldap.php:        $c->messages[] = sprintf(translate('Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<'),
+Privileges:drivers_ldap.php:                                 $this->filterUsers,
+Privileges:drivers_ldap.php:      for($i = ldap_first_entry($this->connect,$entry);
+Privileges:drivers_ldap.php:          $i && $arr = ldap_get_attributes($this->connect,$i);
+Privileges:drivers_ldap.php:          $i = ldap_next_entry($this->connect,$i) ) {
+Privileges:drivers_ldap.php:    $query = $this->ldap_query_all;
+Privileges:drivers_ldap.php:    foreach($this->baseDNGroups as $baseDNGroups) {
+Privileges:drivers_ldap.php:      $entry = $query($this->connect,$baseDNGroups,$this->filterGroups,$attributes);
+Privileges:drivers_ldap.php:      if (!ldap_first_entry($this->connect,$entry)) {
+Privileges:drivers_ldap.php:        $c->messages[] = sprintf(translate('Error NoGroupFound with filter >%s<, attributes >%s< , dn >%s<'),
+Privileges:drivers_ldap.php:                                 $this->filterGroups,
+Privileges:drivers_ldap.php:      for($i = ldap_first_entry($this->connect,$entry);
+Privileges:drivers_ldap.php:          $i && $arr = ldap_get_attributes($this->connect,$i);
+Privileges:drivers_ldap.php:          $i = ldap_next_entry($this->connect,$i) ) {
+Privileges:drivers_ldap.php:    $query = $this->ldap_query_one;
+Privileges:drivers_ldap.php:    foreach($this->baseDNUsers as $baseDNUsers) {
+Privileges:drivers_ldap.php:      $entry = $query($this->connect, $baseDNUsers, $filter, $attributes);
+Privileges:drivers_ldap.php:      if (ldap_first_entry($this->connect,$entry) )
+Privileges:drivers_ldap.php:    if ( !ldap_first_entry($this->connect, $entry) ){
+Privileges:drivers_ldap.php:    $dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
+Privileges:drivers_ldap.php:    if ( isset($c->authenticate_hook['config']['i_use_mode_kerberos']) && $c->authenticate_hook['config']['i_use_mode_kerberos'] == "i_know_what_i_am_doing") {
+Privileges:drivers_ldap.php:      if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
+Privileges:drivers_ldap.php:    $i = ldap_first_entry($this->connect,$entry);
+Privileges:drivers_ldap.php:    $arr = ldap_get_attributes($this->connect,$i);
+Privileges:drivers_ldap.php:    $ldapDrivers =& new ldapDrivers($c->authenticate_hook['config']);
+Privileges:drivers_ldap.php:  if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+Privileges:drivers_ldap.php:    foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+Privileges:drivers_ldap.php:        $usr->{$field} =  $value;
+Privileges:drivers_ldap.php:        dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+Privileges:drivers_ldap.php:      $usr->{$field} =  $ldap_values[$value];
+Privileges:drivers_ldap.php:      dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+Privileges:drivers_ldap.php:  if ( !$ldapDriver->valid ) {
+Privileges:drivers_ldap.php:  $mapping = $c->authenticate_hook['config']['mapping_field'];
+Privileges:drivers_ldap.php:  if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
+Privileges:drivers_ldap.php:    $filter_munge = $ldapDriver->filterUsers;
+Privileges:drivers_ldap.php:  else if ( isset($ldapDriver->filterUsers) && $ldapDriver->filterUsers != '' ) {
+Privileges:drivers_ldap.php:    $filter_munge = "($ldapDriver->filterUsers)";
+Privileges:drivers_ldap.php:  $valid = $ldapDriver->requestUser( $filter, $attributes, $username, $password );
+Privileges:drivers_ldap.php:  foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+Privileges:drivers_ldap.php:  $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+Privileges:drivers_ldap.php:    $db_timestamp = $usr->updated;
+Privileges:drivers_ldap.php:    $db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
+Privileges:drivers_ldap.php:  if($ldapDriver->valid){
+Privileges:drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['group_mapping_field'];
+Privileges:drivers_ldap.php:    $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes);
+Privileges:drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:    while($db_group = $qry->Fetch()) {
+Privileges:drivers_ldap.php:      $db_groups[$db_group->group_name] = $db_group->group_name;
+Privileges:drivers_ldap.php:      $db_group_members[$db_group->group_name][] = $db_group->member_name;
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating groups : %s'),join(', ',$groups_to_create));
+Privileges:drivers_ldap.php:        if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+Privileges:drivers_ldap.php:          foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+Privileges:drivers_ldap.php:              $usr->{$field} =  $value;
+Privileges:drivers_ldap.php:              dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+Privileges:drivers_ldap.php:        $user->user_no = 0;
+Privileges:drivers_ldap.php:            $user->{$field} =  $ldap_values[$value];
+Privileges:drivers_ldap.php:            dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+Privileges:drivers_ldap.php:        if ($user->fullname=="") {
+Privileges:drivers_ldap.php:          $user->fullname = $group;
+Privileges:drivers_ldap.php:        if ($user->displayname=="") {
+Privileges:drivers_ldap.php:          $user->displayname = $group;
+Privileges:drivers_ldap.php:        $user->username = $group;
+Privileges:drivers_ldap.php:        $user->updated = "now";  /** @todo Use the 'updated' timestamp from LDAP for groups too */
+Privileges:drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:        $c->messages[] = sprintf(i18n('- adding users %s to group : %s'),join(',',$ldap_groups_info[$group][$mapping['members']]),$group);
+Privileges:drivers_ldap.php:          $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('- updating groups : %s'),join(', ',$groups_to_update));
+Privileges:drivers_ldap.php:          $c->messages[] = sprintf(i18n('- adding %s to group : %s'),join(', ', $add_users ), $group);
+Privileges:drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:          $c->messages[] = sprintf(i18n('- removing %s from group : %s'),join(', ', $remove_users ), $group);
+Privileges:drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivate groups : %s'),join(', ',$groups_to_deactivate));
+Privileges:drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:  if($ldapDriver->valid){
+Privileges:drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['mapping_field'];
+Privileges:drivers_ldap.php:    $ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
+Privileges:drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+Privileges:drivers_ldap.php:      $db_users[] = $db_user->username;
+Privileges:drivers_ldap.php:      $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated);
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating record for users :  %s'),join(', ',$users_to_create));
+Privileges:drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+Privileges:drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+Privileges:drivers_ldap.php:      if ( isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v]) ) continue;
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivating users : %s'),join(', ',$users_to_deactivate));
+Privileges:drivers_ldap.php:      $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+Privileges:drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+Privileges:drivers_ldap.php:        $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
+Privileges:drivers_ldap.php:        $c->messages[] = sprintf(i18n('- updating user records : %s'),join(', ',$users_to_update));
+Privileges:drivers_ldap.php:        $c->messages[] = sprintf(i18n('- nothing done on : %s'),join(', ', $users_nothing_done));
+Privileges:drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+Privileges:drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+Privileges:drivers_ldap.php:      $admins = $db_user->admins;
+Privileges:drivers_ldap.php:      $c->messages[] = sprintf(i18n('Warning: there are no active admin users, you should fix this before logging out.'));
+Privileges:drivers_pwauth_pam.php:require_once("auth-functions.php");
+Privileges:drivers_pwauth_pam.php:  /**#@-*/
+Privileges:drivers_pwauth_pam.php:    $this->__construct($config);
+Privileges:drivers_pwauth_pam.php:      $c->messages[] =
+Privileges:drivers_pwauth_pam.php:      $this->valid=false;
+Privileges:drivers_pwauth_pam.php:  $program = $c->authenticate_hook['config']['path'];
+Privileges:drivers_pwauth_pam.php:  $email_base = $c->authenticate_hook['config']['email_base'];
+Privileges:drivers_squid_pam.php:require_once("auth-functions.php");
+Privileges:drivers_squid_pam.php:  /**#@-*/
+Privileges:drivers_squid_pam.php:      $this->__construct($config);
+Privileges:drivers_squid_pam.php:          $c->messages[] = sprintf(i18n( 'drivers_squid_pam : Unable to find %s file'), $config );
+Privileges:drivers_squid_pam.php:          $this->valid=false;
+Privileges:drivers_squid_pam.php:  $cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth";
+Privileges:drivers_squid_pam.php:              'email' => $username . "@" . $c->authenticate_hook['config']['email_base'],
+Privileges:freebusy-functions.php:include_once('RRule-v2.php');
+Privileges:freebusy-functions.php:  if ( !isset($bin_privs) ) $bin_privs = $request->Privileges();
+Privileges:freebusy-functions.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+Privileges:freebusy-functions.php:  $params = array( ':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
+Privileges:freebusy-functions.php:  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+Privileges:freebusy-functions.php:  if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+Privileges:freebusy-functions.php:    while( $calendar_object = $qry->Fetch() ) {
+Privileges:freebusy-functions.php:      if ( $calendar_object->status == 'TENTATIVE' ) {
+Privileges:freebusy-functions.php:        $extra = ';BUSY-TENTATIVE';
+Privileges:freebusy-functions.php:      dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+Privileges:freebusy-functions.php:      $ics = new vComponent($calendar_object->caldav_data);
+Privileges:freebusy-functions.php:      $expansion = $expanded->GetComponents( array('VEVENT'=>true,'VTODO'=>true,'VJOURNAL'=>true) );
+Privileges:freebusy-functions.php://        printf( "Type: %s\n", $v->GetType());
+Privileges:freebusy-functions.php://        echo "-----------------------------------------------------\n";
+Privileges:freebusy-functions.php:        $start_date = $v->GetProperty('DTSTART');
+Privileges:freebusy-functions.php:        $start_date = new RepeatRuleDateTime($start_date->Value());
+Privileges:freebusy-functions.php:        $duration = $v->GetProperty('DURATION');
+Privileges:freebusy-functions.php:        $duration = ( !isset($duration) ? 'P1D' : $duration->Value());
+Privileges:freebusy-functions.php:        $end_date->modify( $duration );
+Privileges:freebusy-functions.php:        $thisfb = $start_date->UTC() .'/'. $end_date->UTC() . $extra;
+Privileges:freebusy-functions.php:  $freebusy->SetType('VFREEBUSY');
+Privileges:freebusy-functions.php:  $freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
+Privileges:freebusy-functions.php:  $freebusy->AddProperty('DTSTART', $range_start->UTC());
+Privileges:freebusy-functions.php:  $freebusy->AddProperty('DTEND', $range_end->UTC());
+Privileges:freebusy-functions.php:    $freebusy->AddProperty( 'FREEBUSY', $text[0], (isset($text[1]) ? array('FBTYPE' => $text[1]) : null) );
+Privileges:HTTPAuthSession.php:  * User e-mail
+Privileges:HTTPAuthSession.php:  /**#@-*/
+Privileges:HTTPAuthSession.php:    if ( isset($c->http_auth_mode) && $c->http_auth_mode == "Digest" ) {
+Privileges:HTTPAuthSession.php:      $this->DigestAuthSession();
+Privileges:HTTPAuthSession.php:      $this->BasicAuthSession();
+Privileges:HTTPAuthSession.php:  * @param string $auth_header The WWW-Authenticate header details.
+Privileges:HTTPAuthSession.php:      $auth_header = sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name);
+Privileges:HTTPAuthSession.php:    header('Content-type: text/plain; ; charset="utf-8"' );
+Privileges:HTTPAuthSession.php:    else if ( isset($c->authenticate_hook['server_auth_type']) && $c->authenticate_hook['server_auth_type'] == $_SERVER['AUTH_TYPE']
+Privileges:HTTPAuthSession.php:      if ( ! isset($c->authenticate_hook['call']) ) {
+Privileges:HTTPAuthSession.php:        $c->authenticate_hook['call'] = 'auth_external';
+Privileges:HTTPAuthSession.php:      if ( $u = $this->CheckPassword( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
+Privileges:HTTPAuthSession.php:        $this->AssignSessionDetails($u);
+Privileges:HTTPAuthSession.php:    if ( isset($c->allow_unauthenticated) && $c->allow_unauthenticated ) {
+Privileges:HTTPAuthSession.php:      $this->user_no = -1;
+Privileges:HTTPAuthSession.php:      $this->username = 'guest';
+Privileges:HTTPAuthSession.php:      $this->fullname = 'Unauthenticated User';
+Privileges:HTTPAuthSession.php:      $this->email = 'invalid';
+Privileges:HTTPAuthSession.php:    $this->AuthFailedResponse();
+Privileges:HTTPAuthSession.php:      if ( $data = $this->ParseDigestHeader($_SERVER['PHP_AUTH_DIGEST']) ) {
+Privileges:HTTPAuthSession.php:        $A1 = md5($data['username'] . ':' . $c->system_name . ':' . $user_password);
+Privileges:HTTPAuthSession.php:          $this->AssignSessionDetails($u);
+Privileges:HTTPAuthSession.php:    $opaque = md5($c->system_name);
+Privileges:HTTPAuthSession.php:    $this->AuthFailedResponse( sprintf('WWW-Authenticate: Digest realm="%s", qop="auth,auth-int", nonce="%s", opaque="%s"', $c->system_name, $nonce, $opaque ) );
+Privileges:HTTPAuthSession.php:  *  - largely sourced from the PHP documentation
+Privileges:HTTPAuthSession.php:    if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && function_exists($c->authenticate_hook['call']) ) {
+Privileges:HTTPAuthSession.php:      *   - Accept a username / password
+Privileges:HTTPAuthSession.php:      *   - Confirm the username / password are correct
+Privileges:HTTPAuthSession.php:      *   - Create (or update) a 'usr' record in our database
+Privileges:HTTPAuthSession.php:      *   - Return the 'usr' record as an object
+Privileges:HTTPAuthSession.php:      *   - Return === false when authentication fails
+Privileges:HTTPAuthSession.php:      *   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+Privileges:HTTPAuthSession.php:      $hook_response = call_user_func( $c->authenticate_hook['call'], $username, $password );
+Privileges:HTTPAuthSession.php:      if (isset($c->authenticate_hook['optional']) && $c->authenticate_hook['optional']) {
+Privileges:HTTPAuthSession.php:      dbg_error_log( "BasicAuth", ":CheckPassword: Name:%s, Pass:%s, File:%s, Active:%s", $username, $password, $usr->password, ($usr->active?'Yes':'No') );
+Privileges:HTTPAuthSession.php:      if ( $usr->active && session_validate_password( $password, $usr->password ) ) {
+Privileges:HTTPAuthSession.php:    return ( isset($this->logged_in) && $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
+Privileges:HTTPAuthSession.php:    $this->roles = array();
+Privileges:HTTPAuthSession.php:                                array( ':user_no' => $this->user_no) );
+Privileges:HTTPAuthSession.php:    if ( $qry->Exec('BasicAuth') && $qry->rows() > 0 ) {
+Privileges:HTTPAuthSession.php:      while( $role = $qry->Fetch() ) {
+Privileges:HTTPAuthSession.php:        $this->roles[$role->role_name] = true;
+Privileges:HTTPAuthSession.php:    if ( !isset($u->principal_id) ) {
+Privileges:HTTPAuthSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+Privileges:HTTPAuthSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+Privileges:HTTPAuthSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+Privileges:HTTPAuthSession.php:        $u = $qry->Fetch();
+Privileges:HTTPAuthSession.php:      $this->{$k} = $v;
+Privileges:HTTPAuthSession.php:    $this->GetRoles();
+Privileges:HTTPAuthSession.php:    $this->logged_in = true;
+Privileges:HTTPAuthSession.php:    if ( function_exists("awl_set_locale") && isset($this->locale) && $this->locale != "" ) {
+Privileges:HTTPAuthSession.php:      awl_set_locale($this->locale);
+Privileges:interactive-page.php:$home_menu->AddOption(translate('Logout'), $c->base_url.'/index.php?logout', translate('Log out of DAViCal') );
+Privileges:interactive-page.php:$help_menu->AddOption(translate('DAViCal Homepage'),'http://www.davical.org/',translate('The DAViCal Home Page'), false, 6000, true );
+Privileges:interactive-page.php:$help_menu->AddOption(translate('DAViCal Wiki'),'http://wiki.davical.org/'.$wiki_help,translate('Visit the DAViCal Wiki'), false, 7000, true );
+Privileges:interactive-page.php:$help_menu->AddOption(translate('Request Feature'),'http://davical.uservoice.com/',translate('Go to the DAViCal Feature Requests'), false, 8000, true );
+Privileges:interactive-page.php:$help_menu->AddOption(translate('Report Bug'),'http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785',translate('Report a bug in the system'), false, 9000, true );
+Privileges:interactive-page.php:$user_menu->AddOption(translate('View My Details'),$c->base_url.'/admin.php?action=edit&t=principal&id='.$session->principal_id,translate('View my own principal record'));
+Privileges:interactive-page.php:$user_menu->AddOption(translate('List Users'),$c->base_url.'/admin.php?action=browse&t=principal&type=1');
+Privileges:interactive-page.php:$user_menu->AddOption(translate('List Resources'),$c->base_url.'/admin.php?action=browse&t=principal&type=2');
+Privileges:interactive-page.php:$user_menu->AddOption(translate('List Groups'),$c->base_url.'/admin.php?action=browse&t=principal&type=3');
+Privileges:interactive-page.php:if ( $session->AllowedTo('Admin' )) {
+Privileges:interactive-page.php:  $admin_menu->AddOption(translate('Setup'),$c->base_url.'/setup.php',translate('Setup DAViCal') );
+Privileges:interactive-page.php:  $admin_menu->AddOption(translate('Upgrade Database'),$c->base_url.'/upgrade.php',translate('Upgrade DAViCal database schema') );
+Privileges:interactive-page.php:  $admin_menu->AddOption(translate('Import Calendars'),$c->base_url.'/tools.php',translate('Operations on your calendar') );
+Privileges:interactive-page.php:  $user_menu->AddOption(translate('Inactive Principals'),$c->base_url.'/admin.php?action=browse&t=principal&active=f');
+Privileges:interactive-page.php:  $user_menu->AddOption(translate('Create Principal'),$c->base_url.'/admin.php?action=edit&t=principal',translate('Create a new principal (i.e. a new user, resource or group)'));
+Privileges:interactive-page.php:$main_menu->AddSubMenu($home_menu, translate('Home'), $c->base_url.'/index.php', translate('Home Page'), false, 1000);
+Privileges:interactive-page.php:$main_menu->AddSubMenu($user_menu, translate('User Functions'), $c->base_url.'/admin.php?action=browse&t=principal&type=1', translate('Browse all users'), false, 2000);
+Privileges:interactive-page.php:$main_menu->AddSubMenu($admin_menu, translate('Administration'), $c->base_url.'/index.php', translate('Administration'), false, 3000);
+Privileges:interactive-page.php:$main_menu->AddSubMenu($help_menu, translate('Help'), $c->base_url.'/help.php',translate('Help on the current screen'), false, 9000);
+Privileges:iSchedule.php:*  - verifying Domain Key signatures
+Privileges:iSchedule.php:*  - delivering remote scheduling requests to local users inboxes
+Privileges:iSchedule.php:*  - Utility functions which we can use to decide whether this
+Privileges:iSchedule.php:		$this->selector = 'cal';
+Privileges:iSchedule.php:		if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
+Privileges:iSchedule.php:			$this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
+Privileges:iSchedule.php:		$dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
+Privileges:iSchedule.php:			$this->dk = $dkim [ 0 ] [ 'txt' ];
+Privileges:iSchedule.php:			$this->failed = true;
+Privileges:iSchedule.php:		if ( $this->failed == true )
+Privileges:iSchedule.php:		$clean = preg_replace ( '/[\s\t]*([;=])[\s\t]*/', '$1', $this->dk );
+Privileges:iSchedule.php:		$this->parsed = array();
+Privileges:iSchedule.php:				$this->parsed [ $key ] = $value;
+Privileges:iSchedule.php:				$this->parsed_ignored [ $key ] = $value;
+Privileges:iSchedule.php:		$this->failed = true;
+Privileges:iSchedule.php:		if ( isset ( $this->parsed [ 's' ] ) )
+Privileges:iSchedule.php:			if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) )
+Privileges:iSchedule.php:		if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' )
+Privileges:iSchedule.php:		if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) )
+Privileges:iSchedule.php:			if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
+Privileges:iSchedule.php:				$this->failOnError = false;
+Privileges:iSchedule.php:			if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
+Privileges:iSchedule.php:				$this->subdomainsOK = false;
+Privileges:iSchedule.php:		if ( isset ( $this->parsed [ 'g' ] ) )
+Privileges:iSchedule.php:			$this->remote_user_rule = $this->parsed [ 'g' ];
+Privileges:iSchedule.php:		if ( isset ( $this->parsed [ 'p' ] ) )
+Privileges:iSchedule.php:			$data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( preg_replace ( '/_/', '', $this->parsed [ 'p' ] ), 64 )) . "\n-----END PUBLIC KEY-----"; 
+Privileges:iSchedule.php:			$this->remote_public_key = $data;
+Privileges:iSchedule.php:		$this->failed = false;
+Privileges:iSchedule.php:		$this->remote_ssl = false;
+Privileges:iSchedule.php:		$r = dns_get_record ( '_ischedules._tcp.' . $this->domain , DNS_SRV );
+Privileges:iSchedule.php:			$this->remote_ssl = true;
+Privileges:iSchedule.php:			$r = dns_get_record ( '_ischedule._tcp.' . $this->domain , DNS_SRV );
+Privileges:iSchedule.php:		elseif ( $this->try_anyway == true )
+Privileges:iSchedule.php:				$remote_server = $this->domain;
+Privileges:iSchedule.php:		$this->remote_server = $remote_server;
+Privileges:iSchedule.php:		$this->remote_port = $remote_port;
+Privileges:iSchedule.php:		$remote_capabilities = file_get_contents ( 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' . 
+Privileges:iSchedule.php:			$this->remote_server . ':' . $this->remote_port . 
+Privileges:iSchedule.php:			'/.well-known/ischedule?query=capabilities' );
+Privileges:iSchedule.php:		$xmltree = BuildXMLTree( $request->xml_tags, $position);
+Privileges:iSchedule.php:			  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+Privileges:iSchedule.php:		$dk['s'] = $this->selector;
+Privileges:iSchedule.php:		$dk['d'] = $this->domain;
+Privileges:iSchedule.php:		$dk['c'] = 'simple-http';
+Privileges:iSchedule.php:		 //a=rsa-sha1; d=caveman.name; s=cal; c=simple-http; q=dns/txt; h=Originator:Recipient:Host:Content-Type; b
+Privileges:iSchedule.php:	* @param string $sig the value of the DKIM-Signature header
+Privileges:iSchedule.php:		$this->failed = true;
+Privileges:iSchedule.php:		// the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
+Privileges:iSchedule.php:		// but it does define the value, it should be simple-http.  RFC4871 also defines two methods
+Privileges:iSchedule.php:		if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
+Privileges:iSchedule.php:			$this->body_cannon = $matches[2];
+Privileges:iSchedule.php:			$this->body_cannon = $matches[1];
+Privileges:iSchedule.php:		$this->header_cannon = $matches[1];
+Privileges:iSchedule.php:		if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) 
+Privileges:iSchedule.php:		$this->remote_server = $dkim['d'];
+Privileges:iSchedule.php:			$this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
+Privileges:iSchedule.php:		$this->remote_selector = $dkim['s'];
+Privileges:iSchedule.php:		$this->signed_headers = preg_split ( '/:/', $dkim['h'] );
+Privileges:iSchedule.php:		foreach ( $this->signed_headers as $h )
+Privileges:iSchedule.php:			if ( ( ! isset ( $_SERVER['HTTP_' . strtr ( strtoupper ( $h ), '-', '_' ) ] ) &&
+Privileges:iSchedule.php:             ! isset ( $_SERVER[ strtr ( strtoupper ( $h ), '-', '_' ) ] ) ) 
+Privileges:iSchedule.php:				  || strtolower ( $h ) == 'dkim-signature' )
+Privileges:iSchedule.php:			$this->signed_length = $dkim['l'];
+Privileges:iSchedule.php:		$this->failed = false;
+Privileges:iSchedule.php:		$this->DKSig = $dkim;
+Privileges:iSchedule.php:			$this->remote_user = $matches[1];
+Privileges:iSchedule.php:			$this->domain = $matches[2];
+Privileges:iSchedule.php:		$this->failed = true;
+Privileges:iSchedule.php:		foreach ( $this->signed_headers as $h )
+Privileges:iSchedule.php:			if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
+Privileges:iSchedule.php:				$signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+Privileges:iSchedule.php:				$signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+Privileges:iSchedule.php:		$body = $request->raw_post;
+Privileges:iSchedule.php:		if ( ! isset ( $this->signed_length ) )
+Privileges:iSchedule.php:			$this->signed_length = strlen ( $body );
+Privileges:iSchedule.php:			$body = substr ( $body, 0, $this->signed_length );
+Privileges:iSchedule.php:		if ( isset ( $this->remote_user_rule ) )
+Privileges:iSchedule.php:			if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
+Privileges:iSchedule.php:		$body_hash = base64_encode ( hash ( preg_replace ( '/^.*(sha[1256]+).*/','$1', $this->DKSig['a'] ), $body , true ) );
+Privileges:iSchedule.php:		if ( $this->DKSig['bh'] != $body_hash )
+Privileges:iSchedule.php:		$signed .= 'DKIM-Signature: ' . $sig;
+Privileges:iSchedule.php:		$verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key );
+Privileges:iSchedule.php:		$this->failed = false;
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature missing') );
+Privileges:iSchedule.php:    $err = $this->parseDKIM ( $sig );
+Privileges:iSchedule.php:		if ( $err !== true || $this->failed )
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature invalid ' ) . "\n" . $err . "\n" . $sig );
+Privileges:iSchedule.php:		if ( ! $this->getTxt () || $this->failed )
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(DNS ERROR)') );
+Privileges:iSchedule.php:		if ( ! $this->parseTxt () || $this->failed )
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Parse ERROR)') );
+Privileges:iSchedule.php:		if ( ! $this->validateKey () || $this->failed )
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Validation ERROR)') );
+Privileges:iSchedule.php:		if ( ! $this->verifySignature () || $this->failed )
+Privileges:iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(Signature verification ERROR)') . $this->verifySignature() );
+Privileges:iSchedule.php:if ( $d->validateRequest ( ) )
+Privileges:iSchedule.php:	include ( 'caldav-POST.php' );
+Privileges:log_caldav_action.php:  if ( !isset($c->action_log_name) ) {
+Privileges:log_caldav_action.php:  $logfile = fopen( $c->action_log_name, "a+" );
+Privileges:other_translated_strings.php:i18n("Admin <!-- in the sense of 'systems admin' -->");
+Privileges:other_translated_strings.php:i18n("Group <!-- in the sense of a group of people -->");
+Privileges:other_translated_strings.php:i18n("Public <!-- in the sense of being available to all users -->");
+Privileges:other_translated_strings.php:i18n("Resource <!-- a thing which might be booked: a room, a carpark, a projector... -->");
+Privileges:other_translated_strings.php:i18n("No. <!-- short for 'Number' -->");
+Privileges:other_translated_strings.php:i18n("No <!-- not 'Yes' -->");
+Privileges:other_translated_strings.php:i18n("Id <!-- short for 'Identifier' -->");
+Privileges:other_translated_strings.php:i18n("forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option allowing people to log in automatically in future -->");
+Privileges:page-footer.php:  if ( isset($c->scripts) && is_array($c->scripts) ) {
+Privileges:page-footer.php:    foreach ( $c->scripts AS $script ) {
+Privileges:page-footer.php:  if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+Privileges:page-footer.php:    $script_time = microtime(true) - $c->script_start_time;
+Privileges:page-footer.php:                        $_SERVER['REQUEST_METHOD'], 200, $script_time, $c->total_query_time, $_SERVER['REQUEST_URI']);
+Privileges:page-header.php:if ( !isset($c->page_title) ) {
+Privileges:page-header.php:  $c->page_title = translate('DAViCal CalDAV Server');
+Privileges:page-header.php:  if ( $GLOBALS['session']->AllowedTo('Admin') || $GLOBALS['session']->AllowedTo('Support') ) {
+Privileges:page-header.php:  return ' <a class="help" href="'.$c->base_url.'/help.php?h='.$help_topic.'" title="'.translate('Show help on').' &39;'.$help_topic.'&39;" target="_new">['.$display_url.']</a> ';
+Privileges:page-header.php:  header( 'Content-type: text/html; charset="utf-8"' );
+Privileges:page-header.php:  echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+Privileges:page-header.php:<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+Privileges:page-header.php:<title>$c->page_title</title>
+Privileges:page-header.php:  foreach ( $c->stylesheets AS $stylesheet ) {
+Privileges:page-header.php:  if ( isset($c->local_styles) ) {
+Privileges:page-header.php:    foreach ( $c->local_styles AS $stylesheet ) {
+Privileges:page-header.php:  if ( isset($c->print_styles) ) {
+Privileges:page-header.php:    foreach ( $c->print_styles AS $stylesheet ) {
+Privileges:page-header.php:  if ( isset($main_menu) ) echo $main_menu->RenderAsCSS();
+Privileges:page-header.php:  if ( isset($related_menu) && $related_menu->Size() > 0 ) {
+Privileges:page-header.php:    echo $related_menu->Render( true );
+Privileges:page-header.php:  if ( isset($c->messages) && is_array($c->messages) && count($c->messages) > 0 ) {
+Privileges:page-header.php:    foreach( $c->messages AS $i => $msg ) {
+Privileges:PublicSession.php:  * User e-mail
+Privileges:PublicSession.php:  /**#@-*/
+Privileges:PublicSession.php:    $this->user_no = -1;
+Privileges:PublicSession.php:    $this->principal_id = -1;
+Privileges:PublicSession.php:    $this->email = null;
+Privileges:PublicSession.php:    $this->username = 'guest';
+Privileges:PublicSession.php:    $this->fullname = 'Anonymous';
+Privileges:PublicSession.php:    $this->groups = ( isset($c->public_groups) ? $c->public_groups : array() );
+Privileges:PublicSession.php:    $this->roles = array( 'Public' => true );
+Privileges:PublicSession.php:    $this->logged_in = false;
+Privileges:PublicSession.php:    return ( isset($this->roles[$whatever]) && $this->roles[$whatever] );
+Privileges:pubsub.php:		$this->status = "online";
+Privileges:pubsub.php:		$this->setupXmlParser ();
+Privileges:pubsub.php:		if ( ! isset ( $this->jid ) )
+Privileges:pubsub.php:			return $this->connection = false;
+Privileges:pubsub.php:		if ( ! isset ( $this->idle ) )
+Privileges:pubsub.php:			$this->idle = true;
+Privileges:pubsub.php:		if ( ! isset ( $this->resource ) )
+Privileges:pubsub.php:			$this->resource = 'caldav' . getmypid();
+Privileges:pubsub.php:		if ( ! preg_match ( '/^\//', $this->resource ) )
+Privileges:pubsub.php:			$this->resource = '/' . $this->resource;
+Privileges:pubsub.php:		$temp = explode ( '@', $this->jid );
+Privileges:pubsub.php:		$this->username = $temp[0];
+Privileges:pubsub.php:		if ( ! isset ( $this->server ) )
+Privileges:pubsub.php:			$this->server = $temp[1];
+Privileges:pubsub.php:		$r = dns_get_record("_xmpp-client._tcp.". $this->server , DNS_SRV);
+Privileges:pubsub.php:			$this->original_server   = $this->server;
+Privileges:pubsub.php:			$this->server            = $r[0]['target'];
+Privileges:pubsub.php:			$this->original_port     = $this->port;
+Privileges:pubsub.php:			$this->port              = $r[0]['port'];
+Privileges:pubsub.php:		if ( ! isset ( $this->port ) )
+Privileges:pubsub.php:			$this->port = 5222;
+Privileges:pubsub.php:		if ( 'ssl' == $this->tls || ( ! isset ( $this->tls ) && 5223 == $this->port ) )
+Privileges:pubsub.php:			$url = 'ssl://' . $this->server;
+Privileges:pubsub.php:		elseif ( 'tls' == $this->tls || ( ! isset ( $this->tls ) && 5222 == $this->port ) )
+Privileges:pubsub.php:			$url = 'tcp://' . $this->server;
+Privileges:pubsub.php:			$url = 'tcp://' . $this->server;
+Privileges:pubsub.php:		if ( isset ( $this->original_server ) )
+Privileges:pubsub.php:			$this->server = $this->original_server;
+Privileges:pubsub.php:		$this->connection = stream_socket_client ( $url . ':' . $this->port, $errno, $errstring, 10, STREAM_CLIENT_ASYNC_CONNECT );
+Privileges:pubsub.php:		if ( false === $this->connection )
+Privileges:pubsub.php:		$this->initializeQueue ( );
+Privileges:pubsub.php:		socket_set_blocking ( $this->connection, false );
+Privileges:pubsub.php:		if ( $this->debug ) $this->log ( 'handling features' );
+Privileges:pubsub.php:		if ( 'STARTTLS' == $node->firstChild->nodeName )
+Privileges:pubsub.php:			$this->sendQueue[] = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
+Privileges:pubsub.php:		$elements = $this->query ( '*/MECHANISM', $node );
+Privileges:pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching MECHANISM nodes ");
+Privileges:pubsub.php:				$auth_mech[] = $e->nodeValue;
+Privileges:pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . preg_replace('/@.*$/','',$this->jid) . "\x00" . $this->password) . "</auth>";
+Privileges:pubsub.php:			elseif ( in_array ( 'DIGEST-MD5', $auth_mech ) ) // this code and the associated function are UNTESTED
+Privileges:pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
+Privileges:pubsub.php:				$this->recvHandlers['challenge'] = 'digestAuth' ;
+Privileges:pubsub.php:			$this->recvHandlers['success'] = 'handleSuccess' ;
+Privileges:pubsub.php:		$elements = $this->query ( '*/BIND', $node );
+Privileges:pubsub.php:		if ( ! is_null ( $elements ) && $elements->length > 0 )
+Privileges:pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching BIND nodes ");
+Privileges:pubsub.php:			$this->ready = true;
+Privileges:pubsub.php:		stream_set_blocking ( $this->connection, true );
+Privileges:pubsub.php:		stream_socket_enable_crypto ( $this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
+Privileges:pubsub.php:		stream_set_blocking ( $this->connection, false );
+Privileges:pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+Privileges:pubsub.php:		// this code is based solely on the description found @ http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html
+Privileges:pubsub.php:		$contents = $node->nodeValue;
+Privileges:pubsub.php:			if ( $challenge['realm'] == $this->server ) // might fail need to handle a response with multiple realms
+Privileges:pubsub.php:				$X =  md5 ( preg_replace('/@.*$/','',$this->jid) . ':' . $this->server . ':' .  $this->password, true );
+Privileges:pubsub.php:				$HA1 = md5 ( $X . ':' . $challenge['nonce'] . ':' . $cnonce . ':' . $this->jid . $this->resource );
+Privileges:pubsub.php:				$HA2 = md5 ( "AUTHENTICATE:xmpp/" . $this->server );
+Privileges:pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" .
+Privileges:pubsub.php:					base64_encode("username=\"" . preg_replace('/@.*$/','',$this->jid) . "\"," .
+Privileges:pubsub.php:					"realm=\"" . $this->server . "\",nonce=\"" . $challenge['nonce'] . "\",cnonce=\"". $cnonce . "\"," .
+Privileges:pubsub.php:					"nc=00000001,qop=auth,digest-uri=\"xmpp/" . $this->server . "\",response=" . $resp .
+Privileges:pubsub.php:					",charset=utf-8,authzid=\"". $this->jid . $this->resource . "\"" ) . "</response>" // note the PID component to the resource, just incase
+Privileges:pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>" ;
+Privileges:pubsub.php:		$this->loggedIn = true;
+Privileges:pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+Privileges:pubsub.php:		$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>" . preg_replace('/^\//','',$this->resource) . "</resource></bind></iq>";
+Privileges:pubsub.php:		$this->recvHandlers['stream:error'] = 'handleError' ;
+Privileges:pubsub.php:		$this->recvHandlers['iq'] = 'handleIq' ;
+Privileges:pubsub.php:		$this->recvHandlers['message'] = 'handleMessage' ;
+Privileges:pubsub.php:		$this->mesgcount = 1;
+Privileges:pubsub.php:		if ( $this->debug ) $this->log ( "Handle IQ id:" . $node->getAttribute ( 'id' ) . ' type:' . $node->getAttribute ( 'type' ) . "");
+Privileges:pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'result' || $node->getAttribute ( 'type' ) == 'error' )
+Privileges:pubsub.php:			$commandId = $node->getAttribute ( 'id' );
+Privileges:pubsub.php:			$this->command[$commandId] = true;
+Privileges:pubsub.php:			if ( isset ( $this->handleCommand[$commandId] ) )
+Privileges:pubsub.php:				$this->finishedCommands[$commandId] = true;
+Privileges:pubsub.php:				if ( method_exists ( $this, $this->handleCommand[$commandId] ) )
+Privileges:pubsub.php:					call_user_func_array ( array ( $this, $this->handleCommand[$commandId] ),  array ( &$node ) );
+Privileges:pubsub.php:					call_user_func_array ( $this->handleCommand[$commandId], array ( &$node ) );
+Privileges:pubsub.php:		if ( $node->getAttribute ( 'id' ) == $this->mesgcount && $this->mesgcount < 3 )
+Privileges:pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='" . ( $this->mesgcount++ ) . "'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
+Privileges:pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='get' id='" . ( $this->mesgcount++ ) . "'><query xmlns='jabber:iq:roster' /></iq>";
+Privileges:pubsub.php:		if ( $node->getAttribute ( 'id' ) == '2' && $this->command['2'] == true )
+Privileges:pubsub.php:			$this->nextreply = $this->mesgcount++;
+Privileges:pubsub.php:			$this->sendQueue[] = "<presence id='" . $this->nextreply . "' ><status>" . $this->status . '</status></presence>';
+Privileges:pubsub.php:			$this->ready = true;
+Privileges:pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'chat' )
+Privileges:pubsub.php:			$this->command[$node->getAttribute ( 'id' )] = true;
+Privileges:pubsub.php:			$elements = $this->query ( '//*/body', $node );
+Privileges:pubsub.php:			if ( 0 < $elements->length )
+Privileges:pubsub.php:				$temp = $elements->items(0);
+Privileges:pubsub.php:				if ( $this->debug ) $this->log ( "received message " . $temp->nodeValue );
+Privileges:pubsub.php:		$this->log ( 'STREAM ERROR OCCURRED! XMPP closing connection, this is probably a bug' );
+Privileges:pubsub.php:		$this->idle = false;
+Privileges:pubsub.php:		$this->close ();
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$send  = "<iq type='get' from='" . $this->jid . $this->resource . "' to='$to' id='" . $msg . "'>";
+Privileges:pubsub.php:		$this->handleCommand[$msg] = 'discoResult';
+Privileges:pubsub.php:		$this->sendQueue[] = $send;
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		if ( $this->debug ) $this->log ( $node->ownerDocument->saveXML($node) );
+Privileges:pubsub.php:		$id = $node->getAttribute ( 'id' );
+Privileges:pubsub.php:		$identity = $this->query ( '*/IDENTITY', $node );
+Privileges:pubsub.php:		if ( @is_array ( $this->pubsub [ 'create' ] [ $id ] ) && 0 == $identity->length )
+Privileges:pubsub.php:			$this->pubsubCreateNode( $this->pubsub [ 'create' ] [ $id ] [ 0 ],
+Privileges:pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 1 ],
+Privileges:pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 2 ],
+Privileges:pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 3 ] );
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out .= "<message id='" . $msg . "' from='" . $this->jid . $this->resource . "' to='" . $to. "' >";
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:			$to = 'pubsub.' . $this->server;
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$this->pubsub['create'][$this->mesgcount+1] = array ( $to, $type, $name, $configure );
+Privileges:pubsub.php:		$this->disco ( $to, 'info', $node );
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="get" id="' . $msg . '">';
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:			$to = 'pubsub.' . $this->server;
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		$msg = $this->mesgcount++;
+Privileges:pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+Privileges:pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+Privileges:pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+Privileges:pubsub.php:		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="' . $name . '" jid="' . $this->jid . $this->resource . '"/>';
+Privileges:pubsub.php:		$this->sendQueue[] = $out;
+Privileges:pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+Privileges:pubsub.php:		$this->go();
+Privileges:pubsub.php:		if ( $this->debug ) $this->log ( "pubsub RESULT   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+Privileges:pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'error' )
+Privileges:pubsub.php:			$errnode = $this->query ( 'ERROR', $node );
+Privileges:pubsub.php:			if ( $errnode->length > 0 && (  '403' == $errnode->item( 0 )->getAttribute ( 'code' ) || '500' == $errnode->item( 0 )->getAttribute ( 'code' ) ) )
+Privileges:pubsub.php:				if ( 'CREATE' == $node->firstChild->firstChild->tagName )
+Privileges:pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+Privileges:pubsub.php:					if ( $this->debug ) $this->log ( "403 error during CREATE for node '" . $pubnode . "' ");
+Privileges:pubsub.php:					$name =  preg_replace ( '/^.*?\/' . $this->username . '\//','', $pubnode );
+Privileges:pubsub.php:					if ( ! in_array ( 'create', $this->pubsubNext ) )
+Privileges:pubsub.php:								'to' => $node->getAttribute ( 'from' ),
+Privileges:pubsub.php:							array_unshift ( $this->pubsubNext, $v );
+Privileges:pubsub.php:						$this->pubsubDoNext ( );
+Privileges:pubsub.php:			elseif ( $errnode->length > 0 && '404' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+Privileges:pubsub.php:				if ( 'PUBLISH' == $node->firstChild->firstChild->tagName )
+Privileges:pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+Privileges:pubsub.php:					if ( $this->debug ) $this->log ( "404 error during PUBLISH for node '" . $pubnode . "' ");
+Privileges:pubsub.php:					$publish = $this->query ( '//*/PUBLISH', $node );
+Privileges:pubsub.php:					$this->pubsubNext[] = array (
+Privileges:pubsub.php:						'to' => $node->getAttribute ( 'from' ),
+Privileges:pubsub.php:						'name' => preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) ,
+Privileges:pubsub.php:						'contents' => $publish->item( 0 )->firstChild->nodeValue );
+Privileges:pubsub.php:					if ( $this->debug ) $this->log ( "attempting to create node '" . $this->pubsubNext[0]['name'] . "' ");
+Privileges:pubsub.php:					$this->pubsubCreateNode ( $node->getAttribute ( 'from' ) ,'set', preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) );
+Privileges:pubsub.php:			elseif ( $errnode->length > 0 && '409' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+Privileges:pubsub.php:				if ( 'CANCEL' == $errnode->item( 0 )->firstChild->tagName || 'CONFLICT' == $errnode->item( 0 )->firstChild->tagName )
+Privileges:pubsub.php:					$this->pubsubDoNext ( );
+Privileges:pubsub.php:		elseif ( 0 < count ( $this->pubsubNext ) )
+Privileges:pubsub.php:			$this->pubsubDoNext ( );
+Privileges:pubsub.php:		if ( 0 < count ( $this->pubsubNext ) )
+Privileges:pubsub.php:			$pub = array_shift ( $this->pubsubNext );
+Privileges:pubsub.php:				if ( $this->debug ) $this->log ( "attempting to publish to node '" . $pub['name'] . "'  contents '" . $pub['contents'] . "'");
+Privileges:pubsub.php:				$this->pubsubPublish ( $pub[$to], 'set', $pub['name'], $pub['contents'] );
+Privileges:pubsub.php:				if ( $this->debug ) $this->log ( "attempting to create node '" . $pub['name'] . "' ");
+Privileges:pubsub.php:				$this->pubsubCreateNode ( $pub[$to], 'set', $pub['name'] );
+Privileges:pubsub.php:		$this->loggedIn = false;
+Privileges:pubsub.php:		$this->streamTagBegin = '<'."?xml version='1.0'?"."><stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+Privileges:pubsub.php:		$this->streamTagEnd = '</stream:stream>';
+Privileges:pubsub.php:		$this->sendQueue[] = $this->streamTagBegin;
+Privileges:pubsub.php:		$this->recvHandlers['stream:features'] = 'handleFeatures' ;
+Privileges:pubsub.php:		$this->recvHandlers['features'] = 'handleFeatures' ;
+Privileges:pubsub.php:		$this->recvHandlers['proceed'] = 'enableTLS' ;
+Privileges:pubsub.php:		if ( $this->debug ) $this->log ( "SEND: $data");
+Privileges:pubsub.php:		if ( false !== $this->connection )
+Privileges:pubsub.php:			if ( fwrite ( $this->connection, $data, $len) === $len )
+Privileges:pubsub.php:		if ( false !== $this->connection )
+Privileges:pubsub.php:			$data = fgets ( $this->connection, 4096 );
+Privileges:pubsub.php:				while ( 0 != strlen ( $moredata = fgets ( $this->connection, 1024 ) ) && 20 < $count++ )
+Privileges:pubsub.php:				$this->stream .= $data;
+Privileges:pubsub.php:				if ( $this->debug ) $this->log ( "RECV: $data" );
+Privileges:pubsub.php:		$this->recvQueue = implode ( '', $this->sendQueue );
+Privileges:pubsub.php:		$this->moredata = false;
+Privileges:pubsub.php:		while ( false !== $this->connection )
+Privileges:pubsub.php:			if ( 0 < count ( $this->sendQueue ) )
+Privileges:pubsub.php:				while ( $data = array_shift ( $this->sendQueue ) )
+Privileges:pubsub.php:					$this->send ( $data );
+Privileges:pubsub.php:			$data = $this->recv ( );
+Privileges:pubsub.php:			xml_parse ( $this->xmlparser, $data, false );
+Privileges:pubsub.php:			while ( $rnode = array_shift ( $this->recvTags ) )
+Privileges:pubsub.php:				$rname = strtolower ( $rnode->localName );
+Privileges:pubsub.php:				if ( $this->debug ) $this->log ( " processing $rname ");
+Privileges:pubsub.php:				if ( isset ( $this->recvHandlers[$rname] ) ) //&& is_callable ( $this->recvHandlers[$r->name] ) )
+Privileges:pubsub.php:					if ( method_exists ( $this, $this->recvHandlers[$rname] ) )
+Privileges:pubsub.php:						call_user_func_array ( array ( $this, $this->recvHandlers[$rname] ),  array ( &$rnode ) );
+Privileges:pubsub.php:						call_user_func_array ( $this->recvHandlers[$rname], array ( &$rnode ) );
+Privileges:pubsub.php:				if ( $this->idle === true )
+Privileges:pubsub.php:					if ( $this->ready == true && count ( $this->handleCommand ) <= count ( $this->command ) )
+Privileges:pubsub.php:		$this->depth++;
+Privileges:pubsub.php:			$this->processDepth++;
+Privileges:pubsub.php:				if ( strlen ( $matches[1] ) > 0 && ! isset ( $this->namespaces [ $matches[1] ] ) )
+Privileges:pubsub.php:					$this->xquery->registerNamespace ( $matches[1], $v );
+Privileges:pubsub.php:					$this->namespaces [ $matches[1] ] = $v;
+Privileges:pubsub.php:					if ( $this->debug ) $this->log ( " adding namespace $k => $v ");
+Privileges:pubsub.php:			$node = $this->doc->createElementNS ( $namespace, $name );
+Privileges:pubsub.php:			$node = $this->doc->createElement ( $name );
+Privileges:pubsub.php:			$node->setAttribute ( strtolower ( $k ), $v );
+Privileges:pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->appendChild ( $node );
+Privileges:pubsub.php:		$this->depth--;
+Privileges:pubsub.php:		//if ( $this->debug ) $this->log ( "depth: " . $this->depth . " processDepth: " . $this->processDepth . " ");
+Privileges:pubsub.php:		if ( $this->depth == $this->processDepth || 'STREAM:STREAM' == $name || 'STREAM:FEATURES' == $name || 'PROCEED' == $name )
+Privileges:pubsub.php:			if ( $this->debug ) $this->log ( " adding $name to tags to process ");
+Privileges:pubsub.php:			array_push ( $this->recvTags, $this->currentXMLNode ); // replace with tag
+Privileges:pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->parentNode;
+Privileges:pubsub.php:		$this->currentXMLNode->appendChild ( $this->doc->createTextNode ( $text ) );
+Privileges:pubsub.php:		$this->depth = 0;
+Privileges:pubsub.php:		$this->xmlparser = xml_parser_create ( );
+Privileges:pubsub.php:		xml_set_object ( $this->xmlparser, $this );
+Privileges:pubsub.php:		xml_set_element_handler ( $this->xmlparser, 'startElement', 'endElement' );
+Privileges:pubsub.php:		xml_set_character_data_handler ( $this->xmlparser, 'parseData' );
+Privileges:pubsub.php:		$this->doc = new DOMDocument ();
+Privileges:pubsub.php:		$this->xquery = new DOMXpath ( $this->doc );
+Privileges:pubsub.php:		$this->xquery->registerNamespace ( 'stream', 'http://etherx.jabber.org/streams' );
+Privileges:pubsub.php:		$this->currentXMLNode = $this->doc->appendChild ( $this->doc->createElement ( 'start' ) );
+Privileges:pubsub.php:			return $this->xquery->query	( $expression );
+Privileges:pubsub.php:			return $this->xquery->query	( $expression , $node );
+Privileges:pubsub.php:			$this->jid = $jid;
+Privileges:pubsub.php:			$this->password = $password;
+Privileges:pubsub.php:		$this->ready = false;
+Privileges:pubsub.php:		if ( false !== $this->connect () )
+Privileges:pubsub.php:			$this->go ();
+Privileges:pubsub.php:		if ( false !== $this->connection )
+Privileges:pubsub.php:			$this->send ( '</stream:stream>');
+Privileges:pubsub.php:			fclose ( $this->connection );
+Privileges:pubsub.php:			$this->connection = false;
+Privileges:pubsub.php:			$this->sendHandler[$command] = $handler;
+Privileges:pubsub.php:			$this->recvHandler[$command] = $handler;
+Privileges:pubsub.php:	$t->tls =  'none';
+Privileges:pubsub.php:	$t->idle =  false;
+Privileges:pubsub.php:	if ( 1 == $c->dbg["ALL"] || 1 == $c->dbg["push"] )
+Privileges:pubsub.php:		$t->debug = true ;
+Privileges:pubsub.php:		$t->debug = false ;
+Privileges:pubsub.php:	$t->pubsubLayout = 'flat';
+Privileges:pubsub.php:	$qry->Exec('pubsub');
+Privileges:pubsub.php:	$row = $qry->Fetch();
+Privileges:pubsub.php:	$t->open ( $c->notifications_server['jid'], $c->notifications_server['password'] );
+Privileges:pubsub.php:	if ( isset ( $c->notifications_server['debug_jid'] ) )
+Privileges:pubsub.php:		$t->sendMessage ( $c->notifications_server['debug_jid'], "ACTION: $action_type\nUSER: $user_no\nDAV NAME: $dav_name\nPRINCIPAL ID: " . $row->principal_id );
+Privileges:pubsub.php:	$t->pubsubCreate ( '', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>' );
+Privileges:pubsub.php:	$t->pubsubPublish ( '', 'set', '/davical-' . $row->principal_id , '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid );
+Privileges:pubsub.php:	$t->close();
+Privileges:RRule.php:  /**#@-*/
+Privileges:RRule.php:      $this->_text = $input->_text;
+Privileges:RRule.php:      $this->_epoch = $input->_epoch;
+Privileges:RRule.php:      $this->_yy = $input->_yy;
+Privileges:RRule.php:      $this->_mo = $input->_mo;
+Privileges:RRule.php:      $this->_dd = $input->_dd;
+Privileges:RRule.php:      $this->_hh = $input->_hh;
+Privileges:RRule.php:      $this->_mi = $input->_mi;
+Privileges:RRule.php:      $this->_ss = $input->_ss;
+Privileges:RRule.php:      $this->_tz = $input->_tz;
+Privileges:RRule.php:    $this->_wkst = 1; // Monday
+Privileges:RRule.php:      $this->SetLocalDate($input);
+Privileges:RRule.php:      $this->SetGMTDate($input);
+Privileges:RRule.php:      $this->SetLocalDate(strtotime($input));
+Privileges:RRule.php:      $this->SetEpochDate($input);
+Privileges:RRule.php:    $this->_text = $input;
+Privileges:RRule.php:    $this->_PartsFromText();
+Privileges:RRule.php:    $this->_GMTEpochFromParts();
+Privileges:RRule.php:    $this->_text = $input;
+Privileges:RRule.php:    $this->_PartsFromText();
+Privileges:RRule.php:    $this->_EpochFromParts();
+Privileges:RRule.php:    $this->_epoch = intval($input);
+Privileges:RRule.php:    $this->_TextFromEpoch();
+Privileges:RRule.php:    $this->_PartsFromText();
+Privileges:RRule.php:    $this->_text = date('Ymd\THis', $this->_epoch );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+Privileges:RRule.php:    $this->_text = gmdate('Ymd\THis', $this->_epoch );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+Privileges:RRule.php:    $this->_yy = intval(substr($this->_text,0,4));
+Privileges:RRule.php:    $this->_mo = intval(substr($this->_text,4,2));
+Privileges:RRule.php:    $this->_dd = intval(substr($this->_text,6,2));
+Privileges:RRule.php:    $this->_hh = intval(substr($this->_text,9,2));
+Privileges:RRule.php:    $this->_mi = intval(substr($this->_text,11,2));
+Privileges:RRule.php:    $this->_ss = intval(substr($this->_text,13,2));
+Privileges:RRule.php:    $this->_epoch = gmmktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+Privileges:RRule.php:    $this->_epoch = mktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+Privileges:RRule.php:    $this->_wkst = $ical_weekdays[$weekstart];
+Privileges:RRule.php:  function Render( $fmt = 'Y-m-d H:i:s' ) {
+Privileges:RRule.php:    return date( $fmt, $this->_epoch );
+Privileges:RRule.php:    return gmdate( $fmt, $this->_epoch );
+Privileges:RRule.php:  * No of days in a month 1(Jan) - 12(Dec)
+Privileges:RRule.php:    if ( $mo === false ) $mo = $this->_mo;
+Privileges:RRule.php:        if ( $yy === false ) $yy = $this->_yy;
+Privileges:RRule.php:    if ( $dd == $this->_dd ) return; // Shortcut
+Privileges:RRule.php:    $dd = min($dd,$this->DaysInMonth());
+Privileges:RRule.php:    $this->_dd = $dd;
+Privileges:RRule.php:    $this->_EpochFromParts();
+Privileges:RRule.php:    $this->_TextFromEpoch();
+Privileges:RRule.php://    dbg_error_log( "RRule", " Adding %d months to %s", $mo, $this->_text );
+Privileges:RRule.php:    $this->_mo += $mo;
+Privileges:RRule.php:    while ( $this->_mo < 1 ) {
+Privileges:RRule.php:      $this->_mo += 12;
+Privileges:RRule.php:      $this->_yy--;
+Privileges:RRule.php:    while ( $this->_mo > 12 ) {
+Privileges:RRule.php:      $this->_mo -= 12;
+Privileges:RRule.php:      $this->_yy++;
+Privileges:RRule.php:    if ( ($this->_dd > 28 && $this->_mo == 2) || $this->_dd > 30 ) {
+Privileges:RRule.php:      $dim = $this->DaysInMonth();
+Privileges:RRule.php:      if ( $this->_dd > $dim ) {
+Privileges:RRule.php:        $this->_dd = $dim;
+Privileges:RRule.php:    $this->_EpochFromParts();
+Privileges:RRule.php:    $this->_TextFromEpoch();
+Privileges:RRule.php://    dbg_error_log( "RRule", " Added %d months and got %s", $mo, $this->_text );
+Privileges:RRule.php:    $at_start = $this->_text;
+Privileges:RRule.php:    $this->_dd += $dd;
+Privileges:RRule.php:    while ( 1 > $this->_dd ) {
+Privileges:RRule.php:      $this->_mo--;
+Privileges:RRule.php:      if ( $this->_mo < 1 ) {
+Privileges:RRule.php:        $this->_mo += 12;
+Privileges:RRule.php:        $this->_yy--;
+Privileges:RRule.php:      $this->_dd += $this->DaysInMonth();
+Privileges:RRule.php:    while ( ($dim = $this->DaysInMonth($this->_mo)) < $this->_dd ) {
+Privileges:RRule.php:      $this->_dd -= $dim;
+Privileges:RRule.php:      $this->_mo++;
+Privileges:RRule.php:      if ( $this->_mo > 12 ) {
+Privileges:RRule.php:        $this->_mo -= 12;
+Privileges:RRule.php:        $this->_yy++;
+Privileges:RRule.php:    $this->_EpochFromParts();
+Privileges:RRule.php:    $this->_TextFromEpoch();
+Privileges:RRule.php://    dbg_error_log( "RRule", " Added %d days to %s and got %s", $dd, $at_start, $this->_text );
+Privileges:RRule.php:    $sign = ( $sign == "-" ? -1 : 1);
+Privileges:RRule.php://    dbg_error_log( "RRule", " Adding duration to '%s' of sign: %d,  days: %s,  time: %s", $this->_text, $sign, $days, $time );
+Privileges:RRule.php:      $this->AddDays( $days * $sign );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Adding %02d:%02d:%02d * %d to %02d:%02d:%02d", $hh, $mi, $ss, $sign, $this->_hh, $this->_mi, $this->_ss );
+Privileges:RRule.php:    $this->_hh += ($hh * $sign);
+Privileges:RRule.php:    $this->_mi += ($mi * $sign);
+Privileges:RRule.php:    $this->_ss += ($ss * $sign);
+Privileges:RRule.php:    if ( $this->_ss < 0 ) {  $this->_mi -= (intval(abs($this->_ss/60))+1); $this->_ss += ((intval(abs($this->_mi/60))+1) * 60); }
+Privileges:RRule.php:    if ( $this->_ss > 59) {  $this->_mi += (intval(abs($this->_ss/60))+1); $this->_ss -= ((intval(abs($this->_mi/60))+1) * 60); }
+Privileges:RRule.php:    if ( $this->_mi < 0 ) {  $this->_hh -= (intval(abs($this->_mi/60))+1); $this->_mi += ((intval(abs($this->_mi/60))+1) * 60); }
+Privileges:RRule.php:    if ( $this->_mi > 59) {  $this->_hh += (intval(abs($this->_mi/60))+1); $this->_mi -= ((intval(abs($this->_mi/60))+1) * 60); }
+Privileges:RRule.php:    if ( $this->_hh < 0 ) {  $this->AddDays( -1 * (intval(abs($this->_hh/24))+1) );  $this->_hh += ((intval(abs($this->_hh/24))+1)*24);  }
+Privileges:RRule.php:    if ( $this->_hh > 23) {  $this->AddDays( (intval(abs($this->_hh/24))+1) );       $this->_hh -= ((intval(abs($this->_hh/24))+1)*24);  }
+Privileges:RRule.php:    $this->_EpochFromParts();
+Privileges:RRule.php:    $this->_TextFromEpoch();
+Privileges:RRule.php:    if ( $from->_epoch < $this->_epoch ) {
+Privileges:RRule.php:      return( "-". $from->DateDifference( $self ) );
+Privileges:RRule.php://    if ( $from->_yy == $this->_yy && $from->_mo == $this->_mo ) {
+Privileges:RRule.php:      $diff = $from->_epoch - $this->_epoch;
+Privileges:RRule.php:*  ==> This code is not needed then :-)
+Privileges:RRule.php:    $yy = $from->_yy - $this->_yy;
+Privileges:RRule.php:    $mo = $from->_mo - $this->_mo;
+Privileges:RRule.php:    $dd = $from->_dd - $this->_dd;
+Privileges:RRule.php:    $hh = $from->_hh - $this->_hh;
+Privileges:RRule.php:    $mi = $from->_mi - $this->_mi;
+Privileges:RRule.php:    $ss = $from->_ss - $this->_ss;
+Privileges:RRule.php:    if ( $ss < 0 ) {  $mi -= 1;   $ss += 60;  }
+Privileges:RRule.php:    if ( $mi < 0 ) {  $hh -= 1;   $mi += 60;  }
+Privileges:RRule.php:    if ( $hh < 0 ) {  $dd -= 1;   $hh += 24;  }
+Privileges:RRule.php:    if ( $dd < 0 ) {  $mo -= 1;   $dd += $this->DaysInMonth();  } // Which will use $this->_(mo|yy) - seemingly sensible
+Privileges:RRule.php:    if ( $mo < 0 ) {  $yy -= 1;   $mo += 12;  }
+Privileges:RRule.php:  * @param string $monthlist A comma-separated list of months.
+Privileges:RRule.php://    dbg_error_log( "RRule", " Testing BYMONTH %s against month %d", (isset($monthlist) ? $monthlist : "no month list"), $this->_mo );
+Privileges:RRule.php:    return isset($months[$this->_mo]);
+Privileges:RRule.php:    $days_in_month = $this->DaysInMonth();
+Privileges:RRule.php:    $first_dow = (date('w',$this->_epoch) - $this->_dd + 36) % 7;
+Privileges:RRule.php:      $days = $this->MonthDays($first_dow,$days_in_month,$v);
+Privileges:RRule.php:    $days_in_month = $this->DaysInMonth();
+Privileges:RRule.php:    $dow = date('w',$this->_epoch);
+Privileges:RRule.php:      $dd = $this->_dd - $dow + $daynum;
+Privileges:RRule.php:      if ( $daynum < $this->_wkst ) $dd += 7;
+Privileges:RRule.php:      if ( $dd > $this->_dd || !$increasing ) $set[$dd] = $dd;
+Privileges:RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text );
+Privileges:RRule.php:      return ( $this->_text > $lesser->_text );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser );
+Privileges:RRule.php:    return ( $this->_text > $lesser );  // These sorts of dates are designed that way...
+Privileges:RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text );
+Privileges:RRule.php:      return ( $this->_text < $greater->_text );
+Privileges:RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater );
+Privileges:RRule.php:    return ( $this->_text < $greater );  // These sorts of dates are designed that way...
+Privileges:RRule.php:  * Given a MonthDays string like "1MO", "-2WE" return an integer day of the month.
+Privileges:RRule.php:    preg_match( '/([0-9-]*)(MO|TU|WE|TH|FR|SA|SU)/', $dayspec, $matches);
+Privileges:RRule.php:    $first_matching_day = 1 + ($dow - $dow_first);
+Privileges:RRule.php:        $numeric--;
+Privileges:RRule.php:  * Given set position descriptions like '1', '3', '11', '-3' or '-1' and a set,
+Privileges:RRule.php:    $positions = explode( '[^0-9-]', $bysplist );
+Privileges:RRule.php:        $v--;
+Privileges:RRule.php:                ( ";" x-name "=" text )
+Privileges:RRule.php:     enddate    =/ date-time            ;An UTC value
+Privileges:RRule.php:     minus      = "-"
+Privileges:RRule.php:  /**#@-*/
+Privileges:RRule.php:    $this->_first = new iCalDate($start);
+Privileges:RRule.php:    $this->_finished = false;
+Privileges:RRule.php:    $this->_started = false;
+Privileges:RRule.php:    $this->_dates = array();
+Privileges:RRule.php:    $this->_current = -1;
+Privileges:RRule.php:    $this->_rule = preg_replace( '/\s/m', '', $rrule);
+Privileges:RRule.php:    if ( substr($this->_rule, 0, 6) == 'RRULE:' ) {
+Privileges:RRule.php:      $this->_rule = substr($this->_rule, 6);
+Privileges:RRule.php:    dbg_error_log( "RRule", " new RRule: Start: %s, RRULE: %s", $start->Render(), $this->_rule );
+Privileges:RRule.php:    $parts = explode(';',$this->_rule);
+Privileges:RRule.php:    $this->_part = array( 'INTERVAL' => 1 );
+Privileges:RRule.php:      $this->_part[$type] = $value;
+Privileges:RRule.php:    if ( !isset($this->_part['FREQ']) ) {
+Privileges:RRule.php:    if ( isset($this->_part['COUNT']) && isset($this->_part['UNTIL'])  ) {
+Privileges:RRule.php:    if ( isset($this->_part['COUNT']) && intval($this->_part['COUNT']) < 1 ) {
+Privileges:RRule.php:    if ( !preg_match( '/(YEAR|MONTH|WEEK|DAI)LY/', $this->_part['FREQ']) ) {
+Privileges:RRule.php:    if ( $this->_part['FREQ'] == "YEARLY" ) {
+Privileges:RRule.php:      $this->_part['INTERVAL'] *= 12;
+Privileges:RRule.php:      $this->_part['FREQ'] = "MONTHLY";
+Privileges:RRule.php:    $ptr = $this->_current;
+Privileges:RRule.php://    dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() );
+Privileges:RRule.php:      $days_in_month = $test->DaysInMonth();
+Privileges:RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing for day %d based on %s, with %d days in month", $day, $test->Render(), $days_in_month );
+Privileges:RRule.php:        $test->SetMonthDay($days_in_month);
+Privileges:RRule.php:        $test->AddDays(1);
+Privileges:RRule.php:        $day -= $days_in_month;
+Privileges:RRule.php:        $test->SetMonthDay($day);
+Privileges:RRule.php:        $test->SetMonthDay(1);
+Privileges:RRule.php:        $test->AddDays(-1);
+Privileges:RRule.php:        $days_in_month = $test->DaysInMonth();
+Privileges:RRule.php:        $test->SetMonthDay($day);
+Privileges:RRule.php:        $test->SetMonthDay($day);
+Privileges:RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing if %s is within scope", count($relative_days), $test->Render() );
+Privileges:RRule.php:      if ( isset($this->_part['UNTIL']) && $test->GreaterThan($this->_part['UNTIL']) ) {
+Privileges:RRule.php:        $this->_finished = true;
+Privileges:RRule.php:      // if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue;
+Privileges:RRule.php:      if ( !$test->LessThan($this->_first) ) {
+Privileges:RRule.php://        dbg_error_log( "RRule", " WithinScope: Looks like %s is within scope", $test->Render() );
+Privileges:RRule.php:      if ( isset($this->_part['COUNT']) && $ptr >= $this->_part['COUNT'] ) {
+Privileges:RRule.php:        $this->_finished = true;
+Privileges:RRule.php:    if ( $this->_current < 0 ) {
+Privileges:RRule.php:      $next = new iCalDate($this->_first);
+Privileges:RRule.php:      $this->_current++;
+Privileges:RRule.php:      $next = new iCalDate($this->_dates[$this->_current]);
+Privileges:RRule.php:      $this->_current++;
+Privileges:RRule.php:      if ( isset($this->_dates[$this->_current]) ) {
+Privileges:RRule.php://        dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+Privileges:RRule.php:        return $this->_dates[$this->_current];
+Privileges:RRule.php:        if ( isset($this->_part['COUNT']) && $this->_current >= $this->_part['COUNT'] ) // >= since _current is 0-based and COUNT is 1-based
+Privileges:RRule.php:          $this->_finished = true;
+Privileges:RRule.php:    if ( $this->_finished ) {
+Privileges:RRule.php:    if ( isset($this->_part['WKST']) ) $next->SetWeekStart($this->_part['WKST']);
+Privileges:RRule.php:    if ( $this->_part['FREQ'] == "MONTHLY" ) {
+Privileges:RRule.php:        $limit--;
+Privileges:RRule.php:          $limit--;
+Privileges:RRule.php:          if ( $this->_started ) {
+Privileges:RRule.php:            $next->AddMonths($this->_part['INTERVAL']);
+Privileges:RRule.php:            $this->_started = true;
+Privileges:RRule.php:        while ( isset($this->_part['BYMONTH']) && $limit > 0 && ! $next->TestByMonth($this->_part['BYMONTH']) );
+Privileges:RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+Privileges:RRule.php:          $days = $next->GetMonthByDay($this->_part['BYDAY']);
+Privileges:RRule.php:        else if ( isset($this->_part['BYMONTHDAY']) ) {
+Privileges:RRule.php:          $days = $next->GetMonthByMonthDay($this->_part['BYMONTHDAY']);
+Privileges:RRule.php:          $days[$next->_dd] = $next->_dd;
+Privileges:RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+Privileges:RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+Privileges:RRule.php:        $days = $this->WithinScope( $next, $days);
+Privileges:RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+Privileges:RRule.php:    else if ( $this->_part['FREQ'] == "WEEKLY" ) {
+Privileges:RRule.php:        $limit--;
+Privileges:RRule.php:        if ( $this->_started ) {
+Privileges:RRule.php:          $next->AddDays($this->_part['INTERVAL'] * 7);
+Privileges:RRule.php:          $this->_started = true;
+Privileges:RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+Privileges:RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], false );
+Privileges:RRule.php:          $days[$next->_dd] = $next->_dd;
+Privileges:RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+Privileges:RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+Privileges:RRule.php:        $days = $this->WithinScope( $next, $days);
+Privileges:RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+Privileges:RRule.php:    else if ( $this->_part['FREQ'] == "DAILY" ) {
+Privileges:RRule.php:        $limit--;
+Privileges:RRule.php:        if ( $this->_started ) {
+Privileges:RRule.php:          $next->AddDays($this->_part['INTERVAL']);
+Privileges:RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+Privileges:RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], $this->_started );
+Privileges:RRule.php:          $days[$next->_dd] = $next->_dd;
+Privileges:RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+Privileges:RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+Privileges:RRule.php:        $days = $this->WithinScope( $next, $days);
+Privileges:RRule.php:        $this->_started = true;
+Privileges:RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+Privileges:RRule.php:    $ptr = $this->_current;
+Privileges:RRule.php:      $this->_dates[$ptr++] = $v;
+Privileges:RRule.php:    if ( isset($this->_dates[$this->_current]) ) {
+Privileges:RRule.php://      dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+Privileges:RRule.php:      return $this->_dates[$this->_current];
+Privileges:RRule-v2.php:    $this->tz_defined = false;
+Privileges:RRule-v2.php:      $this->tz_defined = $dtz;
+Privileges:RRule-v2.php:        dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
+Privileges:RRule-v2.php:        $this->tz_defined = false;
+Privileges:RRule-v2.php:    if ( $this->tz_defined === false ) return false;
+Privileges:RRule-v2.php:    $tzid = $this->getName();
+Privileges:RRule-v2.php:    return $this->tz_defined;
+Privileges:RRule-v2.php:  // public static $Format = 'Y-m-d H:i:s';
+Privileges:RRule-v2.php:    $this->is_date = false;
+Privileges:RRule-v2.php:    if ( preg_match('{;?VALUE=DATE[:;]}', $date, $matches) ) $this->is_date = true;
+Privileges:RRule-v2.php:    elseif ( preg_match('{:([12]\d{3}) (0[1-9]|1[012]) (0[1-9]|[12]\d|3[01]Z?) $}x', $date, $matches) ) $this->is_date = true;
+Privileges:RRule-v2.php:        $this->tzid = 'UTC';
+Privileges:RRule-v2.php:        $this->tzid = $dtz->tzid();
+Privileges:RRule-v2.php:        $this->tzid = null;
+Privileges:RRule-v2.php:        if ( strlen($matches[1]) == 8 ) $this->is_date = true;
+Privileges:RRule-v2.php:        $this->tzid = 'UTC';
+Privileges:RRule-v2.php:        $this->tzid = null;
+Privileges:RRule-v2.php:      $this->tzid = $dtz->tzid();
+Privileges:RRule-v2.php:      $this->tzid = $dtz->getName();
+Privileges:RRule-v2.php:    return (string)parent::format(self::$Format) . ' ' . parent::getTimeZone()->getName();
+Privileges:RRule-v2.php:    return $this->format('Ymd');
+Privileges:RRule-v2.php:    if ( preg_match('{^(-)?P((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?$}', $interval, $matches) ) {
+Privileges:RRule-v2.php://    printf( "Modify '%s' by: >>%s<<\n", $this->__toString(), $interval );
+Privileges:RRule-v2.php:    return $this->__toString();
+Privileges:RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+Privileges:RRule-v2.php:      $offset = 0 - $dtz->getOffset($gmt);
+Privileges:RRule-v2.php:      $gmt->modify( $offset . ' seconds' );
+Privileges:RRule-v2.php:    if ( $this->is_date ) return $gmt->format('Ymd');
+Privileges:RRule-v2.php:    return $gmt->format('Ymd\THis\Z');
+Privileges:RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+Privileges:RRule-v2.php:      $result = ';TZID='.$this->tzid;
+Privileges:RRule-v2.php:    if ( $this->is_date ) {
+Privileges:RRule-v2.php:      $result .= ';VALUE=DATE:' . $this->format('Ymd');
+Privileges:RRule-v2.php:      $result .= ':' . $this->format('Ymd\THis');
+Privileges:RRule-v2.php:      if ( isset($this->tzid) && $this->tzid == 'UTC' ) {
+Privileges:RRule-v2.php:    return sprintf( 'PT%dM', intval(($end_stamp->epoch() - $this->epoch()) / 60) );
+Privileges:RRule-v2.php:      $this->tzid = $tz->tzid();
+Privileges:RRule-v2.php:    $this->base = ( is_object($basedate) ? $basedate : new RepeatRuleDateTime($basedate) );
+Privileges:RRule-v2.php:    if ( preg_match('{FREQ=([A-Z]+)(;|$)}', $rrule, $m) ) $this->freq = $m[1];
+Privileges:RRule-v2.php:    if ( preg_match('{UNTIL=([0-9TZ]+)(;|$)}', $rrule, $m) ) $this->until = new RepeatRuleDateTime($m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{COUNT=([0-9]+)(;|$)}', $rrule, $m) ) $this->count = $m[1];
+Privileges:RRule-v2.php:    if ( preg_match('{INTERVAL=([0-9]+)(;|$)}', $rrule, $m) ) $this->interval = $m[1];
+Privileges:RRule-v2.php:    if ( preg_match('{WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)}', $rrule, $m) ) $this->wkst = $m[1];
+Privileges:RRule-v2.php:    if ( preg_match('{BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)}', $rrule, $m) )  $this->byday = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYYEARDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byyearday = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYWEEKNO=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byweekno = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYMONTHDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->bymonthday = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)}', $rrule, $m) ) $this->bymonth = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)}', $rrule, $m) ) $this->bysetpos = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYSECOND=([0-9,]+)(;|$)}', $rrule, $m) ) $this->bysecond = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYMINUTE=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byminute = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( preg_match('{BYHOUR=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byhour = explode(',',$m[1]);
+Privileges:RRule-v2.php:    if ( !isset($this->interval) ) $this->interval = 1;
+Privileges:RRule-v2.php:    switch( $this->freq ) {
+Privileges:RRule-v2.php:      case 'SECONDLY': $this->freq_name = 'second'; break;
+Privileges:RRule-v2.php:      case 'MINUTELY': $this->freq_name = 'minute'; break;
+Privileges:RRule-v2.php:      case 'HOURLY':   $this->freq_name = 'hour';   break;
+Privileges:RRule-v2.php:      case 'DAILY':    $this->freq_name = 'day';    break;
+Privileges:RRule-v2.php:      case 'WEEKLY':   $this->freq_name = 'week';   break;
+Privileges:RRule-v2.php:      case 'MONTHLY':  $this->freq_name = 'month';  break;
+Privileges:RRule-v2.php:      case 'YEARLY':   $this->freq_name = 'year';   break;
+Privileges:RRule-v2.php:    $this->frequency_string = sprintf('+%d %s', $this->interval, $this->freq_name );
+Privileges:RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Frequency modify string is: '%s', base is: '%s'\n", $this->frequency_string, $this->base->format('c') );
+Privileges:RRule-v2.php:    $this->Start();
+Privileges:RRule-v2.php:    $this->base->setTimezone(new DateTimeZone($tzstring));
+Privileges:RRule-v2.php:    $this->instances = array();
+Privileges:RRule-v2.php:    $this->GetMoreInstances();
+Privileges:RRule-v2.php:    $this->rewind();
+Privileges:RRule-v2.php:    $this->finished = false;
+Privileges:RRule-v2.php:    $this->position = -1;
+Privileges:RRule-v2.php:    $this->position++;
+Privileges:RRule-v2.php:    return $this->current();
+Privileges:RRule-v2.php:    if ( !$this->valid() ) return null;
+Privileges:RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+Privileges:RRule-v2.php:    if ( !$this->valid() ) return null;
+Privileges:RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Returning date from position %d: %s (%s)\n", $this->position, $this->instances[$this->position]->format('c'), $this->instances[$this->position]->UTC() );
+Privileges:RRule-v2.php:    return $this->instances[$this->position];
+Privileges:RRule-v2.php:    if ( !$this->valid() ) return null;
+Privileges:RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+Privileges:RRule-v2.php:    if ( !isset($this->keys[$this->position]) ) {
+Privileges:RRule-v2.php:      $this->keys[$this->position] = $this->instances[$this->position];
+Privileges:RRule-v2.php:    return $this->keys[$this->position];
+Privileges:RRule-v2.php:    if ( isset($this->instances[$this->position]) || !$this->finished ) return true;
+Privileges:RRule-v2.php:    if ( $this->finished ) return;
+Privileges:RRule-v2.php:    while( !$this->finished && !$got_more && $loops++ < $loop_limit ) {
+Privileges:RRule-v2.php:      if ( !isset($this->current_base) ) {
+Privileges:RRule-v2.php:        $this->current_base = clone($this->base);
+Privileges:RRule-v2.php:        $this->current_base->modify( $this->frequency_string );
+Privileges:RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Getting more instances from: '%s' - %d\n", $this->current_base->format('c'), count($this->instances) );
+Privileges:RRule-v2.php:      $this->current_set = array( clone($this->current_base) );
+Privileges:RRule-v2.php:      foreach( $rrule_expand_limit[$this->freq] AS $bytype => $action ) {
+Privileges:RRule-v2.php:        if ( isset($this->{$bytype}) ) $this->{$action.'_'.$bytype}();
+Privileges:RRule-v2.php:        if ( !isset($this->current_set[0]) ) break;
+Privileges:RRule-v2.php:      sort($this->current_set);
+Privileges:RRule-v2.php:      if ( isset($this->bysetpos) ) $this->limit_bysetpos();
+Privileges:RRule-v2.php:      $position = count($this->instances) - 1;
+Privileges:RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Inserting %d from current_set into position %d\n", count($this->current_set), $position + 1 );
+Privileges:RRule-v2.php:      foreach( $this->current_set AS $k => $instance ) {
+Privileges:RRule-v2.php:        if ( $instance < $this->base ) continue;
+Privileges:RRule-v2.php:        if ( isset($this->until) && $instance > $this->until ) {
+Privileges:RRule-v2.php:          $this->finished = true;
+Privileges:RRule-v2.php:        if ( !isset($this->instances[$position]) || $instance != $this->instances[$position] ) {
+Privileges:RRule-v2.php:          $this->instances[$position] = $instance;
+Privileges:RRule-v2.php:          if ( $GLOBALS['debug_rrule'] ) printf( "Added date %s into position %d in current set\n", $instance->format('c'), $position );
+Privileges:RRule-v2.php:          if ( isset($this->count) && ($position + 1) >= $this->count ) $this->finished = true;
+Privileges:RRule-v2.php:    $date_parts = explode(',',$date->format('Y,m,d,H,i,s'));
+Privileges:RRule-v2.php:    $tz = $date->getTimezone();
+Privileges:RRule-v2.php:      $date->setDate( $date_parts[0], $date_parts[1], $date_parts[2] );
+Privileges:RRule-v2.php:      $date->setTime( $date_parts[3], $date_parts[4], $date_parts[5] );
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+Privileges:RRule-v2.php:        $expanded = $this->date_mask( clone($instance), null, $month, null, null, null, null);
+Privileges:RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYMONTH $month into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:        $this->current_set[] = $expanded;
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->bymonthday AS $k => $monthday ) {
+Privileges:RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
+Privileges:RRule-v2.php:    $dow_of_instance = $day_in_week->format('w'); // 0 == Sunday
+Privileges:RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+Privileges:RRule-v2.php:      $offset = $dow - $dow_of_instance;
+Privileges:RRule-v2.php:      $expanded->modify( sprintf('+%d day', $offset) );
+Privileges:RRule-v2.php:      $this->current_set[] = $expanded;
+Privileges:RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(W) $weekday into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:    $first_of_month = $this->date_mask( clone($day_in_month), null, null, 1, null, null, null);
+Privileges:RRule-v2.php:    $dow_of_first = $first_of_month->format('w'); // 0 == Sunday
+Privileges:RRule-v2.php:    $days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
+Privileges:RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+Privileges:RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+Privileges:RRule-v2.php:        $first_dom = 1 + $dow - $dow_of_first;  if ( $first_dom < 1 ) $first_dom +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+Privileges:RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(M) $weekday in month of %s\n", $instance->format('c') );
+Privileges:RRule-v2.php:          $whichweek--;
+Privileges:RRule-v2.php:          if ( $matches[1] == '-' ) {
+Privileges:RRule-v2.php:            while( $monthday > $days_in_month ) $monthday -= 7;
+Privileges:RRule-v2.php:            $monthday -= (7 * $whichweek);
+Privileges:RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+Privileges:RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:            $this->current_set[] = $expanded;
+Privileges:RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+Privileges:RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:            $this->current_set[] = $expanded;
+Privileges:RRule-v2.php:    $first_of_year = $this->date_mask( clone($day_in_year), null, 1, 1, null, null, null);
+Privileges:RRule-v2.php:    $dow_of_first = $first_of_year->format('w'); // 0 == Sunday
+Privileges:RRule-v2.php:    $days_in_year = 337 + cal_days_in_month(CAL_GREGORIAN, 2, $first_of_year->format('Y'));
+Privileges:RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+Privileges:RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+Privileges:RRule-v2.php:        $first_doy = 1 + $dow - $dow_of_first;  if ( $first_doy < 1 ) $first_doy +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+Privileges:RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(Y) $weekday from date %s\n", $instance->format('c') );
+Privileges:RRule-v2.php:          $whichweek--;
+Privileges:RRule-v2.php:          if ( $matches[1] == '-' ) {
+Privileges:RRule-v2.php:            while( $yearday > $days_in_year ) $yearday -= 7;
+Privileges:RRule-v2.php:            $yearday -= (7 * $whichweek);
+Privileges:RRule-v2.php:            $expanded->modify(sprintf('+%d day', $yearday - 1));
+Privileges:RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:            $this->current_set[] = $expanded;
+Privileges:RRule-v2.php:          $expanded->modify(sprintf('+%d day', $first_doy - 1));
+Privileges:RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+Privileges:RRule-v2.php:            $this->current_set[] = clone($expanded);
+Privileges:RRule-v2.php:            $expanded->modify('+1 week');
+Privileges:RRule-v2.php:    if ( !isset($this->current_set[0]) ) return;
+Privileges:RRule-v2.php:    if ( $this->freq == 'MONTHLY' || $this->freq == 'YEARLY' ) {
+Privileges:RRule-v2.php:      if ( isset($this->bymonthday) || isset($this->byyearday) ) {
+Privileges:RRule-v2.php:        $this->limit_byday();  /** Per RFC5545 3.3.10 from note 1&2 to table */
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      if ( $this->freq == 'MONTHLY' ) {
+Privileges:RRule-v2.php:        $this->expand_byday_in_month($instance);
+Privileges:RRule-v2.php:      else if ( $this->freq == 'WEEKLY' ) {
+Privileges:RRule-v2.php:        $this->expand_byday_in_week($instance);
+Privileges:RRule-v2.php:        if ( isset($this->bymonth) ) {
+Privileges:RRule-v2.php:          $this->expand_byday_in_month($instance);
+Privileges:RRule-v2.php:        else if ( isset($this->byweekno) ) {
+Privileges:RRule-v2.php:          $this->expand_byday_in_week($instance);
+Privileges:RRule-v2.php:          $this->expand_byday_in_year($instance);
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+Privileges:RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, $hour, null, null);
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+Privileges:RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, $minute, null);
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->bymonth AS $k => $second ) {
+Privileges:RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, null, $second);
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:      foreach( $this->{$element_name} AS $k => $element_value ) {
+Privileges:RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' ? %s\n", $instance->format('c'), $instance->format($fmt_char), $element_value, ($instance->format($fmt_char) == $element_value ? 'Yes' : 'No') );
+Privileges:RRule-v2.php:        if ( $instance->format($fmt_char) == $element_value ) $this->current_set[] = $instance;
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+Privileges:RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' (%d) ? %s\n", $instance->format('c'), $instance->format($fmt_char), $weekday, $dow, ($instance->format($fmt_char) == $dow ? 'Yes' : 'No') );
+Privileges:RRule-v2.php:        if ( $instance->format($fmt_char) == $dow ) $this->current_set[] = $instance;
+Privileges:RRule-v2.php:  private function limit_bymonth()    {   $this->limit_generally( 'm', 'bymonth' );     }
+Privileges:RRule-v2.php:  private function limit_byyearday()  {   $this->limit_generally( 'z', 'byyearday' );   }
+Privileges:RRule-v2.php:  private function limit_bymonthday() {   $this->limit_generally( 'd', 'bymonthday' );  }
+Privileges:RRule-v2.php:  private function limit_byhour()     {   $this->limit_generally( 'H', 'byhour' );      }
+Privileges:RRule-v2.php:  private function limit_byminute()   {   $this->limit_generally( 'i', 'byminute' );    }
+Privileges:RRule-v2.php:  private function limit_bysecond()   {   $this->limit_generally( 's', 'bysecond' );    }
+Privileges:RRule-v2.php:    $instances = $this->current_set;
+Privileges:RRule-v2.php:    $this->current_set = array();
+Privileges:RRule-v2.php:    foreach( $this->bysetpos AS $k => $element_value ) {
+Privileges:RRule-v2.php:        $this->current_set[] = $instances[$element_value - 1];
+Privileges:RRule-v2.php:        $this->current_set[] = $instances[$count + $element_value];
+Privileges:RRule-v2.php:  $properties = $component->GetProperties($property);
+Privileges:RRule-v2.php:    $timezone = $p->GetParameterValue('TZID');
+Privileges:RRule-v2.php:    $rdate = $p->Value();
+Privileges:RRule-v2.php:      $expansion[$rdate->UTC()] = $component;
+Privileges:RRule-v2.php:  $recur = $component->GetProperty($property);
+Privileges:RRule-v2.php:  $recur = $recur->Value();
+Privileges:RRule-v2.php:  $this_start = $component->GetProperty('DTSTART');
+Privileges:RRule-v2.php:    $timezone = $this_start->GetParameterValue('TZID');
+Privileges:RRule-v2.php:    $this_start = new RepeatRuleDateTime($this_start->Value(),$timezone);
+Privileges:RRule-v2.php:  while( $date = $rule->next() ) {
+Privileges:RRule-v2.php://    printf( "[%3d] %s\n", $i, $date->UTC() );
+Privileges:RRule-v2.php:    $expansion[$date->UTC()] = $component;
+Privileges:RRule-v2.php:* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
+Privileges:RRule-v2.php:  $components = $vResource->GetComponents();
+Privileges:RRule-v2.php:  if ( !isset($range_start) ) { $range_start = new RepeatRuleDateTime(); $range_start->modify('-6 weeks'); }
+Privileges:RRule-v2.php:  if ( !isset($range_end) )   { $range_end   = clone($range_start);      $range_end->modify('+6 months');  }
+Privileges:RRule-v2.php:    if ( $comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL' ) {
+Privileges:RRule-v2.php:      $dtstart = $comp->GetProperty('DTSTART');
+Privileges:RRule-v2.php:      $tzid = $dtstart->GetParameterValue('TZID');
+Privileges:RRule-v2.php:      $dtstart = new RepeatRuleDateTime( $dtstart->Value(), $tzid );
+Privileges:RRule-v2.php:      $instances[$dtstart->UTC()] = $comp;
+Privileges:RRule-v2.php:    $p = $comp->GetProperty('RECURRENCE-ID');
+Privileges:RRule-v2.php:    if ( isset($p) && $p->Value() != '' ) {
+Privileges:RRule-v2.php:      $range = $p->GetParameterValue('RANGE');
+Privileges:RRule-v2.php:      $recur_tzid = $p->GetParameterValue('TZID');
+Privileges:RRule-v2.php:      $recur_utc = new RepeatRuleDateTime($p->Value(),$recur_tzid);
+Privileges:RRule-v2.php:      $recur_utc = $recur_utc->UTC();
+Privileges:RRule-v2.php:  $start_utc = $range_start->UTC();
+Privileges:RRule-v2.php:  $end_utc = $range_end->UTC();
+Privileges:RRule-v2.php:    $end_type = ($comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND');
+Privileges:RRule-v2.php:    $duration = $comp->GetProperty('DURATION');
+Privileges:RRule-v2.php:    if ( !isset($duration) || $duration->Value() == '' ) {
+Privileges:RRule-v2.php:      $instance_start = $comp->GetProperty('DTSTART');
+Privileges:RRule-v2.php:      $dtsrt = new RepeatRuleDateTime( $instance_start->Value(), $instance_start->GetParameterValue('TZID'));
+Privileges:RRule-v2.php:      $instance_end = $comp->GetProperty($end_type);
+Privileges:RRule-v2.php:      $dtend = new RepeatRuleDateTime( $instance_end->Value(), $instance_end->GetParameterValue('TZID'));
+Privileges:RRule-v2.php:      $duration = $dtstart->RFC5545Duration( $dtend );
+Privileges:RRule-v2.php:      $duration = $duration->Value();
+Privileges:RRule-v2.php:        $latest_start->modify('-'.$duration);
+Privileges:RRule-v2.php:        $early_start = $latest_start->UTC();
+Privileges:RRule-v2.php:    $component->ClearProperties( array('DTSTART'=> true, 'DUE' => true, 'DTEND' => true) );
+Privileges:RRule-v2.php:    $component->AddProperty('DTSTART', $utc );
+Privileges:RRule-v2.php:    $component->AddProperty('DURATION', $duration );
+Privileges:RRule-v2.php:    $vResource->SetComponents($new_components);
+Privileges:RRule-v2.php:    $vResource->SetComponents(array());
+Privileges:test-RRULE.php:header("Content-type: text/plain");
+Privileges:test-RRULE.php:    $this->description = $description;
+Privileges:test-RRULE.php:    $this->dtstart = $start;
+Privileges:test-RRULE.php:    $this->recur = $recur;
+Privileges:test-RRULE.php:    $this->result_description = $result_description;
+Privileges:test-RRULE.php:    $rule = new RRule( new iCalDate($this->dtstart), $this->recur );
+Privileges:test-RRULE.php:      $date = $rule->GetNext();
+Privileges:test-RRULE.php:        $result .= "   " . $date->Render();
+Privileges:test-RRULE.php:    $qry = new AwlQuery($sql, $this->dtstart, $this->recur);
+Privileges:test-RRULE.php:    // printf( "%s\n", $qry->querystring);
+Privileges:test-RRULE.php:    if ( $qry->Exec("test") && $qry->rows > 0 ) {
+Privileges:test-RRULE.php:      while( $row = $qry->Fetch() ) {
+Privileges:test-RRULE.php:        $result .= "   " . $row->event_date;
+Privileges:test-RRULE.php:  , new RRuleTest( "Monthly, on the 1st monday, 2nd wednesday, 3rd friday and last sunday, forever", "20061117T073000", "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" )
+Privileges:test-RRULE.php:  , new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" )
+Privileges:test-RRULE.php:  , new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
+Privileges:test-RRULE.php:  , new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
+Privileges:test-RRULE.php:  echo "$test->dtstart - $test->recur\n";
+Privileges:test-RRULE.php:  echo "$test->description\n";
+Privileges:test-RRULE.php:  $php_result = $test->PHPTest();
+Privileges:test-RRULE.php:  $sql_result = $test->SQLTest();
+Privileges:test-RRULE.php:    echo "PHP & SQL results are identical :-)\n";
+Privileges:test-RRULE.php:    echo "PHP & SQL results differ :-(\n";
+Privileges:vcard.php:  fn TEXT, -- fullname
+Privileges:vcard.php:  n TEXT, -- Name Surname;First names
+Privileges:vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+Privileges:vcard.php:    $properties = $this->GetProperties( $wanted );
+Privileges:vcard.php:      $pname = ':' . strtolower($v->Name());
+Privileges:vcard.php:      if ( !isset($params[$pname]) /** @TODO: or this is one is in the user's language */ ) $params[$pname] = $v->Value();
+Privileges:vcard.php:    $qry->QDo( $sql, $params );
+Privileges:vcard.php:    $this->WriteAddresses($dav_id);
+Privileges:vcard.php:    $this->WritePhones($dav_id);
+Privileges:vcard.php:    $this->WriteEmails($dav_id);
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+Privileges:vcard.php:  property TEXT -- The full text of the property
+Privileges:vcard.php:    $addresses = $this->GetProperties('ADR');
+Privileges:vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+Privileges:vcard.php:    $qry->QDo('DELETE FROM addressbook_address_adr WHERE dav_id = :dav_id', $params );
+Privileges:vcard.php:      $params[':type'] = $adr->GetParameterValue('TYPE');
+Privileges:vcard.php:      $address = explode(';',$adr->Value());
+Privileges:vcard.php:      $params[':property'] = $adr->Render();
+Privileges:vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_adr (dav_id, type, box_no, unit_no, street_address, locality, region, postcode, country, property)
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+Privileges:vcard.php:  property TEXT -- The full text of the property
+Privileges:vcard.php:    $telephones = $this->GetProperties('TEL');
+Privileges:vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+Privileges:vcard.php:    $qry->QDo('DELETE FROM addressbook_address_tel WHERE dav_id = :dav_id', $params );
+Privileges:vcard.php:      $params[':type'] = $tel->GetParameterValue('TYPE');
+Privileges:vcard.php:      $params[':tel'] = $tel->Value();
+Privileges:vcard.php:      $params[':property'] = $tel->Render();
+Privileges:vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_tel (dav_id, type, tel, property) VALUES( :dav_id, :type, :tel, :property)', $params );
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+Privileges:vcard.php:  property TEXT -- The full text of the property
+Privileges:vcard.php:    $emails = $this->GetProperties('EMAIL');
+Privileges:vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+Privileges:vcard.php:    $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params );
+Privileges:vcard.php:      $params[':type'] = $email->GetParameterValue('TYPE');
+Privileges:vcard.php:      $params[':email'] = $email->Value();
+Privileges:vcard.php:      $params[':property'] = $email->Render();
+Privileges:vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params );
+Privileges:vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+PublicSession.php:  * User e-mail
+PublicSession.php:  /**#@-*/
+PublicSession.php:    $this->user_no = -1;
+PublicSession.php:    $this->principal_id = -1;
+PublicSession.php:    $this->email = null;
+PublicSession.php:    $this->username = 'guest';
+PublicSession.php:    $this->fullname = 'Anonymous';
+PublicSession.php:    $this->groups = ( isset($c->public_groups) ? $c->public_groups : array() );
+PublicSession.php:    $this->roles = array( 'Public' => true );
+PublicSession.php:    $this->logged_in = false;
+PublicSession.php:    return ( isset($this->roles[$whatever]) && $this->roles[$whatever] );
+pubsub.php:		$this->status = "online";
+pubsub.php:		$this->setupXmlParser ();
+pubsub.php:		if ( ! isset ( $this->jid ) )
+pubsub.php:			return $this->connection = false;
+pubsub.php:		if ( ! isset ( $this->idle ) )
+pubsub.php:			$this->idle = true;
+pubsub.php:		if ( ! isset ( $this->resource ) )
+pubsub.php:			$this->resource = 'caldav' . getmypid();
+pubsub.php:		if ( ! preg_match ( '/^\//', $this->resource ) )
+pubsub.php:			$this->resource = '/' . $this->resource;
+pubsub.php:		$temp = explode ( '@', $this->jid );
+pubsub.php:		$this->username = $temp[0];
+pubsub.php:		if ( ! isset ( $this->server ) )
+pubsub.php:			$this->server = $temp[1];
+pubsub.php:		$r = dns_get_record("_xmpp-client._tcp.". $this->server , DNS_SRV);
+pubsub.php:			$this->original_server   = $this->server;
+pubsub.php:			$this->server            = $r[0]['target'];
+pubsub.php:			$this->original_port     = $this->port;
+pubsub.php:			$this->port              = $r[0]['port'];
+pubsub.php:		if ( ! isset ( $this->port ) )
+pubsub.php:			$this->port = 5222;
+pubsub.php:		if ( 'ssl' == $this->tls || ( ! isset ( $this->tls ) && 5223 == $this->port ) )
+pubsub.php:			$url = 'ssl://' . $this->server;
+pubsub.php:		elseif ( 'tls' == $this->tls || ( ! isset ( $this->tls ) && 5222 == $this->port ) )
+pubsub.php:			$url = 'tcp://' . $this->server;
+pubsub.php:			$url = 'tcp://' . $this->server;
+pubsub.php:		if ( isset ( $this->original_server ) )
+pubsub.php:			$this->server = $this->original_server;
+pubsub.php:		$this->connection = stream_socket_client ( $url . ':' . $this->port, $errno, $errstring, 10, STREAM_CLIENT_ASYNC_CONNECT );
+pubsub.php:		if ( false === $this->connection )
+pubsub.php:		$this->initializeQueue ( );
+pubsub.php:		socket_set_blocking ( $this->connection, false );
+pubsub.php:		if ( $this->debug ) $this->log ( 'handling features' );
+pubsub.php:		if ( 'STARTTLS' == $node->firstChild->nodeName )
+pubsub.php:			$this->sendQueue[] = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
+pubsub.php:		$elements = $this->query ( '*/MECHANISM', $node );
+pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching MECHANISM nodes ");
+pubsub.php:				$auth_mech[] = $e->nodeValue;
+pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . preg_replace('/@.*$/','',$this->jid) . "\x00" . $this->password) . "</auth>";
+pubsub.php:			elseif ( in_array ( 'DIGEST-MD5', $auth_mech ) ) // this code and the associated function are UNTESTED
+pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
+pubsub.php:				$this->recvHandlers['challenge'] = 'digestAuth' ;
+pubsub.php:			$this->recvHandlers['success'] = 'handleSuccess' ;
+pubsub.php:		$elements = $this->query ( '*/BIND', $node );
+pubsub.php:		if ( ! is_null ( $elements ) && $elements->length > 0 )
+pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching BIND nodes ");
+pubsub.php:			$this->ready = true;
+pubsub.php:		stream_set_blocking ( $this->connection, true );
+pubsub.php:		stream_socket_enable_crypto ( $this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
+pubsub.php:		stream_set_blocking ( $this->connection, false );
+pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		// this code is based solely on the description found @ http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html
+pubsub.php:		$contents = $node->nodeValue;
+pubsub.php:			if ( $challenge['realm'] == $this->server ) // might fail need to handle a response with multiple realms
+pubsub.php:				$X =  md5 ( preg_replace('/@.*$/','',$this->jid) . ':' . $this->server . ':' .  $this->password, true );
+pubsub.php:				$HA1 = md5 ( $X . ':' . $challenge['nonce'] . ':' . $cnonce . ':' . $this->jid . $this->resource );
+pubsub.php:				$HA2 = md5 ( "AUTHENTICATE:xmpp/" . $this->server );
+pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" .
+pubsub.php:					base64_encode("username=\"" . preg_replace('/@.*$/','',$this->jid) . "\"," .
+pubsub.php:					"realm=\"" . $this->server . "\",nonce=\"" . $challenge['nonce'] . "\",cnonce=\"". $cnonce . "\"," .
+pubsub.php:					"nc=00000001,qop=auth,digest-uri=\"xmpp/" . $this->server . "\",response=" . $resp .
+pubsub.php:					",charset=utf-8,authzid=\"". $this->jid . $this->resource . "\"" ) . "</response>" // note the PID component to the resource, just incase
+pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>" ;
+pubsub.php:		$this->loggedIn = true;
+pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>" . preg_replace('/^\//','',$this->resource) . "</resource></bind></iq>";
+pubsub.php:		$this->recvHandlers['stream:error'] = 'handleError' ;
+pubsub.php:		$this->recvHandlers['iq'] = 'handleIq' ;
+pubsub.php:		$this->recvHandlers['message'] = 'handleMessage' ;
+pubsub.php:		$this->mesgcount = 1;
+pubsub.php:		if ( $this->debug ) $this->log ( "Handle IQ id:" . $node->getAttribute ( 'id' ) . ' type:' . $node->getAttribute ( 'type' ) . "");
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'result' || $node->getAttribute ( 'type' ) == 'error' )
+pubsub.php:			$commandId = $node->getAttribute ( 'id' );
+pubsub.php:			$this->command[$commandId] = true;
+pubsub.php:			if ( isset ( $this->handleCommand[$commandId] ) )
+pubsub.php:				$this->finishedCommands[$commandId] = true;
+pubsub.php:				if ( method_exists ( $this, $this->handleCommand[$commandId] ) )
+pubsub.php:					call_user_func_array ( array ( $this, $this->handleCommand[$commandId] ),  array ( &$node ) );
+pubsub.php:					call_user_func_array ( $this->handleCommand[$commandId], array ( &$node ) );
+pubsub.php:		if ( $node->getAttribute ( 'id' ) == $this->mesgcount && $this->mesgcount < 3 )
+pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='" . ( $this->mesgcount++ ) . "'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
+pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='get' id='" . ( $this->mesgcount++ ) . "'><query xmlns='jabber:iq:roster' /></iq>";
+pubsub.php:		if ( $node->getAttribute ( 'id' ) == '2' && $this->command['2'] == true )
+pubsub.php:			$this->nextreply = $this->mesgcount++;
+pubsub.php:			$this->sendQueue[] = "<presence id='" . $this->nextreply . "' ><status>" . $this->status . '</status></presence>';
+pubsub.php:			$this->ready = true;
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'chat' )
+pubsub.php:			$this->command[$node->getAttribute ( 'id' )] = true;
+pubsub.php:			$elements = $this->query ( '//*/body', $node );
+pubsub.php:			if ( 0 < $elements->length )
+pubsub.php:				$temp = $elements->items(0);
+pubsub.php:				if ( $this->debug ) $this->log ( "received message " . $temp->nodeValue );
+pubsub.php:		$this->log ( 'STREAM ERROR OCCURRED! XMPP closing connection, this is probably a bug' );
+pubsub.php:		$this->idle = false;
+pubsub.php:		$this->close ();
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$send  = "<iq type='get' from='" . $this->jid . $this->resource . "' to='$to' id='" . $msg . "'>";
+pubsub.php:		$this->handleCommand[$msg] = 'discoResult';
+pubsub.php:		$this->sendQueue[] = $send;
+pubsub.php:		$this->go();
+pubsub.php:		if ( $this->debug ) $this->log ( $node->ownerDocument->saveXML($node) );
+pubsub.php:		$id = $node->getAttribute ( 'id' );
+pubsub.php:		$identity = $this->query ( '*/IDENTITY', $node );
+pubsub.php:		if ( @is_array ( $this->pubsub [ 'create' ] [ $id ] ) && 0 == $identity->length )
+pubsub.php:			$this->pubsubCreateNode( $this->pubsub [ 'create' ] [ $id ] [ 0 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 1 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 2 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 3 ] );
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out .= "<message id='" . $msg . "' from='" . $this->jid . $this->resource . "' to='" . $to. "' >";
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:			$to = 'pubsub.' . $this->server;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$this->pubsub['create'][$this->mesgcount+1] = array ( $to, $type, $name, $configure );
+pubsub.php:		$this->disco ( $to, 'info', $node );
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="get" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:			$to = 'pubsub.' . $this->server;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="' . $name . '" jid="' . $this->jid . $this->resource . '"/>';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		if ( $this->debug ) $this->log ( "pubsub RESULT   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'error' )
+pubsub.php:			$errnode = $this->query ( 'ERROR', $node );
+pubsub.php:			if ( $errnode->length > 0 && (  '403' == $errnode->item( 0 )->getAttribute ( 'code' ) || '500' == $errnode->item( 0 )->getAttribute ( 'code' ) ) )
+pubsub.php:				if ( 'CREATE' == $node->firstChild->firstChild->tagName )
+pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+pubsub.php:					if ( $this->debug ) $this->log ( "403 error during CREATE for node '" . $pubnode . "' ");
+pubsub.php:					$name =  preg_replace ( '/^.*?\/' . $this->username . '\//','', $pubnode );
+pubsub.php:					if ( ! in_array ( 'create', $this->pubsubNext ) )
+pubsub.php:								'to' => $node->getAttribute ( 'from' ),
+pubsub.php:							array_unshift ( $this->pubsubNext, $v );
+pubsub.php:						$this->pubsubDoNext ( );
+pubsub.php:			elseif ( $errnode->length > 0 && '404' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+pubsub.php:				if ( 'PUBLISH' == $node->firstChild->firstChild->tagName )
+pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+pubsub.php:					if ( $this->debug ) $this->log ( "404 error during PUBLISH for node '" . $pubnode . "' ");
+pubsub.php:					$publish = $this->query ( '//*/PUBLISH', $node );
+pubsub.php:					$this->pubsubNext[] = array (
+pubsub.php:						'to' => $node->getAttribute ( 'from' ),
+pubsub.php:						'name' => preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) ,
+pubsub.php:						'contents' => $publish->item( 0 )->firstChild->nodeValue );
+pubsub.php:					if ( $this->debug ) $this->log ( "attempting to create node '" . $this->pubsubNext[0]['name'] . "' ");
+pubsub.php:					$this->pubsubCreateNode ( $node->getAttribute ( 'from' ) ,'set', preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) );
+pubsub.php:			elseif ( $errnode->length > 0 && '409' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+pubsub.php:				if ( 'CANCEL' == $errnode->item( 0 )->firstChild->tagName || 'CONFLICT' == $errnode->item( 0 )->firstChild->tagName )
+pubsub.php:					$this->pubsubDoNext ( );
+pubsub.php:		elseif ( 0 < count ( $this->pubsubNext ) )
+pubsub.php:			$this->pubsubDoNext ( );
+pubsub.php:		if ( 0 < count ( $this->pubsubNext ) )
+pubsub.php:			$pub = array_shift ( $this->pubsubNext );
+pubsub.php:				if ( $this->debug ) $this->log ( "attempting to publish to node '" . $pub['name'] . "'  contents '" . $pub['contents'] . "'");
+pubsub.php:				$this->pubsubPublish ( $pub[$to], 'set', $pub['name'], $pub['contents'] );
+pubsub.php:				if ( $this->debug ) $this->log ( "attempting to create node '" . $pub['name'] . "' ");
+pubsub.php:				$this->pubsubCreateNode ( $pub[$to], 'set', $pub['name'] );
+pubsub.php:		$this->loggedIn = false;
+pubsub.php:		$this->streamTagBegin = '<'."?xml version='1.0'?"."><stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		$this->streamTagEnd = '</stream:stream>';
+pubsub.php:		$this->sendQueue[] = $this->streamTagBegin;
+pubsub.php:		$this->recvHandlers['stream:features'] = 'handleFeatures' ;
+pubsub.php:		$this->recvHandlers['features'] = 'handleFeatures' ;
+pubsub.php:		$this->recvHandlers['proceed'] = 'enableTLS' ;
+pubsub.php:		if ( $this->debug ) $this->log ( "SEND: $data");
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			if ( fwrite ( $this->connection, $data, $len) === $len )
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			$data = fgets ( $this->connection, 4096 );
+pubsub.php:				while ( 0 != strlen ( $moredata = fgets ( $this->connection, 1024 ) ) && 20 < $count++ )
+pubsub.php:				$this->stream .= $data;
+pubsub.php:				if ( $this->debug ) $this->log ( "RECV: $data" );
+pubsub.php:		$this->recvQueue = implode ( '', $this->sendQueue );
+pubsub.php:		$this->moredata = false;
+pubsub.php:		while ( false !== $this->connection )
+pubsub.php:			if ( 0 < count ( $this->sendQueue ) )
+pubsub.php:				while ( $data = array_shift ( $this->sendQueue ) )
+pubsub.php:					$this->send ( $data );
+pubsub.php:			$data = $this->recv ( );
+pubsub.php:			xml_parse ( $this->xmlparser, $data, false );
+pubsub.php:			while ( $rnode = array_shift ( $this->recvTags ) )
+pubsub.php:				$rname = strtolower ( $rnode->localName );
+pubsub.php:				if ( $this->debug ) $this->log ( " processing $rname ");
+pubsub.php:				if ( isset ( $this->recvHandlers[$rname] ) ) //&& is_callable ( $this->recvHandlers[$r->name] ) )
+pubsub.php:					if ( method_exists ( $this, $this->recvHandlers[$rname] ) )
+pubsub.php:						call_user_func_array ( array ( $this, $this->recvHandlers[$rname] ),  array ( &$rnode ) );
+pubsub.php:						call_user_func_array ( $this->recvHandlers[$rname], array ( &$rnode ) );
+pubsub.php:				if ( $this->idle === true )
+pubsub.php:					if ( $this->ready == true && count ( $this->handleCommand ) <= count ( $this->command ) )
+pubsub.php:		$this->depth++;
+pubsub.php:			$this->processDepth++;
+pubsub.php:				if ( strlen ( $matches[1] ) > 0 && ! isset ( $this->namespaces [ $matches[1] ] ) )
+pubsub.php:					$this->xquery->registerNamespace ( $matches[1], $v );
+pubsub.php:					$this->namespaces [ $matches[1] ] = $v;
+pubsub.php:					if ( $this->debug ) $this->log ( " adding namespace $k => $v ");
+pubsub.php:			$node = $this->doc->createElementNS ( $namespace, $name );
+pubsub.php:			$node = $this->doc->createElement ( $name );
+pubsub.php:			$node->setAttribute ( strtolower ( $k ), $v );
+pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->appendChild ( $node );
+pubsub.php:		$this->depth--;
+pubsub.php:		//if ( $this->debug ) $this->log ( "depth: " . $this->depth . " processDepth: " . $this->processDepth . " ");
+pubsub.php:		if ( $this->depth == $this->processDepth || 'STREAM:STREAM' == $name || 'STREAM:FEATURES' == $name || 'PROCEED' == $name )
+pubsub.php:			if ( $this->debug ) $this->log ( " adding $name to tags to process ");
+pubsub.php:			array_push ( $this->recvTags, $this->currentXMLNode ); // replace with tag
+pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->parentNode;
+pubsub.php:		$this->currentXMLNode->appendChild ( $this->doc->createTextNode ( $text ) );
+pubsub.php:		$this->depth = 0;
+pubsub.php:		$this->xmlparser = xml_parser_create ( );
+pubsub.php:		xml_set_object ( $this->xmlparser, $this );
+pubsub.php:		xml_set_element_handler ( $this->xmlparser, 'startElement', 'endElement' );
+pubsub.php:		xml_set_character_data_handler ( $this->xmlparser, 'parseData' );
+pubsub.php:		$this->doc = new DOMDocument ();
+pubsub.php:		$this->xquery = new DOMXpath ( $this->doc );
+pubsub.php:		$this->xquery->registerNamespace ( 'stream', 'http://etherx.jabber.org/streams' );
+pubsub.php:		$this->currentXMLNode = $this->doc->appendChild ( $this->doc->createElement ( 'start' ) );
+pubsub.php:			return $this->xquery->query	( $expression );
+pubsub.php:			return $this->xquery->query	( $expression , $node );
+pubsub.php:			$this->jid = $jid;
+pubsub.php:			$this->password = $password;
+pubsub.php:		$this->ready = false;
+pubsub.php:		if ( false !== $this->connect () )
+pubsub.php:			$this->go ();
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			$this->send ( '</stream:stream>');
+pubsub.php:			fclose ( $this->connection );
+pubsub.php:			$this->connection = false;
+pubsub.php:			$this->sendHandler[$command] = $handler;
+pubsub.php:			$this->recvHandler[$command] = $handler;
+pubsub.php:	$t->tls =  'none';
+pubsub.php:	$t->idle =  false;
+pubsub.php:	if ( 1 == $c->dbg["ALL"] || 1 == $c->dbg["push"] )
+pubsub.php:		$t->debug = true ;
+pubsub.php:		$t->debug = false ;
+pubsub.php:	$t->pubsubLayout = 'flat';
+pubsub.php:	$qry->Exec('pubsub');
+pubsub.php:	$row = $qry->Fetch();
+pubsub.php:	$t->open ( $c->notifications_server['jid'], $c->notifications_server['password'] );
+pubsub.php:	if ( isset ( $c->notifications_server['debug_jid'] ) )
+pubsub.php:		$t->sendMessage ( $c->notifications_server['debug_jid'], "ACTION: $action_type\nUSER: $user_no\nDAV NAME: $dav_name\nPRINCIPAL ID: " . $row->principal_id );
+pubsub.php:	$t->pubsubCreate ( '', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>' );
+pubsub.php:	$t->pubsubPublish ( '', 'set', '/davical-' . $row->principal_id , '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid );
+pubsub.php:	$t->close();
+RRule.php:  /**#@-*/
+RRule.php:      $this->_text = $input->_text;
+RRule.php:      $this->_epoch = $input->_epoch;
+RRule.php:      $this->_yy = $input->_yy;
+RRule.php:      $this->_mo = $input->_mo;
+RRule.php:      $this->_dd = $input->_dd;
+RRule.php:      $this->_hh = $input->_hh;
+RRule.php:      $this->_mi = $input->_mi;
+RRule.php:      $this->_ss = $input->_ss;
+RRule.php:      $this->_tz = $input->_tz;
+RRule.php:    $this->_wkst = 1; // Monday
+RRule.php:      $this->SetLocalDate($input);
+RRule.php:      $this->SetGMTDate($input);
+RRule.php:      $this->SetLocalDate(strtotime($input));
+RRule.php:      $this->SetEpochDate($input);
+RRule.php:    $this->_text = $input;
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_GMTEpochFromParts();
+RRule.php:    $this->_text = $input;
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_epoch = intval($input);
+RRule.php:    $this->_TextFromEpoch();
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_text = date('Ymd\THis', $this->_epoch );
+RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+RRule.php:    $this->_text = gmdate('Ymd\THis', $this->_epoch );
+RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+RRule.php:    $this->_yy = intval(substr($this->_text,0,4));
+RRule.php:    $this->_mo = intval(substr($this->_text,4,2));
+RRule.php:    $this->_dd = intval(substr($this->_text,6,2));
+RRule.php:    $this->_hh = intval(substr($this->_text,9,2));
+RRule.php:    $this->_mi = intval(substr($this->_text,11,2));
+RRule.php:    $this->_ss = intval(substr($this->_text,13,2));
+RRule.php:    $this->_epoch = gmmktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_epoch = mktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_wkst = $ical_weekdays[$weekstart];
+RRule.php:  function Render( $fmt = 'Y-m-d H:i:s' ) {
+RRule.php:    return date( $fmt, $this->_epoch );
+RRule.php:    return gmdate( $fmt, $this->_epoch );
+RRule.php:  * No of days in a month 1(Jan) - 12(Dec)
+RRule.php:    if ( $mo === false ) $mo = $this->_mo;
+RRule.php:        if ( $yy === false ) $yy = $this->_yy;
+RRule.php:    if ( $dd == $this->_dd ) return; // Shortcut
+RRule.php:    $dd = min($dd,$this->DaysInMonth());
+RRule.php:    $this->_dd = $dd;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Adding %d months to %s", $mo, $this->_text );
+RRule.php:    $this->_mo += $mo;
+RRule.php:    while ( $this->_mo < 1 ) {
+RRule.php:      $this->_mo += 12;
+RRule.php:      $this->_yy--;
+RRule.php:    while ( $this->_mo > 12 ) {
+RRule.php:      $this->_mo -= 12;
+RRule.php:      $this->_yy++;
+RRule.php:    if ( ($this->_dd > 28 && $this->_mo == 2) || $this->_dd > 30 ) {
+RRule.php:      $dim = $this->DaysInMonth();
+RRule.php:      if ( $this->_dd > $dim ) {
+RRule.php:        $this->_dd = $dim;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Added %d months and got %s", $mo, $this->_text );
+RRule.php:    $at_start = $this->_text;
+RRule.php:    $this->_dd += $dd;
+RRule.php:    while ( 1 > $this->_dd ) {
+RRule.php:      $this->_mo--;
+RRule.php:      if ( $this->_mo < 1 ) {
+RRule.php:        $this->_mo += 12;
+RRule.php:        $this->_yy--;
+RRule.php:      $this->_dd += $this->DaysInMonth();
+RRule.php:    while ( ($dim = $this->DaysInMonth($this->_mo)) < $this->_dd ) {
+RRule.php:      $this->_dd -= $dim;
+RRule.php:      $this->_mo++;
+RRule.php:      if ( $this->_mo > 12 ) {
+RRule.php:        $this->_mo -= 12;
+RRule.php:        $this->_yy++;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Added %d days to %s and got %s", $dd, $at_start, $this->_text );
+RRule.php:    $sign = ( $sign == "-" ? -1 : 1);
+RRule.php://    dbg_error_log( "RRule", " Adding duration to '%s' of sign: %d,  days: %s,  time: %s", $this->_text, $sign, $days, $time );
+RRule.php:      $this->AddDays( $days * $sign );
+RRule.php://    dbg_error_log( "RRule", " Adding %02d:%02d:%02d * %d to %02d:%02d:%02d", $hh, $mi, $ss, $sign, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_hh += ($hh * $sign);
+RRule.php:    $this->_mi += ($mi * $sign);
+RRule.php:    $this->_ss += ($ss * $sign);
+RRule.php:    if ( $this->_ss < 0 ) {  $this->_mi -= (intval(abs($this->_ss/60))+1); $this->_ss += ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_ss > 59) {  $this->_mi += (intval(abs($this->_ss/60))+1); $this->_ss -= ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_mi < 0 ) {  $this->_hh -= (intval(abs($this->_mi/60))+1); $this->_mi += ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_mi > 59) {  $this->_hh += (intval(abs($this->_mi/60))+1); $this->_mi -= ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_hh < 0 ) {  $this->AddDays( -1 * (intval(abs($this->_hh/24))+1) );  $this->_hh += ((intval(abs($this->_hh/24))+1)*24);  }
+RRule.php:    if ( $this->_hh > 23) {  $this->AddDays( (intval(abs($this->_hh/24))+1) );       $this->_hh -= ((intval(abs($this->_hh/24))+1)*24);  }
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php:    if ( $from->_epoch < $this->_epoch ) {
+RRule.php:      return( "-". $from->DateDifference( $self ) );
+RRule.php://    if ( $from->_yy == $this->_yy && $from->_mo == $this->_mo ) {
+RRule.php:      $diff = $from->_epoch - $this->_epoch;
+RRule.php:*  ==> This code is not needed then :-)
+RRule.php:    $yy = $from->_yy - $this->_yy;
+RRule.php:    $mo = $from->_mo - $this->_mo;
+RRule.php:    $dd = $from->_dd - $this->_dd;
+RRule.php:    $hh = $from->_hh - $this->_hh;
+RRule.php:    $mi = $from->_mi - $this->_mi;
+RRule.php:    $ss = $from->_ss - $this->_ss;
+RRule.php:    if ( $ss < 0 ) {  $mi -= 1;   $ss += 60;  }
+RRule.php:    if ( $mi < 0 ) {  $hh -= 1;   $mi += 60;  }
+RRule.php:    if ( $hh < 0 ) {  $dd -= 1;   $hh += 24;  }
+RRule.php:    if ( $dd < 0 ) {  $mo -= 1;   $dd += $this->DaysInMonth();  } // Which will use $this->_(mo|yy) - seemingly sensible
+RRule.php:    if ( $mo < 0 ) {  $yy -= 1;   $mo += 12;  }
+RRule.php:  * @param string $monthlist A comma-separated list of months.
+RRule.php://    dbg_error_log( "RRule", " Testing BYMONTH %s against month %d", (isset($monthlist) ? $monthlist : "no month list"), $this->_mo );
+RRule.php:    return isset($months[$this->_mo]);
+RRule.php:    $days_in_month = $this->DaysInMonth();
+RRule.php:    $first_dow = (date('w',$this->_epoch) - $this->_dd + 36) % 7;
+RRule.php:      $days = $this->MonthDays($first_dow,$days_in_month,$v);
+RRule.php:    $days_in_month = $this->DaysInMonth();
+RRule.php:    $dow = date('w',$this->_epoch);
+RRule.php:      $dd = $this->_dd - $dow + $daynum;
+RRule.php:      if ( $daynum < $this->_wkst ) $dd += 7;
+RRule.php:      if ( $dd > $this->_dd || !$increasing ) $set[$dd] = $dd;
+RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text );
+RRule.php:      return ( $this->_text > $lesser->_text );
+RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser );
+RRule.php:    return ( $this->_text > $lesser );  // These sorts of dates are designed that way...
+RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text );
+RRule.php:      return ( $this->_text < $greater->_text );
+RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater );
+RRule.php:    return ( $this->_text < $greater );  // These sorts of dates are designed that way...
+RRule.php:  * Given a MonthDays string like "1MO", "-2WE" return an integer day of the month.
+RRule.php:    preg_match( '/([0-9-]*)(MO|TU|WE|TH|FR|SA|SU)/', $dayspec, $matches);
+RRule.php:    $first_matching_day = 1 + ($dow - $dow_first);
+RRule.php:        $numeric--;
+RRule.php:  * Given set position descriptions like '1', '3', '11', '-3' or '-1' and a set,
+RRule.php:    $positions = explode( '[^0-9-]', $bysplist );
+RRule.php:        $v--;
+RRule.php:                ( ";" x-name "=" text )
+RRule.php:     enddate    =/ date-time            ;An UTC value
+RRule.php:     minus      = "-"
+RRule.php:  /**#@-*/
+RRule.php:    $this->_first = new iCalDate($start);
+RRule.php:    $this->_finished = false;
+RRule.php:    $this->_started = false;
+RRule.php:    $this->_dates = array();
+RRule.php:    $this->_current = -1;
+RRule.php:    $this->_rule = preg_replace( '/\s/m', '', $rrule);
+RRule.php:    if ( substr($this->_rule, 0, 6) == 'RRULE:' ) {
+RRule.php:      $this->_rule = substr($this->_rule, 6);
+RRule.php:    dbg_error_log( "RRule", " new RRule: Start: %s, RRULE: %s", $start->Render(), $this->_rule );
+RRule.php:    $parts = explode(';',$this->_rule);
+RRule.php:    $this->_part = array( 'INTERVAL' => 1 );
+RRule.php:      $this->_part[$type] = $value;
+RRule.php:    if ( !isset($this->_part['FREQ']) ) {
+RRule.php:    if ( isset($this->_part['COUNT']) && isset($this->_part['UNTIL'])  ) {
+RRule.php:    if ( isset($this->_part['COUNT']) && intval($this->_part['COUNT']) < 1 ) {
+RRule.php:    if ( !preg_match( '/(YEAR|MONTH|WEEK|DAI)LY/', $this->_part['FREQ']) ) {
+RRule.php:    if ( $this->_part['FREQ'] == "YEARLY" ) {
+RRule.php:      $this->_part['INTERVAL'] *= 12;
+RRule.php:      $this->_part['FREQ'] = "MONTHLY";
+RRule.php:    $ptr = $this->_current;
+RRule.php://    dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() );
+RRule.php:      $days_in_month = $test->DaysInMonth();
+RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing for day %d based on %s, with %d days in month", $day, $test->Render(), $days_in_month );
+RRule.php:        $test->SetMonthDay($days_in_month);
+RRule.php:        $test->AddDays(1);
+RRule.php:        $day -= $days_in_month;
+RRule.php:        $test->SetMonthDay($day);
+RRule.php:        $test->SetMonthDay(1);
+RRule.php:        $test->AddDays(-1);
+RRule.php:        $days_in_month = $test->DaysInMonth();
+RRule.php:        $test->SetMonthDay($day);
+RRule.php:        $test->SetMonthDay($day);
+RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing if %s is within scope", count($relative_days), $test->Render() );
+RRule.php:      if ( isset($this->_part['UNTIL']) && $test->GreaterThan($this->_part['UNTIL']) ) {
+RRule.php:        $this->_finished = true;
+RRule.php:      // if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue;
+RRule.php:      if ( !$test->LessThan($this->_first) ) {
+RRule.php://        dbg_error_log( "RRule", " WithinScope: Looks like %s is within scope", $test->Render() );
+RRule.php:      if ( isset($this->_part['COUNT']) && $ptr >= $this->_part['COUNT'] ) {
+RRule.php:        $this->_finished = true;
+RRule.php:    if ( $this->_current < 0 ) {
+RRule.php:      $next = new iCalDate($this->_first);
+RRule.php:      $this->_current++;
+RRule.php:      $next = new iCalDate($this->_dates[$this->_current]);
+RRule.php:      $this->_current++;
+RRule.php:      if ( isset($this->_dates[$this->_current]) ) {
+RRule.php://        dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+RRule.php:        return $this->_dates[$this->_current];
+RRule.php:        if ( isset($this->_part['COUNT']) && $this->_current >= $this->_part['COUNT'] ) // >= since _current is 0-based and COUNT is 1-based
+RRule.php:          $this->_finished = true;
+RRule.php:    if ( $this->_finished ) {
+RRule.php:    if ( isset($this->_part['WKST']) ) $next->SetWeekStart($this->_part['WKST']);
+RRule.php:    if ( $this->_part['FREQ'] == "MONTHLY" ) {
+RRule.php:        $limit--;
+RRule.php:          $limit--;
+RRule.php:          if ( $this->_started ) {
+RRule.php:            $next->AddMonths($this->_part['INTERVAL']);
+RRule.php:            $this->_started = true;
+RRule.php:        while ( isset($this->_part['BYMONTH']) && $limit > 0 && ! $next->TestByMonth($this->_part['BYMONTH']) );
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetMonthByDay($this->_part['BYDAY']);
+RRule.php:        else if ( isset($this->_part['BYMONTHDAY']) ) {
+RRule.php:          $days = $next->GetMonthByMonthDay($this->_part['BYMONTHDAY']);
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    else if ( $this->_part['FREQ'] == "WEEKLY" ) {
+RRule.php:        $limit--;
+RRule.php:        if ( $this->_started ) {
+RRule.php:          $next->AddDays($this->_part['INTERVAL'] * 7);
+RRule.php:          $this->_started = true;
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], false );
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    else if ( $this->_part['FREQ'] == "DAILY" ) {
+RRule.php:        $limit--;
+RRule.php:        if ( $this->_started ) {
+RRule.php:          $next->AddDays($this->_part['INTERVAL']);
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], $this->_started );
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:        $this->_started = true;
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    $ptr = $this->_current;
+RRule.php:      $this->_dates[$ptr++] = $v;
+RRule.php:    if ( isset($this->_dates[$this->_current]) ) {
+RRule.php://      dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+RRule.php:      return $this->_dates[$this->_current];
+RRule-v2.php:    $this->tz_defined = false;
+RRule-v2.php:      $this->tz_defined = $dtz;
+RRule-v2.php:        dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
+RRule-v2.php:        $this->tz_defined = false;
+RRule-v2.php:    if ( $this->tz_defined === false ) return false;
+RRule-v2.php:    $tzid = $this->getName();
+RRule-v2.php:    return $this->tz_defined;
+RRule-v2.php:  // public static $Format = 'Y-m-d H:i:s';
+RRule-v2.php:    $this->is_date = false;
+RRule-v2.php:    if ( preg_match('{;?VALUE=DATE[:;]}', $date, $matches) ) $this->is_date = true;
+RRule-v2.php:    elseif ( preg_match('{:([12]\d{3}) (0[1-9]|1[012]) (0[1-9]|[12]\d|3[01]Z?) $}x', $date, $matches) ) $this->is_date = true;
+RRule-v2.php:        $this->tzid = 'UTC';
+RRule-v2.php:        $this->tzid = $dtz->tzid();
+RRule-v2.php:        $this->tzid = null;
+RRule-v2.php:        if ( strlen($matches[1]) == 8 ) $this->is_date = true;
+RRule-v2.php:        $this->tzid = 'UTC';
+RRule-v2.php:        $this->tzid = null;
+RRule-v2.php:      $this->tzid = $dtz->tzid();
+RRule-v2.php:      $this->tzid = $dtz->getName();
+RRule-v2.php:    return (string)parent::format(self::$Format) . ' ' . parent::getTimeZone()->getName();
+RRule-v2.php:    return $this->format('Ymd');
+RRule-v2.php:    if ( preg_match('{^(-)?P((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?$}', $interval, $matches) ) {
+RRule-v2.php://    printf( "Modify '%s' by: >>%s<<\n", $this->__toString(), $interval );
+RRule-v2.php:    return $this->__toString();
+RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+RRule-v2.php:      $offset = 0 - $dtz->getOffset($gmt);
+RRule-v2.php:      $gmt->modify( $offset . ' seconds' );
+RRule-v2.php:    if ( $this->is_date ) return $gmt->format('Ymd');
+RRule-v2.php:    return $gmt->format('Ymd\THis\Z');
+RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+RRule-v2.php:      $result = ';TZID='.$this->tzid;
+RRule-v2.php:    if ( $this->is_date ) {
+RRule-v2.php:      $result .= ';VALUE=DATE:' . $this->format('Ymd');
+RRule-v2.php:      $result .= ':' . $this->format('Ymd\THis');
+RRule-v2.php:      if ( isset($this->tzid) && $this->tzid == 'UTC' ) {
+RRule-v2.php:    return sprintf( 'PT%dM', intval(($end_stamp->epoch() - $this->epoch()) / 60) );
+RRule-v2.php:      $this->tzid = $tz->tzid();
+RRule-v2.php:    $this->base = ( is_object($basedate) ? $basedate : new RepeatRuleDateTime($basedate) );
+RRule-v2.php:    if ( preg_match('{FREQ=([A-Z]+)(;|$)}', $rrule, $m) ) $this->freq = $m[1];
+RRule-v2.php:    if ( preg_match('{UNTIL=([0-9TZ]+)(;|$)}', $rrule, $m) ) $this->until = new RepeatRuleDateTime($m[1]);
+RRule-v2.php:    if ( preg_match('{COUNT=([0-9]+)(;|$)}', $rrule, $m) ) $this->count = $m[1];
+RRule-v2.php:    if ( preg_match('{INTERVAL=([0-9]+)(;|$)}', $rrule, $m) ) $this->interval = $m[1];
+RRule-v2.php:    if ( preg_match('{WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)}', $rrule, $m) ) $this->wkst = $m[1];
+RRule-v2.php:    if ( preg_match('{BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)}', $rrule, $m) )  $this->byday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYYEARDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byyearday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYWEEKNO=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byweekno = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMONTHDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->bymonthday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)}', $rrule, $m) ) $this->bymonth = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)}', $rrule, $m) ) $this->bysetpos = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYSECOND=([0-9,]+)(;|$)}', $rrule, $m) ) $this->bysecond = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMINUTE=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byminute = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYHOUR=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byhour = explode(',',$m[1]);
+RRule-v2.php:    if ( !isset($this->interval) ) $this->interval = 1;
+RRule-v2.php:    switch( $this->freq ) {
+RRule-v2.php:      case 'SECONDLY': $this->freq_name = 'second'; break;
+RRule-v2.php:      case 'MINUTELY': $this->freq_name = 'minute'; break;
+RRule-v2.php:      case 'HOURLY':   $this->freq_name = 'hour';   break;
+RRule-v2.php:      case 'DAILY':    $this->freq_name = 'day';    break;
+RRule-v2.php:      case 'WEEKLY':   $this->freq_name = 'week';   break;
+RRule-v2.php:      case 'MONTHLY':  $this->freq_name = 'month';  break;
+RRule-v2.php:      case 'YEARLY':   $this->freq_name = 'year';   break;
+RRule-v2.php:    $this->frequency_string = sprintf('+%d %s', $this->interval, $this->freq_name );
+RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Frequency modify string is: '%s', base is: '%s'\n", $this->frequency_string, $this->base->format('c') );
+RRule-v2.php:    $this->Start();
+RRule-v2.php:    $this->base->setTimezone(new DateTimeZone($tzstring));
+RRule-v2.php:    $this->instances = array();
+RRule-v2.php:    $this->GetMoreInstances();
+RRule-v2.php:    $this->rewind();
+RRule-v2.php:    $this->finished = false;
+RRule-v2.php:    $this->position = -1;
+RRule-v2.php:    $this->position++;
+RRule-v2.php:    return $this->current();
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Returning date from position %d: %s (%s)\n", $this->position, $this->instances[$this->position]->format('c'), $this->instances[$this->position]->UTC() );
+RRule-v2.php:    return $this->instances[$this->position];
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+RRule-v2.php:    if ( !isset($this->keys[$this->position]) ) {
+RRule-v2.php:      $this->keys[$this->position] = $this->instances[$this->position];
+RRule-v2.php:    return $this->keys[$this->position];
+RRule-v2.php:    if ( isset($this->instances[$this->position]) || !$this->finished ) return true;
+RRule-v2.php:    if ( $this->finished ) return;
+RRule-v2.php:    while( !$this->finished && !$got_more && $loops++ < $loop_limit ) {
+RRule-v2.php:      if ( !isset($this->current_base) ) {
+RRule-v2.php:        $this->current_base = clone($this->base);
+RRule-v2.php:        $this->current_base->modify( $this->frequency_string );
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Getting more instances from: '%s' - %d\n", $this->current_base->format('c'), count($this->instances) );
+RRule-v2.php:      $this->current_set = array( clone($this->current_base) );
+RRule-v2.php:      foreach( $rrule_expand_limit[$this->freq] AS $bytype => $action ) {
+RRule-v2.php:        if ( isset($this->{$bytype}) ) $this->{$action.'_'.$bytype}();
+RRule-v2.php:        if ( !isset($this->current_set[0]) ) break;
+RRule-v2.php:      sort($this->current_set);
+RRule-v2.php:      if ( isset($this->bysetpos) ) $this->limit_bysetpos();
+RRule-v2.php:      $position = count($this->instances) - 1;
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Inserting %d from current_set into position %d\n", count($this->current_set), $position + 1 );
+RRule-v2.php:      foreach( $this->current_set AS $k => $instance ) {
+RRule-v2.php:        if ( $instance < $this->base ) continue;
+RRule-v2.php:        if ( isset($this->until) && $instance > $this->until ) {
+RRule-v2.php:          $this->finished = true;
+RRule-v2.php:        if ( !isset($this->instances[$position]) || $instance != $this->instances[$position] ) {
+RRule-v2.php:          $this->instances[$position] = $instance;
+RRule-v2.php:          if ( $GLOBALS['debug_rrule'] ) printf( "Added date %s into position %d in current set\n", $instance->format('c'), $position );
+RRule-v2.php:          if ( isset($this->count) && ($position + 1) >= $this->count ) $this->finished = true;
+RRule-v2.php:    $date_parts = explode(',',$date->format('Y,m,d,H,i,s'));
+RRule-v2.php:    $tz = $date->getTimezone();
+RRule-v2.php:      $date->setDate( $date_parts[0], $date_parts[1], $date_parts[2] );
+RRule-v2.php:      $date->setTime( $date_parts[3], $date_parts[4], $date_parts[5] );
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $expanded = $this->date_mask( clone($instance), null, $month, null, null, null, null);
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYMONTH $month into date %s\n", $expanded->format('c') );
+RRule-v2.php:        $this->current_set[] = $expanded;
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonthday AS $k => $monthday ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
+RRule-v2.php:    $dow_of_instance = $day_in_week->format('w'); // 0 == Sunday
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      $offset = $dow - $dow_of_instance;
+RRule-v2.php:      $expanded->modify( sprintf('+%d day', $offset) );
+RRule-v2.php:      $this->current_set[] = $expanded;
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(W) $weekday into date %s\n", $expanded->format('c') );
+RRule-v2.php:    $first_of_month = $this->date_mask( clone($day_in_month), null, null, 1, null, null, null);
+RRule-v2.php:    $dow_of_first = $first_of_month->format('w'); // 0 == Sunday
+RRule-v2.php:    $days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+RRule-v2.php:        $first_dom = 1 + $dow - $dow_of_first;  if ( $first_dom < 1 ) $first_dom +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(M) $weekday in month of %s\n", $instance->format('c') );
+RRule-v2.php:          $whichweek--;
+RRule-v2.php:          if ( $matches[1] == '-' ) {
+RRule-v2.php:            while( $monthday > $days_in_month ) $monthday -= 7;
+RRule-v2.php:            $monthday -= (7 * $whichweek);
+RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:    $first_of_year = $this->date_mask( clone($day_in_year), null, 1, 1, null, null, null);
+RRule-v2.php:    $dow_of_first = $first_of_year->format('w'); // 0 == Sunday
+RRule-v2.php:    $days_in_year = 337 + cal_days_in_month(CAL_GREGORIAN, 2, $first_of_year->format('Y'));
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+RRule-v2.php:        $first_doy = 1 + $dow - $dow_of_first;  if ( $first_doy < 1 ) $first_doy +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(Y) $weekday from date %s\n", $instance->format('c') );
+RRule-v2.php:          $whichweek--;
+RRule-v2.php:          if ( $matches[1] == '-' ) {
+RRule-v2.php:            while( $yearday > $days_in_year ) $yearday -= 7;
+RRule-v2.php:            $yearday -= (7 * $whichweek);
+RRule-v2.php:            $expanded->modify(sprintf('+%d day', $yearday - 1));
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:          $expanded->modify(sprintf('+%d day', $first_doy - 1));
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = clone($expanded);
+RRule-v2.php:            $expanded->modify('+1 week');
+RRule-v2.php:    if ( !isset($this->current_set[0]) ) return;
+RRule-v2.php:    if ( $this->freq == 'MONTHLY' || $this->freq == 'YEARLY' ) {
+RRule-v2.php:      if ( isset($this->bymonthday) || isset($this->byyearday) ) {
+RRule-v2.php:        $this->limit_byday();  /** Per RFC5545 3.3.10 from note 1&2 to table */
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      if ( $this->freq == 'MONTHLY' ) {
+RRule-v2.php:        $this->expand_byday_in_month($instance);
+RRule-v2.php:      else if ( $this->freq == 'WEEKLY' ) {
+RRule-v2.php:        $this->expand_byday_in_week($instance);
+RRule-v2.php:        if ( isset($this->bymonth) ) {
+RRule-v2.php:          $this->expand_byday_in_month($instance);
+RRule-v2.php:        else if ( isset($this->byweekno) ) {
+RRule-v2.php:          $this->expand_byday_in_week($instance);
+RRule-v2.php:          $this->expand_byday_in_year($instance);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, $hour, null, null);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, $minute, null);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $second ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, null, $second);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->{$element_name} AS $k => $element_value ) {
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' ? %s\n", $instance->format('c'), $instance->format($fmt_char), $element_value, ($instance->format($fmt_char) == $element_value ? 'Yes' : 'No') );
+RRule-v2.php:        if ( $instance->format($fmt_char) == $element_value ) $this->current_set[] = $instance;
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' (%d) ? %s\n", $instance->format('c'), $instance->format($fmt_char), $weekday, $dow, ($instance->format($fmt_char) == $dow ? 'Yes' : 'No') );
+RRule-v2.php:        if ( $instance->format($fmt_char) == $dow ) $this->current_set[] = $instance;
+RRule-v2.php:  private function limit_bymonth()    {   $this->limit_generally( 'm', 'bymonth' );     }
+RRule-v2.php:  private function limit_byyearday()  {   $this->limit_generally( 'z', 'byyearday' );   }
+RRule-v2.php:  private function limit_bymonthday() {   $this->limit_generally( 'd', 'bymonthday' );  }
+RRule-v2.php:  private function limit_byhour()     {   $this->limit_generally( 'H', 'byhour' );      }
+RRule-v2.php:  private function limit_byminute()   {   $this->limit_generally( 'i', 'byminute' );    }
+RRule-v2.php:  private function limit_bysecond()   {   $this->limit_generally( 's', 'bysecond' );    }
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:    foreach( $this->bysetpos AS $k => $element_value ) {
+RRule-v2.php:        $this->current_set[] = $instances[$element_value - 1];
+RRule-v2.php:        $this->current_set[] = $instances[$count + $element_value];
+RRule-v2.php:  $properties = $component->GetProperties($property);
+RRule-v2.php:    $timezone = $p->GetParameterValue('TZID');
+RRule-v2.php:    $rdate = $p->Value();
+RRule-v2.php:      $expansion[$rdate->UTC()] = $component;
+RRule-v2.php:  $recur = $component->GetProperty($property);
+RRule-v2.php:  $recur = $recur->Value();
+RRule-v2.php:  $this_start = $component->GetProperty('DTSTART');
+RRule-v2.php:    $timezone = $this_start->GetParameterValue('TZID');
+RRule-v2.php:    $this_start = new RepeatRuleDateTime($this_start->Value(),$timezone);
+RRule-v2.php:  while( $date = $rule->next() ) {
+RRule-v2.php://    printf( "[%3d] %s\n", $i, $date->UTC() );
+RRule-v2.php:    $expansion[$date->UTC()] = $component;
+RRule-v2.php:* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
+RRule-v2.php:  $components = $vResource->GetComponents();
+RRule-v2.php:  if ( !isset($range_start) ) { $range_start = new RepeatRuleDateTime(); $range_start->modify('-6 weeks'); }
+RRule-v2.php:  if ( !isset($range_end) )   { $range_end   = clone($range_start);      $range_end->modify('+6 months');  }
+RRule-v2.php:    if ( $comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL' ) {
+RRule-v2.php:      $dtstart = $comp->GetProperty('DTSTART');
+RRule-v2.php:      $tzid = $dtstart->GetParameterValue('TZID');
+RRule-v2.php:      $dtstart = new RepeatRuleDateTime( $dtstart->Value(), $tzid );
+RRule-v2.php:      $instances[$dtstart->UTC()] = $comp;
+RRule-v2.php:    $p = $comp->GetProperty('RECURRENCE-ID');
+RRule-v2.php:    if ( isset($p) && $p->Value() != '' ) {
+RRule-v2.php:      $range = $p->GetParameterValue('RANGE');
+RRule-v2.php:      $recur_tzid = $p->GetParameterValue('TZID');
+RRule-v2.php:      $recur_utc = new RepeatRuleDateTime($p->Value(),$recur_tzid);
+RRule-v2.php:      $recur_utc = $recur_utc->UTC();
+RRule-v2.php:  $start_utc = $range_start->UTC();
+RRule-v2.php:  $end_utc = $range_end->UTC();
+RRule-v2.php:    $end_type = ($comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND');
+RRule-v2.php:    $duration = $comp->GetProperty('DURATION');
+RRule-v2.php:    if ( !isset($duration) || $duration->Value() == '' ) {
+RRule-v2.php:      $instance_start = $comp->GetProperty('DTSTART');
+RRule-v2.php:      $dtsrt = new RepeatRuleDateTime( $instance_start->Value(), $instance_start->GetParameterValue('TZID'));
+RRule-v2.php:      $instance_end = $comp->GetProperty($end_type);
+RRule-v2.php:      $dtend = new RepeatRuleDateTime( $instance_end->Value(), $instance_end->GetParameterValue('TZID'));
+RRule-v2.php:      $duration = $dtstart->RFC5545Duration( $dtend );
+RRule-v2.php:      $duration = $duration->Value();
+RRule-v2.php:        $latest_start->modify('-'.$duration);
+RRule-v2.php:        $early_start = $latest_start->UTC();
+RRule-v2.php:    $component->ClearProperties( array('DTSTART'=> true, 'DUE' => true, 'DTEND' => true) );
+RRule-v2.php:    $component->AddProperty('DTSTART', $utc );
+RRule-v2.php:    $component->AddProperty('DURATION', $duration );
+RRule-v2.php:    $vResource->SetComponents($new_components);
+RRule-v2.php:    $vResource->SetComponents(array());
+test-RRULE.php:header("Content-type: text/plain");
+test-RRULE.php:    $this->description = $description;
+test-RRULE.php:    $this->dtstart = $start;
+test-RRULE.php:    $this->recur = $recur;
+test-RRULE.php:    $this->result_description = $result_description;
+test-RRULE.php:    $rule = new RRule( new iCalDate($this->dtstart), $this->recur );
+test-RRULE.php:      $date = $rule->GetNext();
+test-RRULE.php:        $result .= "   " . $date->Render();
+test-RRULE.php:    $qry = new AwlQuery($sql, $this->dtstart, $this->recur);
+test-RRULE.php:    // printf( "%s\n", $qry->querystring);
+test-RRULE.php:    if ( $qry->Exec("test") && $qry->rows > 0 ) {
+test-RRULE.php:      while( $row = $qry->Fetch() ) {
+test-RRULE.php:        $result .= "   " . $row->event_date;
+test-RRULE.php:  , new RRuleTest( "Monthly, on the 1st monday, 2nd wednesday, 3rd friday and last sunday, forever", "20061117T073000", "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" )
+test-RRULE.php:  , new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" )
+test-RRULE.php:  , new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
+test-RRULE.php:  , new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
+test-RRULE.php:  echo "$test->dtstart - $test->recur\n";
+test-RRULE.php:  echo "$test->description\n";
+test-RRULE.php:  $php_result = $test->PHPTest();
+test-RRULE.php:  $sql_result = $test->SQLTest();
+test-RRULE.php:    echo "PHP & SQL results are identical :-)\n";
+test-RRULE.php:    echo "PHP & SQL results differ :-(\n";
+vcard.php:  fn TEXT, -- fullname
+vcard.php:  n TEXT, -- Name Surname;First names
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $properties = $this->GetProperties( $wanted );
+vcard.php:      $pname = ':' . strtolower($v->Name());
+vcard.php:      if ( !isset($params[$pname]) /** @TODO: or this is one is in the user's language */ ) $params[$pname] = $v->Value();
+vcard.php:    $qry->QDo( $sql, $params );
+vcard.php:    $this->WriteAddresses($dav_id);
+vcard.php:    $this->WritePhones($dav_id);
+vcard.php:    $this->WriteEmails($dav_id);
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $addresses = $this->GetProperties('ADR');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_adr WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $adr->GetParameterValue('TYPE');
+vcard.php:      $address = explode(';',$adr->Value());
+vcard.php:      $params[':property'] = $adr->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_adr (dav_id, type, box_no, unit_no, street_address, locality, region, postcode, country, property)
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $telephones = $this->GetProperties('TEL');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_tel WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $tel->GetParameterValue('TYPE');
+vcard.php:      $params[':tel'] = $tel->Value();
+vcard.php:      $params[':property'] = $tel->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_tel (dav_id, type, tel, property) VALUES( :dav_id, :type, :tel, :property)', $params );
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $emails = $this->GetProperties('EMAIL');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $email->GetParameterValue('TYPE');
+vcard.php:      $params[':email'] = $email->Value();
+vcard.php:      $params[':property'] = $email->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params );
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
Index: contrib/davical/inc/caldav-REPORT-multiget.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-multiget.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-multiget.php	(revision 3733)
@@ -0,0 +1,189 @@
+<?php
+/**
+* Handle the calendar-multiget REPORT request.
+*/
+include_once("drivers_ldap.php");
+$responses = array();
+
+$need_expansion = false;
+function check_for_expansion( $calendar_data_node ) {
+  global $need_expansion, $expand_range_start, $expand_range_end;
+
+  if ( !class_exists('DateTime') ) return; /** We don't support expansion on PHP5.1 */
+
+  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+  if ( isset($expansion[0]) ) {
+    $need_expansion = true;
+    $expand_range_start = $expansion[0]->GetAttribute('start');
+    $expand_range_end = $expansion[0]->GetAttribute('end');
+    if ( isset($expand_range_start) ) $expand_range_start = new RepeatRuleDateTime($expand_range_start);
+    if ( isset($expand_range_end) )   $expand_range_end   = new RepeatRuleDateTime($expand_range_end);
+  }
+}
+
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$proptype = $qry_content[0]->GetTag();
+$properties = array();
+switch( $proptype ) {
+  case 'DAV::prop':
+    $qry_props = $xmltree->GetPath('/*/'.$proptype.'/*');
+    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+      $properties[$propertyname] = 1;
+      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+    }
+    break;
+
+  case 'DAV::allprop':
+    $properties['allprop'] = 1;
+    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+      }
+    }
+    break;
+
+ default:
+    $propertyname = preg_replace( '/^.*:/', '', $proptype );
+    $properties[$propertyname] = 1;
+}
+$collection = new DAVResource($request->path);
+$bound_from = $collection->bound_from();
+
+/**
+ * Build the href list for the IN ( href, href, href, ... ) clause.
+ */
+$mg_hrefs = $xmltree->GetPath('/*/DAV::href');
+$href_in = '';
+$params = array();
+foreach( $mg_hrefs AS $k => $v ) {
+  /**
+   * We need this to work if they specified a relative *or* a full path, so we strip off
+   * anything up to the matching request->path (which will include any http...) and then
+   * put the $bound_from prefix back on.
+   */
+  $href = $bound_from . preg_replace( "{^.*\E$request->path\Q}", '', rawurldecode($v->GetContent()) );
+  dbg_error_log("REPORT", "Reporting on href '%s'", $href );
+  $href_in .= ($href_in == '' ? '' : ', ');
+  $href_in .= ':href'.$k;
+  $params[':href'.$k] = $href;
+}
+
+//$where = " WHERE caldav_data.collection_id = " . $collection->resource_id();
+//$where .= "AND caldav_data.dav_name IN ( $href_in ) ";
+$path = preg_replace("/\/|addressbook/","",$request->path);
+//$filtro = "uid=".$path;
+//$atributos = array("uidNumber");
+//$uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+//if ($uidnumber == false) {
+//                 dbg_error_log( "REPORT", "Responding with report error:  usuario nÃ£o encontrado no diretorio");
+//                 continue;
+//}
+//$nome = $uidnumber['uidNumber'];
+$nome = $collection->GetProperty('user_no');
+if ( $mode == 'caldav' ) {
+  if ( $collection->Privileges() != privilege_to_bits('DAV::all') ) {
+    $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
+  }
+
+  if ( isset($c->hide_TODO) && $c->hide_TODO && ! $collection->Privileges() == privilege_to_bits('all') ) {
+    $where .= "AND caldav_data.caldav_type NOT IN ('VTODO') ";
+  }
+
+/******************************
+***************** Melhorar as consultas
+*/
+    $sqlp = "SELECT cal_id FROM phpgw_cal_user WHERE  cal_type = 'u' AND cal_status = 'A' AND cal_login = :nome";
+    $qryp = new AwlQuery( $sqlp, array( ':nome' => $nome) );
+    if ( $qryp->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qryp->rows() > 0 ) {
+       while( $part = $qryp->Fetch() ) {
+           $sql = "SELECT * FROM phpgw_cal WHERE cal_id = $part->cal_id AND cal_type = 'E'";
+           $qry = new AwlQuery( $sql);
+           if ( $qry->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+              while( $calendar_object = $qry->Fetch() ) {
+                if ( $bound_from != $collection->dav_name() ) {
+                      $calendar_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $calendar_object->dav_name);
+                    }
+                if ( $need_expansion ) {
+                   $vResource = new vComponent($calendar_object->caldav_data);
+                   $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                   $calendar_object->caldav_data = $expanded->Render();
+                     }
+                  $responses[] = calendar_to_xml( $properties, $calendar_object );   
+              }
+           }
+            $sql = "SELECT * FROM phpgw_cal INNER JOIN phpgw_cal_repeats USING(cal_id) WHERE cal_id = $part->cal_id";
+            $qry = new AwlQuery( $sql);
+            if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+                  while( $calendar_object = $qry->Fetch() ) {
+                    if ( $bound_from != $collection->dav_name() ) {
+                      $calendar_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $calendar_object->dav_name);
+                    }
+                    if ( $need_expansion ) {
+                       $vResource = new vComponent($calendar_object->caldav_data);
+                        $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                       $calendar_object->caldav_data = $expanded->Render();
+                     }
+                    $responses[] = calendar_to_xml( $properties, $calendar_object );
+               }
+           }
+      }
+   }
+
+
+
+    
+  
+  //$sql = 'SELECT caldav_data.*,calendar_item.* FROM caldav_data
+  //                LEFT JOIN calendar_item USING(dav_id, user_no, dav_name, collection_id)
+  //                LEFT JOIN collection USING(collection_id)';
+}
+else if ( $mode == 'carddav' ) {
+
+        $sql = "SELECT * FROM phpgw_cc_contact WHERE  id_owner= :nome";
+           $qry = new AwlQuery( $sql,array(':nome' => $nome));
+           if ( $qry->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+              while( $contacts_object = $qry->Fetch() ) {
+                if ( $bound_from != $collection->dav_name() ) {
+                      $contacts_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $contacts_object->dav_name);
+                    }
+                if ( $need_expansion ) {
+                   $vResource = new vComponent($contacts_object->caldav_data);
+                   $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                   $contacts_object->caldav_data = $expanded->Render();
+                     }
+
+                  $responses[] = contacts_to_xml( $properties, $contacts_object);
+
+              }
+           }
+   
+
+  //$sql = 'SELECT caldav_data.*, addressbook_resource.* FROM caldav_data
+  //                LEFT JOIN addressbook_resource USING(dav_id)
+  //                LEFT JOIN collection USING(collection_id)';
+}
+
+//if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $where .= " ORDER BY caldav_data.dav_id";
+//$qry = new AwlQuery( $sql . $where, $params );
+//if ( $qry->Exec('REPORT',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+//  while( $dav_object = $qry->Fetch() ) {
+//    if ( $bound_from != $collection->dav_name() ) {
+//      $dav_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $dav_object->dav_name);
+//    }
+//    if ( $need_expansion ) {
+//      $vResource = new vComponent($dav_object->caldav_data);
+//      $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+//      $dav_object->caldav_data = $expanded->Render();
+//    }
+//    $responses[] = component_to_xml( $properties, $dav_object );
+//  }
+//}
+
+$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/caldav-PUT-vcard.php
===================================================================
--- contrib/davical/inc/caldav-PUT-vcard.php	(revision 3733)
+++ contrib/davical/inc/caldav-PUT-vcard.php	(revision 3733)
@@ -0,0 +1,113 @@
+<?php
+/**
+* CalDAV Server - handle PUT method on VCARD content-types
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("PUT", "method handler vcard");
+
+
+require_once('DAVResource.php');
+require_once('vComponent.php');
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+  $fh = fopen('/tmp/PUT.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+$lock_opener = $request->FailIfLocked();
+
+$dest = new DAVResource($request->path);
+
+$container = $dest->FetchParentContainer();
+if ( ! $dest->Exists() ) {
+  if ( $container->IsPrincipal() ) {
+    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+  }
+  if ( ! $container->Exists() ) {
+    $request->DoResponse( 409, translate('Destination collection does not exist') );
+  }
+  $container->NeedPrivilege('DAV::bind');
+}
+else {
+ // if ( $dest->IsCollection() ) {
+ //   if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+ //     $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+ //   }
+ //   $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+ // }
+  $dest->NeedPrivilege('DAV::write-content');
+}
+
+if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+  $request->DoResponse(412);
+}
+
+//if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+//  $request->DoResponse(412);
+//}
+
+//$collection_id = $container->GetProperty('collection_id');
+
+//$qry = new AwlQuery();
+//$qry->Begin();
+
+$etag = md5($request->raw_post);
+//$params = array(
+//    ':user_no' => $dest->GetProperty('user_no'),
+//    ':dav_name' => $dest->bound_from(),
+//    ':etag' => $etag,
+//    ':dav_data' => $request->raw_post,
+//    ':session_user' => $session->user_no
+//);
+//if ( $dest->Exists() ) {
+//  $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
+//          modified=current_timestamp, user_no=:user_no, caldav_type=\'VCARD\' WHERE dav_name=:dav_name';
+//  $response_code = 200;
+//  $qry->QDo( $sql, $params );
+//
+//  $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']) );
+//}
+//else {
+//  $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id )
+//          VALUES( :user_no, :dav_name, :etag, :dav_data, \'VCARD\', :session_user, current_timestamp, current_timestamp, :collection_id )';
+//  $params[':collection_id'] = $collection_id;
+//  $response_code = 201;
+//  $qry->QDo( $sql, $params );
+//
+//  $qry->QDo("SELECT currval('dav_id_seq') AS dav_id" );
+//}
+//$row = $qry->Fetch();
+
+require_once('vcard.php');
+
+$vcard = new vCard( $request->raw_post );
+$response_code=$vcard->Write_expresso( $container->GetProperty('user_no'),$dest->Exists() );
+
+//$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+
+
+//if ( !$qry->QDo('COMMIT') ) rollback(500);
+
+header('ETag: "'. $etag . '"' );
+//if ( $response_code == 200 ) $response_code = 204;
+$request->DoResponse( $response_code );
+
+
+
+
+
+
+
+
+
+
+
+
Index: contrib/davical/inc/caldav-MOVE.php
===================================================================
--- contrib/davical/inc/caldav-MOVE.php	(revision 3733)
+++ contrib/davical/inc/caldav-MOVE.php	(revision 3733)
@@ -0,0 +1,173 @@
+<?php
+/**
+* CalDAV Server - handle MOVE method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+dbg_error_log("MOVE", "method handler");
+
+require_once('DAVResource.php');
+
+$request->NeedPrivilege('DAV::unbind');
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['move']) && $c->dbg['move'])) ) {
+  $fh = fopen('/tmp/MOVE.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+$lock_opener = $request->FailIfLocked();
+
+$dest = new DAVResource($request->destination);
+
+if ( $dest->dav_name() == '/' || $dest->IsPrincipal() ) {
+  $dest->NeedPrivilege('DAV::bind');
+}
+
+if ( ! $dest->ContainerExists() ) {
+  $request->DoResponse( 409, translate('Destination collection does not exist') );
+}
+
+if ( ! $request->overwrite && $dest->Exists() ) {
+  $request->DoResponse( 412, translate('Not overwriting existing destination resource') );
+}
+
+if ( isset($request->etag_none_match) && $request->etag_none_match != '*' ) {
+  $request->DoResponse( 412 );  /** request to move, but only if there is no source? WTF! */
+}
+
+$src  = new DAVResource($request->path);
+if ( ! $src->Exists() ) {
+  $request->DoResponse( 412, translate('Source resource does not exist.') );
+}
+
+if ( $src->IsCollection() ) {
+  switch( $dest->ContainerType() ) {
+    case 'calendar':
+    case 'addressbook':
+    case 'schedule-inbox':
+    case 'schedule-outbox':
+      $request->DoResponse( 412, translate('Special collections may not contain a calendar or other special collection.') );
+  };
+}
+else {
+  if ( (isset($request->etag_if_match) && $request->etag_if_match != '' )
+        || ( isset($request->etag_none_match) && $request->etag_none_match != '') ) {
+
+    /**
+    * RFC2068, 14.25:
+    * If none of the entity tags match, or if "*" is given and no current
+    * entity exists, the server MUST NOT perform the requested method, and
+    * MUST return a 412 (Precondition Failed) response.
+    *
+    * RFC2068, 14.26:
+    * If any of the entity tags match the entity tag of the entity that
+    * would have been returned in the response to a similar GET request
+    * (without the If-None-Match header) on that resource, or if "*" is
+    * given and any current entity exists for that resource, then the
+    * server MUST NOT perform the requested method.
+    */
+    $error = '';
+    if ( isset($request->etag_if_match) && $request->etag_if_match != $src->unique_tag() ) {
+      $error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
+    }
+    else if ( isset($request->etag_none_match) && $request->etag_none_match != '' && $request->etag_none_match == $src->unique_tag() ) {
+      $error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
+    }
+    if ( $error != '' ) $request->DoResponse( 412, $error );
+  }
+}
+
+$src->NeedPrivilege('DAV::unbind');
+$dest->NeedPrivilege('DAV::write-content');
+if ( ! $dest->Exists() ) $dest->NeedPrivilege('DAV::bind');
+
+
+function rollback( $response_code = 412 ) {
+  global $request;
+  $qry = new AwlQuery('ROLLBACK');
+  $qry->Exec('move'); // Just in case
+  $request->DoResponse( $response_code );
+  // And we don't return from that.
+}
+
+
+$qry = new AwlQuery('BEGIN');
+if ( !$qry->Exec('move') ) rollback(500);
+
+$src_name = $src->dav_name();
+$dst_name = $dest->dav_name();
+$src_collection = $src->GetProperty('collection_id');
+$dst_collection = $dest->GetProperty('collection_id');
+$src_user_no = $src->GetProperty('user_no');
+$dst_user_no = $dest->GetProperty('user_no');
+
+
+if ( $src->IsCollection()  ) {
+  if ( $dest->Exists() ) {
+    $qry = new AwlQuery( 'DELETE FROM collection WHERE dav_name = :dst_name', array( ':dst_name' => $dst_name ) );
+    if ( !$qry->Exec('move') ) rollback(500);
+  }
+  /** @TODO: Need to confirm this will work correctly if we move this into another user's hierarchy. */
+  $sql = 'UPDATE collection SET dav_name = :dst_name ';
+  $params = array(':dst_name' => $dst_name);
+  if ( $src_user_no != $dst_user_no ) {
+    $sql .= ', user_no = :dst_user_no ';
+    $params[':dst_user_no'] = $dst_user_no;
+  }
+  $sql .= 'WHERE collection_id = :src_collection';
+  $params[':src_collection'] = $src_collection;
+  $qry = new AwlQuery( $sql, $params );
+  if ( !$qry->Exec('move') ) rollback(500);
+}
+else {
+  if ( $dest->Exists() ) {
+    $qry = new AwlQuery( 'DELETE FROM caldav_data WHERE dav_name = :dst_name', array( ':dst_name' => $dst_name) );
+    if ( !$qry->Exec('move') ) rollback(500);
+  }
+  $sql = 'UPDATE caldav_data SET dav_name = :dst_name';
+  $params = array( ':dst_name' => $dst_name );
+  if ( $src_user_no != $dst_user_no ) {
+    $sql .= ', user_no = :dst_user_no';
+    $params[':dst_user_no'] = $dst_user_no;
+  }
+  if ( $src_collection != $dst_collection ) {
+    $sql .= ', collection_id = :dst_collection';
+    $params[':dst_collection'] = $dst_collection;
+  }
+  $sql .=' WHERE dav_name = :src_name';
+  $params[':src_name'] = $src_name;
+  $qry = new AwlQuery( $sql, $params );
+  if ( !$qry->Exec('move') ) rollback(500);
+
+  $qry = new AwlQuery( 'SELECT write_sync_change( :src_collection, 404, :src_name );', array(
+    ':src_name' => $src_name,
+    ':src_collection' => $src_collection
+  ) );
+  if ( !$qry->Exec('move') ) rollback(500);
+  if ( function_exists('log_caldav_action') ) {
+    log_caldav_action( 'DELETE', $src->GetProperty('uid'), $src_user_no, $src_collection, $src_name );
+  }
+
+  $qry = new AwlQuery( 'SELECT write_sync_change( :dst_collection, :sync_type, :dst_name );', array(
+    ':dst_name' => $dst_name,
+    ':dst_collection' => $dst_collection,
+    ':sync_type' => ( $dest->Exists() ? 200 : 201 )
+  ) );
+  if ( !$qry->Exec('move') ) rollback(500);
+  if ( function_exists('log_caldav_action') ) {
+    log_caldav_action( ( $dest->Exists() ? 'UPDATE' : 'INSERT' ), $src->GetProperty('uid'), $dst_user_no, $dst_collection, $dst_name );
+  }
+
+}
+
+$qry = new AwlQuery('COMMIT');
+if ( !$qry->Exec('move') ) rollback(500);
+
+$request->DoResponse( 200 );
Index: contrib/davical/inc/caldav-PUT.php
===================================================================
--- contrib/davical/inc/caldav-PUT.php	(revision 3733)
+++ contrib/davical/inc/caldav-PUT.php	(revision 3733)
@@ -0,0 +1,101 @@
+<?php
+/**
+* CalDAV Server - handle PUT method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("PUT", "method handler");
+
+require_once('DAVResource.php');
+
+$dav_resource = new DAVResource($request->path);
+if ( ! $dav_resource->HavePrivilegeTo('DAV::write-content') ) {
+  $request->DoResponse(403);
+}
+
+if ( ! $dav_resource->Exists() && ! $dav_resource->HavePrivilegeTo('DAV::bind') ) {
+  $request->DoResponse(403);
+}
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+  $fh = fopen('/tmp/PUT.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+include_once('caldav-PUT-functions.php');
+
+//controlRequestContainer( $dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
+
+$lock_opener = $request->FailIfLocked();
+
+
+//if ( $dav_resource->IsCollection()  ) {
+//  if ( $dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+//    $request->DoResponse( 405 ); // Method not allowed
+//    return;
+//  }
+  
+//  $appending = (isset($_GET['mode']) && $_GET['mode'] == 'append' );
+
+  /**
+  * CalDAV does not define the result of a PUT on a collection.  We treat that
+  * as an import. The code is in caldav-PUT-functions.php
+  */
+//  import_collection($request->raw_post,$request->user_no,$request->path,true, $appending);
+//  $request->DoResponse( 200 );
+//  return;
+//}
+
+$etag = md5($request->raw_post);
+$ic = new iCalComponent( $request->raw_post );
+
+if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
+  /**
+  * RFC2068, 14.25:
+  * If none of the entity tags match, or if "*" is given and no current
+  * entity exists, the server MUST NOT perform the requested method, and
+  * MUST return a 412 (Precondition Failed) response.
+  */
+  $request->PreconditionFailed(412,'if-match');
+}
+
+if ( $dav_resource->Exists() ) {
+  //if ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $dav_resource->unique_tag() ) {
+    /**
+    * RFC2068, 14.25:
+    * If none of the entity tags match, or if "*" is given and no current
+    * entity exists, the server MUST NOT perform the requested method, and
+    * MUST return a 412 (Precondition Failed) response.
+    */
+    //$request->PreconditionFailed(412,'if-match', translate( 'Existing resource does not match "If-Match" header - not accepted.'));
+  //}
+   if ( isset($request->etag_none_match) && $request->etag_none_match != ''
+               && ($request->etag_none_match == $dav_resource->unique_tag() || $request->etag_none_match == '*') ) {
+    /**
+    * RFC2068, 14.26:
+    * If any of the entity tags match the entity tag of the entity that
+    * would have been returned in the response to a similar GET request
+    * (without the If-None-Match header) on that resource, or if "*" is
+    * given and any current entity exists for that resource, then the
+    * server MUST NOT perform the requested method.
+    */
+    $request->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
+  }
+}
+
+$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
+
+
+write_resource( $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), $request->raw_post, $dav_resource->GetProperty('collection_id'),
+                                $session->user_no, $etag, $ic, $put_action_type, true, true );
+
+header(sprintf('ETag: "%s"', $etag) );
+
+$request->DoResponse( ($dav_resource->Exists() ? 204 : 201) );
Index: contrib/davical/inc/CalDAVRequest.php
===================================================================
--- contrib/davical/inc/CalDAVRequest.php	(revision 3733)
+++ contrib/davical/inc/CalDAVRequest.php	(revision 3733)
@@ -0,0 +1,1291 @@
+<?php
+/**
+* Functions that are needed for all CalDAV Requests
+*
+*  - Ascertaining the paths
+*  - Ascertaining the current user's permission to those paths.
+*  - Utility functions which we can use to decide whether this
+*    is a permitted activity for this user.
+*
+* @package   davical
+* @subpackage   Request
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once("XMLDocument.php");
+require_once("CalDAVPrincipal.php");
+include("DAVTicket.php");
+include_once("drivers_ldap.php");
+
+define('DEPTH_INFINITY', 9999);
+
+/**
+* A class for collecting things to do with this request.
+*
+* @package   davical
+*/
+class CalDAVRequest
+{
+  var $options;
+
+  /**
+  * The raw data sent along with the request
+  */
+  var $raw_post;
+
+  /**
+  * The HTTP request method: PROPFIND, LOCK, REPORT, OPTIONS, etc...
+  */
+  var $method;
+
+  /**
+  * The depth parameter from the request headers, coerced into a valid integer: 0, 1
+  * or DEPTH_INFINITY which is defined above.  The default is set per various RFCs.
+  */
+  var $depth;
+
+  /**
+  * The 'principal' (user/resource/...) which this request seeks to access
+  * @var CalDAVPrincipal
+  */
+  var $principal;
+
+  /**
+  * The 'current_user_principal_xml' the DAV:current-user-principal answer. An
+  * XMLElement object with an <href> or <unauthenticated> fragment.
+  */
+  var $current_user_principal_xml;
+
+  /**
+  * The user agent making the request.
+  */
+  var $user_agent;
+
+  /**
+  * The ID of the collection containing this path, or of this path if it is a collection
+  */
+  var $collection_id;
+
+  /**
+  * The path corresponding to the collection_id
+  */
+  var $collection_path;
+
+  /**
+  * The type of collection being requested:
+  *  calendar, schedule-inbox, schedule-outbox
+  */
+  var $collection_type;
+
+  /**
+  * The type of collection being requested:
+  *  calendar, schedule-inbox, schedule-outbox
+  */
+  protected $exists;
+
+  /**
+  * The decimal privileges allowed by this user to the identified resource.
+  */
+  protected $privileges;
+
+  /**
+  * A static structure of supported privileges.
+  */
+  var $supported_privileges;
+
+  /**
+  * A DAVTicket object, if there is a ?ticket=id or Ticket: id with this request
+  */
+  public $ticket;
+
+  /**
+  * Create a new CalDAVRequest object.
+  */
+  function __construct( $options = array() ) {
+    global $session, $c, $debugging;
+
+    $this->supported_privileges = array(
+      'all' => array(
+        'read' => translate('Read the content of a resource or collection'),
+        'write' => array(
+          'bind' => translate('Create a resource or collection'),
+          'unbind' => translate('Delete a resource or collection'),
+          'write-content' => translate('Write content'),
+          'write-properties' => translate('Write properties')
+        ),
+        'urn:ietf:params:xml:ns:caldav:read-free-busy' => translate('Read the free/busy information for a calendar collection'),
+        'read-acl' => translate('Read ACLs for a resource or collection'),
+        'read-current-user-privilege-set' => translate('Read the details of the current user\'s access control to this resource.'),
+        'write-acl' => translate('Write ACLs for a resource or collection'),
+        'unlock' => translate('Remove a lock'),
+
+        'urn:ietf:params:xml:ns:caldav:schedule-deliver' => array(
+          'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite'=> translate('Deliver scheduling invitations from an organiser to this scheduling inbox'),
+          'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply' => translate('Deliver scheduling replies from an attendee to this scheduling inbox'),
+          'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy' => translate('Allow free/busy enquiries targeted at the owner of this scheduling inbox')
+        ),
+
+        'urn:ietf:params:xml:ns:caldav:schedule-send' => array(
+          'urn:ietf:params:xml:ns:caldav:schedule-send-invite' => translate('Send scheduling invitations as an organiser from the owner of this scheduling outbox.'),
+          'urn:ietf:params:xml:ns:caldav:schedule-send-reply' => translate('Send scheduling replies as an attendee from the owner of this scheduling outbox.'),
+          'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy' => translate('Send free/busy enquiries')
+        )
+      )
+    );
+
+    $this->options = $options;
+    if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false;
+    $this->principal = (object) array( 'username' => $session->username, 'user_no' => $session->user_no );
+
+    $this->raw_post = file_get_contents ( 'php://input');
+
+    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['request']) && $c->dbg['request']) ) {
+      /** Log the request headers */
+      $lines = apache_request_headers();
+      dbg_error_log( "LOG ", "***************** Request Header ****************" );
+      dbg_error_log( "LOG ", "%s %s", $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'] );
+      foreach( $lines AS $k => $v ) {
+        dbg_error_log( "LOG headers", "-->%s: %s", $k, $v );
+      }
+      dbg_error_log( "LOG ", "******************** Request ********************" );
+      // Log the request in all it's gory detail.
+      $lines = preg_split( '#[\r\n]+#', $this->raw_post);
+      foreach( $lines AS $v ) {
+        dbg_error_log( "LOG request", "-->%s", $v );
+      }
+    }
+
+    if ( isset($debugging) && isset($_GET['method']) ) {
+      $_SERVER['REQUEST_METHOD'] = $_GET['method'];
+    }
+    else if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']) ){
+      $_SERVER['REQUEST_METHOD'] = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
+    }
+    $this->method = $_SERVER['REQUEST_METHOD'];
+    if ( isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 7 ) {
+      $this->content_type = (isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null);
+      if ( preg_match( '{^(\S+/\S+)\s*(;.*)?$}', $this->content_type, $matches ) ) {
+        $this->content_type = $matches[1];
+      }
+      if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' ) {
+        if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
+          dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
+                                                 $request->method, $this->content_type );
+          $this->content_type = 'text/xml';
+        }
+      }
+    }
+    $this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
+
+    /**
+    * A variety of requests may set the "Depth" header to control recursion
+    */
+    if ( isset($_SERVER['HTTP_DEPTH']) ) {
+      $this->depth = $_SERVER['HTTP_DEPTH'];
+    }
+    else {
+      /**
+      * Per rfc2518, section 9.2, 'Depth' might not always be present, and if it
+      * is not present then a reasonable request-type-dependent default should be
+      * chosen.
+      */
+      switch( $this->method ) {
+        case 'PROPFIND':
+        case 'DELETE':
+        case 'MOVE':
+        case 'COPY':
+        case 'LOCK':
+          $this->depth = 'infinity';
+          break;
+
+        case 'REPORT':
+        default:
+          $this->depth = 0;
+      }
+    }
+    if ( $this->depth == 'infinity' ) $this->depth = DEPTH_INFINITY;
+    $this->depth = intval($this->depth);
+
+    /**
+    * MOVE/COPY use a "Destination" header and (optionally) an "Overwrite" one.
+    */
+    if ( isset($_SERVER['HTTP_DESTINATION']) ) $this->destination = $_SERVER['HTTP_DESTINATION'];
+    $this->overwrite = ( isset($_SERVER['HTTP_OVERWRITE']) && ($_SERVER['HTTP_OVERWRITE'] == 'F') ? false : true ); // RFC4918, 9.8.4 says default True.
+
+    /**
+    * LOCK things use an "If" header to hold the lock in some cases, and "Lock-token" in others
+    */
+    if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
+    if ( isset($_SERVER['HTTP_LOCK_TOKEN']) && preg_match( '#[<]opaquelocktoken:(.*)[>]#', $_SERVER['HTTP_LOCK_TOKEN'], $matches ) ) {
+      $this->lock_token = $matches[1];
+    }
+
+    /**
+    * Check for an access ticket.
+    */
+    if ( isset($_GET['ticket']) ) {
+      $this->ticket = new DAVTicket($_GET['ticket']);
+    }
+    else if ( isset($_SERVER['HTTP_TICKET']) ) {
+      $this->ticket = new DAVTicket($_SERVER['HTTP_TICKET']);
+    }
+
+    /**
+    * LOCK things use a "Timeout" header to set a series of reducing alternative values
+    */
+    if ( isset($_SERVER['HTTP_TIMEOUT']) ) {
+      $timeouts = explode( ',', $_SERVER['HTTP_TIMEOUT'] );
+      foreach( $timeouts AS $k => $v ) {
+        if ( strtolower($v) == 'infinite' ) {
+          $this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
+          break;
+        }
+        elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
+          $this->timeout = min( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
+          break;
+        }
+      }
+      if ( ! isset($this->timeout) || $this->timeout == 0 ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
+    }
+
+    /**
+    * Our path is /<script name>/<user name>/<user controlled> if it ends in
+    * a trailing '/' then it is referring to a DAV 'collection' but otherwise
+    * it is referring to a DAV data item.
+    *
+    * Permissions are controlled as follows:
+    *  1. if there is no <user name> component, the request has read privileges
+    *  2. if the requester is an admin, the request has read/write priviliges
+    *  3. if there is a <user name> component which matches the logged on user
+    *     then the request has read/write privileges
+    *  4. otherwise we query the defined relationships between users and use
+    *     the minimum privileges returned from that analysis.
+    */
+    $this->path = (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : "/");
+    $this->path = rawurldecode($this->path);
+
+    /** Allow a request for .../calendar.ics to translate into the calendar URL */
+    if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) {
+      $this->path = $matches[1]. '/';
+    }
+
+    // dbg_error_log( "caldav", "Sanitising path '%s'", $this->path );
+    $bad_chars_regex = '/[\\^\\[\\(\\\\]/';
+    if ( preg_match( $bad_chars_regex, $this->path ) ) {
+      $this->DoResponse( 400, translate("The calendar path contains illegal characters.") );
+    }
+    if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path);
+
+    $this->user_no = $session->user_no;
+    $this->username = $session->username;
+    if ( $session->user_no > 0 ) {
+      $this->current_user_principal_url = new XMLElement('href', ConstructURL('/'.$session->username.'/') );
+    }
+    else {
+      $this->current_user_principal_url = new XMLElement('unauthenticated' );
+    }
+
+    /**
+    * RFC2518, 5.2: URL pointing to a collection SHOULD end in '/', and if it does not then
+    * we SHOULD return a Content-location header with the correction...
+    *
+    * We therefore look for a collection which matches one of the following URLs:
+    *  - The exact request.
+    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+    *  - The request URL truncated to the last '/'
+    * The collection URL for this request is therefore the longest row in the result, so we
+    * can "... ORDER BY LENGTH(dav_name) DESC LIMIT 1"
+    */
+    $sql = "SELECT * FROM collection WHERE dav_name = :exact_name";
+    $params = array( ':exact_name' => $this->path );
+    if ( !preg_match( '#/$#', $this->path ) ) {
+      $sql .= " OR dav_name = :truncated_name OR dav_name = :trailing_slash_name";
+      $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $this->path);
+      $params[':trailing_slash_name'] = $this->path."/";
+    }
+    $sql .= " ORDER BY LENGTH(dav_name) DESC LIMIT 1";
+    $qry = new AwlQuery( $sql, $params );
+    if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+      if ( $row->dav_name == $this->path."/" ) {
+        $this->path = $row->dav_name;
+        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+        header( "Content-Location: ".ConstructURL($this->path) );
+      }
+
+      $this->collection_id = $row->collection_id;
+      $this->collection_path = $row->dav_name;
+      $this->collection_type = ($row->is_calendar == 't' ? 'calendar' : 'collection');
+      $this->collection_type = ($row->is_addressbook == 't' ? 'addressbook' : 'collection');
+      $this->collection = $row;
+      if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
+        $this->collection_type = 'schedule-'. $matches[3]. 'box';
+      }
+      $this->collection->type = $this->collection_type;
+    }
+    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->path, $matches ) ) {
+      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+      $params = array( ':username' => $matches[3], ':parent_container' => $matches[2], ':dav_name' => $matches[1] );
+      $params[':boxname'] = ($matches[4] == 'in' ? ' Inbox' : ' Outbox');
+      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+      $sql = <<<EOSQL
+INSERT INTO collection ( user_no, parent_container, dav_name, dav_displayname, is_calendar, created, modified, dav_etag, resourcetypes )
+    VALUES( (SELECT user_no FROM usr WHERE username = :username),
+            :parent_container, :dav_name,
+            (SELECT fullname FROM usr WHERE username = :username) || :boxname,
+             FALSE, current_timestamp, current_timestamp, '1', :resourcetypes )
+EOSQL;
+
+      $qry = new AwlQuery( $sql, $params );
+      $qry->Exec('caldav',__LINE__,__FILE__);
+      dbg_error_log( 'caldav', 'Created new collection as "%s".', trim($params[':boxname']) );
+
+      $qry = new AwlQuery( "SELECT * FROM collection WHERE dav_name = :dav_name", array( ':dav_name' => $matches[1] ) );
+      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+        $this->collection_id = $row->collection_id;
+        $this->collection_path = $matches[1];
+        $this->collection = $row;
+        $this->collection->type = $this->collection_type;
+      }
+    }
+    else if ( preg_match( '{^( ( / ([^/]+) / ) addressbook/ ) [^/]*$}x', $this->path, $matches )  ) {
+      // The request is for a scheduling adressbook and we should auto-create it
+      
+      $dav_etag = md5("/".$matches[3]."/addressbook/");
+      $params = array( ':username' => $matches[3], ':parent_container' => "/".$matches[3]."/", ':dav_name' => "/".$matches[3]."/addressbook/", ':etag' =>  $dav_etag );
+      $params[':boxname'] = $matches[3];
+      $this->collection_type = 'addressbook';
+      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:carddav:%s/>',$this->collection_type);
+      $sql = "INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified, is_addressbook, resourcetypes ) VALUES ( (SELECT user_no FROM usr WHERE username = :username),:parent_container, :dav_name, :etag, (SELECT fullname FROM usr WHERE username = :username) || :boxname, FALSE, current_timestamp, current_timestamp, TRUE , :resourcetypes )";
+      $qry = new AwlQuery( $sql, $params );
+      $qry->Exec('carddav',__LINE__,__FILE__);
+      dbg_error_log( 'carddav', 'Created new collection as "%s".', trim($params[':boxname']) );
+
+      $qry = new AwlQuery( "SELECT * FROM collection WHERE dav_name = :dav_name", array( ':dav_name' => $matches[3].$matches[4] ) );
+      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+        $this->collection_id = $row->collection_id;
+        $this->collection_path = $row->dav_name;
+        $this->collection = $row;
+        $this->collection->type = $this->collection_type;
+      }
+    }
+ 
+    else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) {
+      $this->collection_type = 'proxy';
+      $this->_is_proxy_request = true;
+      $this->proxy_type = $matches[3];
+      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+      if ( $this->collection_path == $this->path."/" ) {
+        $this->path .= '/';
+        dbg_error_log( "caldav", "Path is actually a (proxy) collection - sending Content-Location header." );
+        header( "Content-Location: ".ConstructURL($this->path) );
+      }
+    }
+    else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) {
+      /** @TODO: we should deprecate this now that Evolution 2.27 can do scheduling extensions */
+      $this->collection_id = -1;
+      $this->collection_type = 'email';
+      $this->collection_path = $this->path;
+      $this->_is_principal = true;
+    }
+    else if ( preg_match( '#^(/[^/]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+      $this->collection_id = -1;
+      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+      $this->collection_type = 'principal';
+      $this->_is_principal = true;
+      if ( $this->collection_path == $this->path."/" ) {
+        $this->path .= '/';
+        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+        header( "Content-Location: ".ConstructURL($this->path) );
+      }
+      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+        // Force a depth of 0 on these, which are at the wrong URL.
+        $this->depth = 0;
+      }
+    }
+    else if ( $this->path == '/' ) {
+      $this->collection_id = -1;
+      $this->collection_path = '/';
+      $this->collection_type = 'root';
+    }
+
+    if ( $this->collection_path == $this->path ) $this->_is_collection = true;
+    dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
+
+    /**
+    * Extract the user whom we are accessing
+    */
+    $this->principal = new CalDAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
+    if ( isset($this->principal->user_no) ) $this->user_no  = $this->principal->user_no;
+    if ( isset($this->principal->username)) $this->username = $this->principal->username;
+    if ( isset($this->principal->by_email) && $this->principal->by_email) $this->by_email = true;
+    if ( isset($this->principal->principal_id)) $this->principal_id = $this->principal->principal_id;
+
+    if ( $this->collection_type == 'principal' || $this->collection_type == 'email' || $this->collection_type == 'proxy' ) {
+      $this->collection = $this->principal->AsCollection();
+      if( $this->collection_type == 'proxy' ) {
+        $this->collection = $this->principal->AsCollection();
+        $this->collection->is_proxy = 't';
+        $this->collection->type = 'proxy';
+        $this->collection->proxy_type = $this->proxy_type;
+        $this->collection->dav_displayname = sprintf('Proxy %s for %s', $this->proxy_type, $this->principal->username() );
+      }
+    }
+    elseif( $this->collection_type == 'root' ) {
+      $this->collection = (object) array(
+                            'collection_id' => 0,
+                            'dav_name' => '/',
+                            'dav_etag' => md5($c->system_name),
+                            'is_calendar' => 'f',
+                            'is_addressbook' => 'f',
+                            'is_principal' => 'f',
+                            'user_no' => 0,
+                            'dav_displayname' => $c->system_name,
+                            'type' => 'root',
+                            'created' => date('Ymd\THis')
+                          );
+    }
+
+    /**
+    * Evaluate our permissions for accessing the target
+    */
+    $this->setPermissions();
+
+    $this->supported_methods = array(
+      'OPTIONS' => '',
+      'PROPFIND' => '',
+      'REPORT' => '',
+      'DELETE' => '',
+      'LOCK' => '',
+      'UNLOCK' => '',
+      'MOVE' => '',
+      'ACL' => ''
+    );
+    if ( $this->IsCollection() ) {
+      switch ( $this->collection_type ) {
+        case 'root':
+        case 'email':
+          // We just override the list completely here.
+          $this->supported_methods = array(
+            'OPTIONS' => '',
+            'PROPFIND' => '',
+            'REPORT' => ''
+          );
+          break;
+        case 'schedule-inbox':
+        case 'schedule-outbox':
+          $this->supported_methods = array_merge(
+            $this->supported_methods,
+            array(
+              'POST' => '', 'GET' => '', 'PUT' => '', 'HEAD' => '', 'PROPPATCH' => ''
+            )
+          );
+          break;
+        case 'calendar':
+          $this->supported_methods['GET'] = '';
+          $this->supported_methods['PUT'] = '';
+          $this->supported_methods['HEAD'] = '';
+          break;
+        case 'collection':
+        case 'principal':
+          $this->supported_methods['GET'] = '';
+          $this->supported_methods['PUT'] = '';
+          $this->supported_methods['HEAD'] = '';
+          $this->supported_methods['MKCOL'] = '';
+          $this->supported_methods['MKCALENDAR'] = '';
+          $this->supported_methods['PROPPATCH'] = '';
+          $this->supported_methods['BIND'] = '';
+          break;
+      }
+    }
+    else {
+      $this->supported_methods = array_merge(
+        $this->supported_methods,
+        array(
+          'GET' => '',
+          'HEAD' => '',
+          'PUT' => ''
+        )
+      );
+    }
+    /**** comentado por motivos de incmpatibilidade do sync-collection
+    $this->supported_reports = array(
+      'DAV::principal-property-search' => '',
+      'DAV::expand-property' => '',
+      'DAV::sync-collection' => ''
+    );
+    */  
+    $this->supported_reports = array(
+      'DAV::principal-property-search' => '',
+      'DAV::expand-property' => ''
+    );
+    if ( isset($this->collection) && $this->collection->is_calendar ) {
+      $this->supported_reports = array_merge(
+        $this->supported_reports,
+        array(
+          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+        )
+      );
+    }
+    if ( isset($this->collection) && $this->collection->is_addressbook ) {
+      $this->supported_reports = array_merge(
+        $this->supported_reports,
+        array(
+          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+        )
+      );
+    }
+
+
+    /**
+    * If the content we are receiving is XML then we parse it here.  RFC2518 says we
+    * should reasonably expect to see either text/xml or application/xml
+    */
+    if ( isset($this->content_type) && preg_match( '#(application|text)/xml#', $this->content_type ) ) {
+      $xml_parser = xml_parser_create_ns('UTF-8');
+      $this->xml_tags = array();
+      xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
+      xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
+      $rc = xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
+      if ( $rc == false ) {
+        dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
+                    xml_error_string(xml_get_error_code($xml_parser)),
+                    xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
+        $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml'), array( 'xmlns' => 'DAV:') ) );
+      }
+      xml_parser_free($xml_parser);
+      if ( count($this->xml_tags) ) {
+        dbg_error_log( "caldav", " Parsed incoming XML request body." );
+      }
+      else {
+        $this->xml_tags = null;
+        dbg_error_log( "ERROR", "Incoming request sent content-type XML with no XML request body." );
+      }
+    }
+
+    /**
+    * Look out for If-None-Match or If-Match headers
+    */
+    if ( isset($_SERVER["HTTP_IF_NONE_MATCH"]) ) {
+      $this->etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
+      if ( $this->etag_none_match == '' ) unset($this->etag_none_match);
+    }
+    if ( isset($_SERVER["HTTP_IF_MATCH"]) ) {
+      $this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
+      if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
+    }
+   dbg_error_log( "caldav", " FIM do contntrutor." );
+  }
+
+
+  /**
+  * Work out the user whose calendar we are accessing, based on elements of the path.
+  */
+  function UserFromPath() {
+    global $session;
+
+    $this->user_no = $session->user_no;
+    $this->username = $session->username;
+    $this->principal_id = $session->principal_id;
+
+    @dbg_error_log( "WARN", "Call to deprecated CalDAVRequest::UserFromPath()" );
+
+    if ( $this->path == '/' || $this->path == '' ) {
+      dbg_error_log( "caldav", "No useful path split possible" );
+      return false;
+    }
+
+    $path_split = explode('/', $this->path );
+    $this->username = $path_split[1];
+    if ( $this->username == 'principals' ) $this->username = $path_split[3];
+    @dbg_error_log( "caldav", "Path split into at least /// %s /// %s /// %s", $path_split[1], $path_split[2], $path_split[3] );
+    if ( isset($this->options['allow_by_email']) && preg_match( '#/(\S+@\S+[.]\S+)/?$#', $this->path, $matches) ) {
+      $this->by_email = $matches[1];
+      $qry = new AwlQuery("SELECT user_no, principal_id, username FROM usr JOIN principal USING (user_no) WHERE email = :email",
+                          array(':email' => $this->by_email ) );
+      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $user = $qry->Fetch() ) {
+        $this->user_no = $user->user_no;
+        $this->username = $user->username;
+        $this->principal_id = $user->principal_id;
+      }
+    }
+    elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)) {
+      $this->principal = $user;
+      $this->user_no = $user->user_no;
+      $this->principal_id = $user->principal_id;
+    }
+  }
+
+
+  /**
+  * Permissions are controlled as follows:
+  *  1. if the path is '/', the request has read privileges
+  *  2. if the requester is an admin, the request has read/write priviliges
+  *  3. if there is a <user name> component which matches the logged on user
+  *     then the request has read/write privileges
+  *  4. otherwise we query the defined relationships between users and use
+  *     the minimum privileges returned from that analysis.
+  *
+  * @param int $user_no The current user number
+  *
+  */
+  function setPermissions() {
+    global $c, $session;
+     
+    $path = preg_replace("/\/|addressbook/","",$this->collection_path);
+    dbg_error_log( "permissionn", "pasta  %s pasta de verdade %s",$path,$this->path );
+    if ( preg_match('{^/.well-known/carddav$}',$this->path))
+        {
+            $this->privileges = privilege_to_bits('all');
+             dbg_error_log( "caldav", "Full permissions for %s for path %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator"),$this->path ); 
+       }   
+    else if ( $this->path == '/' || $this->path == '' ) {
+      $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+      dbg_error_log( "caldav", "Full read permissions for user accessing /" );
+    }
+    else if ( $session->AllowedTo("Admin") || $session->user_no == $this->user_no && $this->username == $path ) {
+    //else if ( $session->AllowedTo("Admin") || $session->user_no == $this->user_no ) {
+      $this->privileges = privilege_to_bits('all');
+      dbg_error_log( "caldav", "Full permissions for %s for path %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator"),$this->path );
+    }
+    else if ( $this->username != $path && $this->collection_type == 'calendar' ) { 
+         $this->privileges = 0; 
+         $filtro = "uid=".$path;
+         $atributos = array("uidNumber");
+         $newpathh = ldapDrivers::requestAtributo($filtro, $atributos);           
+         $newpath = $newpathh['uidNumber'];
+         $filtro = "uid=".$this->username;
+         $atributos = array("uidNumber");
+         $accountt = ldapDrivers::requestAtributo($filtro, $atributos); 
+         $account = $accountt['uidNumber'];
+         $qry = new AwlQuery("select acl_rights from phpgw_acl where acl_location = :account and acl_account = :path" ,array(':account' => $account ,':path' => $newpath));
+         if ( $qry->Exec('Request',__LINE__,__FILE__) &&  $qry->rows() == 1 && $permission_result = $qry->Fetch()){
+               switch( $permission_result->acl_rights ) {
+                     case '1' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+                                  dbg_error_log( "caldav", "Basic read permissions for user accessing " );
+                                  break;
+                     case '3' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write'));
+                                  dbg_error_log( "caldav", "Basic read/write permissions for user accessing " );
+                                  break;
+                     case '7' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write'));
+                                  dbg_error_log( "caldav", "Basic read/write/edit permissions for user accessing " );
+                                  break;
+                     case '15' :  $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write','schedule-send'));
+                                  dbg_error_log( "caldav", "Basic read/write/edit/delete  permissions for user accessing  " );
+                                  break;
+                     case '31' :  $this->privileges = privilege_to_bits('all');      
+                                  dbg_error_log( "caldav", "Full permissions for %s for path %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator"),$this->path );
+                                  break;
+             } 
+            
+         }         
+      }
+    else if ( $this->username == $path  && $this->collection_type == 'addressbook' ) {
+           $this->privileges = privilege_to_bits('all');  
+      }
+    else {
+      $this->privileges = 0;
+      if ( $this->IsPublic() ) {
+        $this->privileges = privilege_to_bits(array('read','read-free-busy'));
+        dbg_error_log( "caldav", "Basic read permissions for user accessing a public collection" );
+      }
+      else if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
+        $this->privileges = privilege_to_bits('read-free-busy');
+        dbg_error_log( "caldav", "Basic free/busy permissions for user accessing a public free/busy URL" );
+      }
+
+      /**
+      * In other cases we need to query the database for permissions
+      */
+      $params = array( ':session_principal_id' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+      if ( isset($this->by_email) && $this->by_email ) {
+        $sql ='SELECT pprivs( :session_principal_id::int8, :request_principal_id::int8, :scan_depth::int ) AS perm';
+        $params[':request_principal_id'] = $this->principal_id;
+      }
+      else {
+        $sql = 'SELECT path_privs( :session_principal_id::int8, :request_path::text, :scan_depth::int ) AS perm';
+        $params[':request_path'] = $this->path;
+      }
+      $qry = new AwlQuery( $sql, $params );
+      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $permission_result = $qry->Fetch() )
+        $this->privileges |= bindec($permission_result->perm);
+
+      dbg_error_log( 'caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges) );
+      if ( isset($this->ticket) && $this->ticket->MatchesPath($this->path) ) {
+        $this->privileges |= $this->ticket->privileges();
+        dbg_error_log( 'caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges) );
+      }
+    }
+
+    /** convert privileges into older style permissions */
+    $this->permissions = array();
+    $privs = bits_to_privilege($this->privileges);
+    foreach( $privs AS $k => $v ) {
+      switch( $v ) {
+        case 'DAV::all':    $type = 'abstract';   break;
+        case 'DAV::write':  $type = 'aggregate';  break;
+        default: $type = 'real';
+      }
+      $v = str_replace('DAV::', '', $v);
+      $this->permissions[$v] = $type;
+    }
+
+  }
+
+
+  /**
+  * Checks whether the resource is locked, returning any lock token, or false
+  *
+  * @todo This logic does not catch all locking scenarios.  For example an infinite
+  * depth request should check the permissions for all collections and resources within
+  * that.  At present we only maintain permissions on a per-collection basis though.
+  */
+  function IsLocked() {
+    if ( !isset($this->_locks_found) ) {
+      $this->_locks_found = array();
+
+      $sql = 'DELETE FROM locks WHERE (start + timeout) < current_timestamp';
+      $qry = new AwlQuery($sql);
+      $qry->Exec('caldav',__LINE__,__FILE__);
+
+      /**
+      * Find the locks that might apply and load them into an array
+      */
+      $sql = 'SELECT * FROM locks WHERE :dav_name::text ~ (\'^\'||dav_name||:pattern_end_match)::text';
+      $qry = new AwlQuery($sql, array( ':dav_name' => $this->path, ':pattern_end_match' => ($this->IsInfiniteDepth() ? '' : '$') ) );
+      if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+        while( $lock_row = $qry->Fetch() ) {
+          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+        }
+      }
+      else {
+        $this->DoResponse(500,translate("Database Error"));
+        // Does not return.
+      }
+    }
+
+    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
+        return $lock_token;
+      }
+    }
+
+    return false;  // Nothing matched
+  }
+
+
+  /**
+  * Checks whether the collection is public
+  */
+  function IsPublic() {
+    if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
+      return true;
+    }
+    return false;
+  }
+
+
+  /**
+  * Returns the dav_name of the resource in our internal namespace
+  */
+  function dav_name() {
+    if ( isset($this->path) ) return $this->path;
+    return null;
+  }
+
+
+  /**
+  * Returns the name for this depth: 0, 1, infinity
+  */
+  function GetDepthName( ) {
+    if ( $this->IsInfiniteDepth() ) return 'infinity';
+    return $this->depth;
+  }
+
+  /**
+  * Returns the tail of a Regex appropriate for this Depth, when appended to
+  *
+  */
+  function DepthRegexTail() {
+    if ( $this->IsInfiniteDepth() ) return '';
+    if ( $this->depth == 0 ) return '$';
+    return '[^/]*/?$';
+  }
+
+  /**
+  * Returns the locked row, either from the cache or from the database
+  *
+  * @param string $dav_name The resource which we want to know the lock status for
+  */
+  function GetLockRow( $lock_token ) {
+    if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
+      return $this->_locks_found[$lock_token];
+    }
+
+    $qry = new AwlQuery('SELECT * FROM locks WHERE opaquelocktoken = :lock_token', array( ':lock_token' => $lock_token ) );
+    if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+      $lock_row = $qry->Fetch();
+      $this->_locks_found = array( $lock_token => $lock_row );
+      return $this->_locks_found[$lock_token];
+    }
+    else {
+      $this->DoResponse( 500, translate("Database Error") );
+    }
+
+    return false;  // Nothing matched
+  }
+
+
+  /**
+  * Checks to see whether the lock token given matches one of the ones handed in
+  * with the request.
+  *
+  * @param string $lock_token The opaquelocktoken which we are looking for
+  */
+  function ValidateLockToken( $lock_token ) {
+    if ( isset($this->lock_token) && $this->lock_token == $lock_token ) {
+      dbg_error_log( "caldav", "They supplied a valid lock token.  Great!" );
+      return true;
+    }
+    if ( isset($this->if_clause) ) {
+      dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $this->if_clause );
+      $tokens = preg_split( '/[<>]/', $this->if_clause );
+      foreach( $tokens AS $k => $v ) {
+        dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $v );
+        if ( 'opaquelocktoken:' == substr( $v, 0, 16 ) ) {
+          if ( substr( $v, 16 ) == $lock_token ) {
+            dbg_error_log( "caldav", "Lock token '%s' validated OK against '%s'", $lock_token, $v );
+            return true;
+          }
+        }
+      }
+    }
+    else {
+      @dbg_error_log( "caldav", "Invalid lock token '%s' - not in Lock-token (%s) or If headers (%s) ", $lock_token, $this->lock_token, $this->if_clause );
+    }
+
+    return false;
+  }
+
+
+  /**
+  * Returns the DB object associated with a lock token, or false.
+  *
+  * @param string $lock_token The opaquelocktoken which we are looking for
+  */
+  function GetLockDetails( $lock_token ) {
+    if ( !isset($this->_locks_found) && false === $this->IsLocked() ) return false;
+    if ( isset($this->_locks_found[$lock_token]) ) return $this->_locks_found[$lock_token];
+    return false;
+  }
+
+
+  /**
+  * This will either (a) return false if no locks apply, or (b) return the lock_token
+  * which the request successfully included to open the lock, or:
+  * (c) respond directly to the client with the failure.
+  *
+  * @return mixed false (no lock) or opaquelocktoken (opened lock)
+  */
+  function FailIfLocked() {
+    if ( $existing_lock = $this->IsLocked() ) { // NOTE Assignment in if() is expected here.
+      dbg_error_log( "caldav", "There is a lock on '%s'", $this->path);
+      if ( ! $this->ValidateLockToken($existing_lock) ) {
+        $lock_row = $this->GetLockRow($existing_lock);
+        /**
+        * Already locked - deny it
+        */
+        $response[] = new XMLElement( 'response', array(
+            new XMLElement( 'href',   $lock_row->dav_name ),
+            new XMLElement( 'status', 'HTTP/1.1 423 Resource Locked')
+        ));
+        if ( $lock_row->dav_name != $this->path ) {
+          $response[] = new XMLElement( 'response', array(
+              new XMLElement( 'href',   $this->path ),
+              new XMLElement( 'propstat', array(
+                new XMLElement( 'prop', new XMLElement( 'lockdiscovery' ) ),
+                new XMLElement( 'status', 'HTTP/1.1 424 Failed Dependency')
+              ))
+          ));
+        }
+        $response = new XMLElement( "multistatus", $response, array('xmlns'=>'DAV:') );
+        $xmldoc = $response->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+        $this->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+        // Which we won't come back from
+      }
+      return $existing_lock;
+    }
+    return false;
+  }
+
+
+  /**
+  * Coerces the Content-type of the request into something valid/appropriate
+  */
+  function CoerceContentType() {
+    if ( isset($this->content_type) ) {
+      $type = explode( '/', $this->content_type, 2);
+      /** @todo: Perhaps we should look at the target collection type, also. */
+      if ( $type[0] == 'text' ) return;
+    }
+
+    /** Null (or peculiar) content-type supplied so we have to try and work it out... */
+    $first_word = trim(substr( $this->raw_post, 0, 30));
+    $first_word = strtoupper( preg_replace( '/\s.*/s', '', $first_word ) );
+    switch( $first_word ) {
+      case '<?XML':
+        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/xml"',
+                                        (isset($this->content_type)?$this->content_type:'(null)') );
+        $this->content_type = 'text/xml';
+        break;
+      case 'BEGIN:VCALENDAR':
+        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/calendar"',
+                                        (isset($this->content_type)?$this->content_type:'(null)') );
+        $this->content_type = 'text/calendar';
+        break;
+      case 'BEGIN:VCARD':
+        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/vcard"',
+                                        (isset($this->content_type)?$this->content_type:'(null)') );
+        $this->content_type = 'text/vcard';
+        break;
+      default:
+        dbg_error_log( 'LOG NOTICE', 'Unusual content-type of "%s" and first word of content is "%s"',
+                                        (isset($this->content_type)?$this->content_type:'(null)'), $first_word );
+    }
+  }
+
+
+  /**
+  * Returns true if the URL referenced by this request points at a collection.
+  */
+  function IsCollection( ) {
+    if ( !isset($this->_is_collection) ) {
+      $this->_is_collection = preg_match( '#/$#', $this->path );
+    }
+    return $this->_is_collection;
+  }
+
+
+  /**
+  * Returns true if the URL referenced by this request points at a calendar collection.
+  */
+  function IsCalendar( ) {
+    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+    return $this->collection->is_calendar == 't';
+  }
+
+
+  /**
+  * Returns true if the URL referenced by this request points at an addressbook collection.
+  */
+  function IsAddressBook( ) {
+    //if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+    if ( $this->collection_type == 'addressbook' )  
+         return true;
+    else
+       return false;
+  }
+
+
+  /**
+  * Returns true if the URL referenced by this request points at a principal.
+  */
+  function IsPrincipal( ) {
+    if ( !isset($this->_is_principal) ) {
+      $this->_is_principal = preg_match( '#^/[^/]+/$#', $this->path );
+    }
+    return $this->_is_principal;
+  }
+
+
+  /**
+  * Returns true if the URL referenced by this request is within a proxy URL
+  */
+  function IsProxyRequest( ) {
+    if ( !isset($this->_is_proxy_request) ) {
+      $this->_is_proxy_request = preg_match( '#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->path );
+    }
+    return $this->_is_proxy_request;
+  }
+
+
+  /**
+  * Returns true if the request asked for infinite depth
+  */
+  function IsInfiniteDepth( ) {
+    return ($this->depth == DEPTH_INFINITY);
+  }
+
+
+  /**
+  * Returns the ID of the collection of, or containing this request
+  */
+  function CollectionId( ) {
+    return $this->collection_id;
+  }
+
+
+  /**
+  * Returns the array of supported privileges converted into XMLElements
+  */
+  function BuildSupportedPrivileges( &$reply, $privs = null ) {
+    $privileges = array();
+    if ( $privs === null ) $privs = $this->supported_privileges;
+    foreach( $privs AS $k => $v ) {
+      dbg_error_log( 'caldav', 'Adding privilege "%s" which is "%s".', $k, $v );
+      $privilege = new XMLElement('privilege');
+      $reply->NSElement($privilege,$k);
+      $privset = array($privilege);
+      if ( is_array($v) ) {
+        dbg_error_log( 'caldav', '"%s" is a container of sub-privileges.', $k );
+        $privset = array_merge($privset, $this->BuildSupportedPrivileges($reply,$v));
+      }
+      else if ( $v == 'abstract' ) {
+        dbg_error_log( 'caldav', '"%s" is an abstract privilege.', $v );
+        $privset[] = new XMLElement('abstract');
+      }
+      else if ( strlen($v) > 1 ) {
+        $privset[] = new XMLElement('description', $v);
+      }
+      $privileges[] = new XMLElement('supported-privilege',$privset);
+    }
+    return $privileges;
+  }
+
+
+  /**
+  * Are we allowed to do the requested activity
+  *
+  * +------------+------------------------------------------------------+
+  * | METHOD     | PRIVILEGES                                           |
+  * +------------+------------------------------------------------------+
+  * | MKCALENDAR | DAV:bind                                             |
+  * | REPORT     | DAV:read or CALDAV:read-free-busy (on all referenced |
+  * |            | resources)                                           |
+  * +------------+------------------------------------------------------+
+  *
+  * @param string $activity The activity we want to do.
+  */
+  function AllowedTo( $activity ) {
+    global $session;
+    dbg_error_log('caldav', 'Checking whether "%s" is allowed to "%s"', $session->username, $activity);
+    if ( isset($this->permissions['all']) ) return true;
+    switch( $activity ) {
+      case 'all':
+        return false; // If they got this far then they don't
+        break;
+
+      case "CALDAV:schedule-send-freebusy":
+        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+        break;
+
+      case "CALDAV:schedule-send-invite":
+        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+        break;
+
+      case "CALDAV:schedule-send-reply":
+        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+        break;
+
+      case 'freebusy':
+        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+        break;
+
+      case 'delete':
+        return isset($this->permissions['write']) || isset($this->permissions['unbind']);
+        break;
+
+      case 'proppatch':
+        return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
+        break;
+
+      case 'modify':
+        return isset($this->permissions['write']) || isset($this->permissions['write-content']);
+        break;
+
+      case 'create':
+        return isset($this->permissions['write']) || isset($this->permissions['bind']);
+        break;
+
+      case 'mkcalendar':
+      case 'mkcol':
+        if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
+        if ( $this->is_principal ) return false;
+        if ( $this->path == '/' ) return false;
+        break;
+
+      default:
+        $test_bits = privilege_to_bits( $activity );
+//        dbg_error_log( 'caldav', 'request::AllowedTo("%s") (%s) against allowed "%s" => "%s" (%s)',
+//             (is_array($activity) ? implode(',',$activity) : $activity), decbin($test_bits),
+//             decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+        return (($this->privileges & $test_bits) > 0 );
+        break;
+    }
+
+    return false;
+  }
+
+
+
+  /**
+  * Return the privileges bits for the current session user to this resource
+  */
+  function Privileges() {
+    return $this->privileges;
+  }
+
+
+  /**
+  * Is the user has the privileges to do what is requested.
+  */
+  function HavePrivilegeTo( $do_what ) {
+    $test_bits = privilege_to_bits( $do_what );
+//    dbg_error_log( 'caldav', 'request::HavePrivilegeTo("%s") [%s] against allowed "%s" => "%s" (%s)',
+//             (is_array($do_what) ? implode(',',$do_what) : $do_what), decbin($test_bits),
+//              decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+    return ($this->privileges & $test_bits) > 0;
+  }
+
+
+  /**
+  * Sometimes it's a perfectly formed request, but we just don't do that :-(
+  * @param array $unsupported An array of the properties we don't support.
+  */
+  function UnsupportedRequest( $unsupported ) {
+    if ( isset($unsupported) && count($unsupported) > 0 ) {
+      $badprops = new XMLElement( "prop" );
+      foreach( $unsupported AS $k => $v ) {
+        // Not supported at this point...
+        dbg_error_log("ERROR", " %s: Support for $v:$k properties is not implemented yet", $this->method );
+        $badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
+      }
+      $error = new XMLElement("error", $badprops, array("xmlns" => "DAV:") );
+
+      $this->XMLResponse( 422, $error );
+    }
+  }
+
+
+  /**
+  * Send a need-privileges error response.  This function will only return
+  * if the $href is not supplied and the current user has the specified
+  * permission for the request path.
+  *
+  * @param string $privilege The name of the needed privilege.
+  * @param string $href The unconstructed URI where we needed the privilege.
+  */
+  function NeedPrivilege( $privileges, $href=null ) {
+    if ( is_string($privileges) ) $privileges = array( $privileges );
+    if ( !isset($href) ) {
+      if ( $this->HavePrivilegeTo($privileges) ) return;
+      $href = $this->path;
+    }
+
+    $reply = new XMLDocument( array('DAV:' => '') );
+    $privnodes = array( $reply->href(ConstructURL($href)), new XMLElement( 'privilege' ) );
+    // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
+    $reply->NSElement( $privnodes[1], $privileges[0] );
+    $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+    $xmldoc = $reply->Render('error',$xml);
+    $this->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+    exit(0);  // Unecessary, but might clarify things
+  }
+
+
+  /**
+  * Send an error response for a failed precondition.
+  *
+  * @param int $status The status code for the failed precondition.  Normally 403
+  * @param string $precondition The namespaced precondition tag.
+  * @param string $explanation An optional text explanation for the failure.
+  */
+  function PreconditionFailed( $status, $precondition, $explanation = '') {
+    $xmldoc = sprintf('<?xml version="1.0" encoding="utf-8" ?>
+<error xmlns="DAV:">
+  <%s/>%s
+</error>', $precondition, $explanation );
+
+    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+    exit(0);  // Unecessary, but might clarify things
+  }
+
+
+  /**
+  * Send a simple error informing the client that was a malformed request
+  *
+  * @param string $text An optional text description of the failure.
+  */
+  function MalformedRequest( $text = 'Bad request' ) {
+    $this->DoResponse( 400, $text );
+    exit(0);  // Unecessary, but might clarify things
+  }
+
+
+  /**
+  * Send an XML Response.  This function will never return.
+  *
+  * @param int $status The HTTP status to respond
+  * @param XMLElement $xmltree An XMLElement tree to be rendered
+  */
+  function XMLResponse( $status, $xmltree ) {
+    $xmldoc = $xmltree->Render(0,'<?xml version="1.0" encoding="ISO-8859-1" ?>');
+    $etag = md5($xmldoc);
+    header("ETag: \"$etag\"");
+    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+    exit(0);  // Unecessary, but might clarify things
+  }
+
+  /**
+  * Utility function we call when we have a simple status-based response to
+  * return to the client.  Possibly
+  *
+  * @param int $status The HTTP status code to send.
+  * @param string $message The friendly text message to send with the response.
+  */
+  function DoResponse( $status, $message="", $content_type="text/plain; charset=\"utf-8\"" ) {
+    global $session, $c;
+    @header( sprintf("HTTP/1.1 %d %s", $status, getStatusMessage($status)) );
+    @header( sprintf("X-DAViCal-Version: DAViCal/%d.%d.%d; DB/%d.%d.%d", $c->code_major, $c->code_minor, $c->code_patch, $c->schema_major, $c->schema_minor, $c->schema_patch) );
+    @header( "Content-type: ".$content_type );
+
+    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['response']) && $c->dbg['response']) || $status > 399 ) {
+      $lines = headers_list();
+      dbg_error_log( "LOG ", "***************** Response Header ****************" );
+      foreach( $lines AS $v ) {
+        dbg_error_log( "LOG headers", "-->%s", $v );
+      }
+      dbg_error_log( "LOG ", "******************** Response ********************" );
+      // Log the request in all it's gory detail.
+      $lines = preg_split( '#[\r\n]+#', $message);
+      foreach( $lines AS $v ) {
+        dbg_error_log( "LOG response", "-->%s", $v );
+      }
+    }
+
+    header( "Content-Length: ".strlen($message) );
+    echo $message;
+
+    if ( isset($c->dbg['caldav']) && $c->dbg['caldav'] ) {
+      if ( strlen($message) > 100 || strstr($message, "\n") ) {
+        $message = substr( preg_replace("#\s+#m", ' ', $message ), 0, 100) . (strlen($message) > 100 ? "..." : "");
+      }
+
+      dbg_error_log("caldav", "Status: %d, Message: %s, User: %d, Path: %s", $status, $message, $session->user_no, $this->path);
+    }
+    if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+      $script_time = microtime(true) - $c->script_start_time;
+      @dbg_error_log("statistics", "Method: %s, Status: %d, Script: %5.3lfs, Queries: %5.3lfs, URL: %s",
+                         $this->method, $status, $script_time, $c->total_query_time, $this->path);
+    }
+
+    exit(0);
+  }
+
+}
+
Index: contrib/davical/inc/caldav-REPORT.php
===================================================================
--- contrib/davical/inc/caldav-REPORT.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT.php	(revision 3733)
@@ -0,0 +1,990 @@
+<?php
+/**
+* CalDAV Server - handle REPORT method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+dbg_error_log("REPORT", "method handler");
+
+require_once("XMLDocument.php");
+require_once('DAVResource.php');
+include_once("drivers_ldap.php"); 
+require_once('RRule-v2.php');
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
+  $fh = fopen('/tmp/REPORT.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+if ( !isset($request->xml_tags) ) {
+  $request->DoResponse( 406, translate("REPORT body contains no XML data!") );
+}
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+if ( !is_object($xmltree) ) {
+  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+}
+
+$target = new DAVResource($request->path);
+$target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
+
+if ( $xmltree->GetTag() != 'DAV::principal-property-search'
+                && $xmltree->GetTag() != 'DAV::principal-property-search-set' ) {
+  $target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
+}
+
+require_once("iCalendar.php");
+
+$reportnum = -1;
+$report = array();
+$denied = array();
+$unsupported = array();
+if ( isset($prop_filter) ) unset($prop_filter);
+
+if ( $xmltree->GetTag() == 'urn:ietf:params:xml:ns:caldav:free-busy-query' ) {
+  include("caldav-REPORT-freebusy.php");
+  exit; // Not that the above include should return anyway
+}
+
+$reply = new XMLDocument( array( "DAV:" => "" ) );
+switch( $xmltree->GetTag() ) {
+  case 'DAV::principal-property-search':
+    include("caldav-REPORT-principal.php");
+    exit; // Not that it should return anyway.
+  case 'DAV::principal-search-property-set':
+    include("caldav-REPORT-pps-set.php");
+    exit; // Not that it should return anyway.
+  case 'DAV::sync-collection':
+    include("caldav-REPORT-sync-collection.php");
+    exit; // Not that it should return anyway.
+  case 'DAV::expand-property':
+    include("caldav-REPORT-expand-property.php");
+    exit; // Not that it should return anyway.
+}
+
+
+/**
+* Return XML for a single calendar (or todo) entry from the DB
+*
+* @param array $properties The properties for this calendar
+* @param string $item The calendar data for this calendar
+*
+* @return string An XML document which is the response for the calendar
+******************************** FunÃ§Ã£o Customizada para o Expresso ********************  
+//
+//function calendar_to_xml( $properties, $item ) {
+//  global $session, $c, $request, $reply;
+//
+// dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+//
+//  $denied = array();
+//  $caldav_data = $item->caldav_data;
+//  $displayname = $item->summary;
+//  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+//    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+//      // the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
+//      /** @todo We should examine the ORGANIZER and ATTENDEE fields in the event.  If this person is there then they should see this */
+//      if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+//        $ical = new iCalComponent( $caldav_data );
+//        $resources = $ical->GetComponents('VTIMEZONE',false);
+//        $first = $resources[0];
+//
+//        // if the event is confidential we fake one that just says "Busy"
+//        $confidential = new iCalComponent();
+//        $confidential->SetType($first->GetType());
+//        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+//        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+//        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+//        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+//        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+//        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+//        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+//        $ical->SetComponents(array($confidential),$confidential->GetType());
+//
+//        $caldav_data = $ical->Render();
+//        $displayname = translate('Busy');
+//      }
+//    }
+//  }
+//
+//  $url = ConstructURL($item->dav_name);
+//
+//  $prop = new XMLElement("prop");
+//  foreach( $properties AS $k => $v ) {
+//    switch( $k ) {
+//      case 'getcontentlength':
+//        $contentlength = strlen($caldav_data);
+//        $prop->NewElement($k, $contentlength );
+//        break;
+//      case 'calendar-data':
+//        $reply->CalDAVElement($prop, $k, $caldav_data );
+//        break;
+//      case 'getcontenttype':
+//        $prop->NewElement($k, "text/calendar" );
+//        break;
+//      case 'current-user-principal':
+//        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+//        break;
+//     case 'displayname':
+//        $prop->NewElement($k, $displayname );
+//        break;
+//      case 'resourcetype':
+//        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+//        break;
+//      case 'getetag':
+//        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+//        break;
+//      case '"current-user-privilege-set"':
+//        $prop->NewElement($k, privileges($request->permissions) );
+//        break;
+//      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+//        $denied[] = $v;
+//        break;
+//      default:
+//        dbg_error_log( 'REPORT', "Request for unsupported property '%s' of calendar item.", $v );
+//        $unsupported[] = $v;
+//   }
+//  }
+//  $status = new XMLElement("status", "HTTP/1.1 200 OK" );
+//
+//  $propstat = new XMLElement( "propstat", array( $prop, $status) );
+//  $href = new XMLElement("href", $url );
+//  $elements = array($href,$propstat);
+//
+//  if ( count($denied) > 0 ) {
+//    $status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
+//    $noprop = new XMLElement("prop");
+//    foreach( $denied AS $k => $v ) {
+//      $noprop->NewElement( strtolower($v) );
+//    }
+//    $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
+//  }
+//
+//  $response = new XMLElement( "response", $elements );
+//
+//  return $response;
+//}
+//
+//*/
+/************* FunÃ§Ã£o nova Customizada *************
+*/
+function contacts_to_xml( $properties, $item)
+{
+  global $session, $c, $request, $reply;
+  $path = preg_replace("/^\//","",$request->path);
+  //$filtro = "uid=".$path;
+  //$atributos = array("uidNumber");
+  //$uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+  //if ($uidnumber == false) {
+  //             dbg_error_log( "REPORT", "Responding with report error:  usuario nÃ£o encontrado no diretorio");
+  //             $request->DoResponse( 501, 'Database error');
+  //}
+  // $nome = $uidnumber['uidNumber'];
+  // $nome = $target->GetProperty('user_no');
+   $nome = $item->id_owner; 
+   $mails = array();
+   $phones = array();
+   unset($VCARD);
+   $VCARD = "UID:$item->id_contact@$nome\n\n";
+    
+   /***************************
+   ********* DADOS DEFAULT ****
+   ****************************/
+   $VCARD .= "N:$item->family_names;$item->given_names\n\n";
+   $VCARD .= "FN:$item->names_ordered\n\n";
+   if ( isset($item->alias)) $VCARD .= "NICKNAME:$item->alias\n\n";
+   if ( isset($item->photo)) $VCARD .= "PHOTO:ENCODING=b;TYPE=JPEG:$item->photo\n\n";
+   if ( isset($item->birthdate)) $VCARD .= "BDAY:$item->birthdate\n\n";
+   if ( isset($item->notes)) $VCARD .= "NOTE:$item->notes\n\n";
+   /**************************
+   ********** EMAIL & TEL ****
+   ***************************/
+    $qry = new AwlQuery("SELECT * FROM phpgw_cc_contact_conns  inner join phpgw_cc_connections using(id_connection) WHERE  phpgw_cc_contact_conns.id_contact= :id", array(':id' => $item->id_contact));
+    if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ){
+      while( $email_tel = $qry->Fetch() ) {
+          if ( $email_tel->id_typeof_contact_connection == 1)
+             {
+               if ( $email_tel->connection_name == 'Principal' )
+                  $type=WORK;
+               else
+                  $type=HOME;
+               $VCARD .= "EMAIL;TYPE=$type:$email_tel->connection_value\n\n";
+              }
+          else if ( $email_tel->id_typeof_contact_connection == 2)
+             { 
+                if ( $email_tel->connection_name == 'Trabalho' )
+                  $type=WORK;
+               else if ( $email_tel->connection_name == 'Casa' )
+                  $type=HOME;
+               else if ( $email_tel->connection_name == 'Celular' )
+                  $type=CELL;
+               else if ( $email_tel->connection_name == 'Fax' )
+                  $type=FAX;
+
+              $VCARD .= "TEL;TYPE=$type:$email_tel->connection_value\n\n";
+              }    
+      } 
+    } 
+   /**************************
+    ************ ADDR ********
+    **************************/
+    $qry = new AwlQuery("SELECT * FROM phpgw_cc_contact_addrs  inner join phpgw_cc_addresses  using(id_address) WHERE  phpgw_cc_contact_addrs.id_contact= :id", array(':id' => $item->id_contact));
+    if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ){
+      while( $addr = $qry->Fetch() ) {
+          if ( $addr->id_typeof_contact_address == 1)
+             {
+               $type=HOME;
+               $VCARD .= "ADR;TYPE=$type:;;$addr->address1;$addr->city;$addr->state;$addr->postal_code;$addr->id_contry\n\n";
+              }
+          else if ( $addr->id_typeof_contact_address == 2)
+             {
+               $type=WORK;
+               $VCARD .= "ADR;TYPE=$type:;;$addr->address1;$addr->city;$addr->state;$addr->postal_code;$addr->id_contry\n\n";
+              }
+      }
+    }    
+   
+
+   /********** PUBLIC KEY ********
+    *****************************/ 
+
+   
+
+
+   dbg_error_log("REPORT","Building XML Response for item '%s' usuario  '%s'", $item->id_contact,$nome );
+   $response = "BEGIN:VCARD\n\nVERSION:3.0\n\nPRODID:-//Inverse inc.//SOGo Connector 1.0//EN\n\n";
+   $response .= "$VCARD";
+   $response .= "END:VCARD\n\n";
+   $caldav_data = $response;
+   
+   $dav_nam = "{$item->id_contact}@{$item->id_owner}";
+   $dav_etag_nam = md5($item->id_contact.$item->id_owner.$item->last_update);
+   //$dav_name = "{$item->id_contact}@{$item->id_owner}";
+   $dav_name = sprintf('/%s/%s@%s.vcf',$path,$item->id_contact,$item->id_owner);
+   $url = ConstructURL($dav_name); 
+  
+   $prop = new XMLElement("prop");                                                                                                      
+   foreach( $properties AS $k => $v ) {                                                                                                 
+    switch( $k ) {                                                                                                                     
+      case 'getcontentlength':                                                                                                         
+        $contentlength = strlen($caldav_data);                                                                                         
+        $prop->NewElement($k, $contentlength );                                                                                        
+        break;                                                                                                                         
+      case 'address-data':                                                                                                            
+        $reply->CardDAVElement($prop, $k, $caldav_data );                                                                               
+        break;                                                                                                                         
+      case 'getcontenttype':                                                                                                           
+        $prop->NewElement($k, "text/x-vcard" );                                                                                       
+        break;                                                                                                                         
+      case 'current-user-principal':                                                                                                   
+        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);                                             
+        break;                                                                                                                         
+     case 'displayname':                                                                                                               
+        $prop->NewElement($k, $displayname );                                                                                          
+        break;                                                                                                                         
+      case 'resourcetype':                                                                                                             
+        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.                                                     
+        break;                                                                                                                         
+      case 'getetag':                                                                                                                  
+        $prop->NewElement($k, '"'.$dav_etag_nam.'"' );                                                                               
+        break;                                                                                                                         
+      case '"current-user-privilege-set"':                                                                                             
+        $prop->NewElement($k, privileges($request->permissions) );                                                                     
+        break;                                                                                                                         
+      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */                                                   
+        $denied[] = $v;                                                                                                                
+        break;                                                                                                                         
+      default:                                                                                                                         
+        dbg_error_log( 'REPORT', "Request for unsupported property '%s' of calendar item.", $v );                                      
+        $unsupported[] = $v;                                                                                                           
+   }                                                                                                                                   
+  }  
+
+   $status = new XMLElement("status", "HTTP/1.1 200 OK" );
+   $propstat = new XMLElement( "propstat", array( $prop, $status) );
+   $href = new XMLElement("href", $url );
+   $elements = array($href,$propstat);
+   if ( count($denied) > 0 ) {
+                $status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
+                $noprop = new XMLElement("prop");
+                foreach( $denied AS $k => $v ) {
+                $noprop->NewElement( strtolower($v) );
+          }
+     $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
+    }
+
+
+ $response = new XMLElement( "response", $elements );
+ return $response;     
+
+}
+function calendar_to_xml( $properties, $item ) {                        
+        global $c, $request, $reply;
+        $path = preg_replace("/^\//","",$request->path);                                   
+        //$filtro = "uid=".$path;
+        //$atributos = array("uidNumber");
+        //$uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+        //if ($uidnumber == false) {
+        //         dbg_error_log( "REPORT", "Responding with report error:  usuario nÃ£o encontrado no diretorio");
+        //         $request->DoResponse( 501, 'Database error');
+        //}  
+        //$nome = $uidnumber['uidNumber'];
+        $nome = $item->owner;                          
+        dbg_error_log("REPORT","Building XML Response for item '%s' usuario  '%s'", $item->cal_id,$nome );
+        $mdate = $item->mdatetime;
+        $calid = $item->cal_id;
+        $modify_data = dataSegundosParaT($mdate,1);
+        $date = $item->datetime;
+        $create_data = dataSegundosParaT($date,1);
+        $start_data = dataSegundosParaT($date);
+        $edate = $item->edatetime;
+        $end_data = dataSegundosParaT($edate);
+        $titulo = utf8_encode($item->title);
+        $local = utf8_encode($item->location);
+        if($local != "" )
+              $local = "\n\nLOCATION:$local";
+        else
+              $local = "";                     
+        $descricao = utf8_encode($item->description);
+        if($descricao != "")
+              $descricao = "\n\nDESCRIPTION:".preg_replace('/\r\n/', '\\n', $descricao);
+        else
+              $descricao = "";
+        $moz="";
+        
+        /*******************
+         **** Sequencia ****
+         ******************/
+       
+        $sequec = "\n\nSEQUENCE:0";
+  
+        /*********
+         *ALARMES*
+         *********/
+        $alarme_id = $item->cal_id;
+        $alarme = new AwlQuery( "SELECT * FROM phpgw_async WHERE id = :alarme_id", array(':alarme_id' => 'cal:'.$alarme_id.':0'));
+        if ( $alarme->Exec("REPORT",__LINE__,__FILE__) && $alarme->rows() > 0 ){          
+                $nao_alarme = new AwlQuery( "SELECT * FROM phpgw_cal_extra WHERE  cal_id = :alarme_id AND (cal_extra_name = 'X-MOZ-LASTACK' OR cal_extra_name = :X-MOZ-LASTACK-PART)",array( ':alarme_id' => $alarme_id, ':X-MOZ-LASTACK-PART' => 'X-MOZ-LASTACK-PART-'.$nome));
+                if ( $nao_alarme->Exec("REPORT",__LINE__,__FILE__) && $nao_alarme->rows() == 1 ){                                                                                               
+                        $alarmeqryno = $nao_alarme->Fetch();                                                                                                                                   
+                        $moz = "\n\nX-MOZ-LASTACK:$alarmeqryno->cal_extra_value";
+                        $moz .= "\n\nX-MOZ-GENERATION:1";
+                        $modify_data = "$alarmeqryno->cal_extra_value";
+                        $adiamento = new AwlQuery( "SELECT * FROM phpgw_cal_extra WHERE  cal_id = :alarme_id AND cal_extra_name LIKE 'X-MOZ-SNOOZE%'",  array( ':alarme_id' => $alarme_id));                                       
+                        if ( $adiamento->Exec("REPORT",__LINE__,__FILE__) && $adiamento->rows() > 0 ){                                                                                         
+                             $admno = $adiamento->Fetch();                                                                                                                                     
+                            $moz .= "\n\n$admno->cal_extra_name:$admno->cal_extra_value";                                                                                                      
+                        }  
+                        dbg_error_log("REPORT","CANCELAR ALARME '%s' ", $moz);
+                                                                                                                                                                    
+                        //$deleta = new AwlQuery( "DELETE FROM phpgw_cal_extra  WHERE cal_id = '$alarme_id'");                                                                                  
+                        //$deleta->Exec("calquery",__LINE__,__FILE__);                                                                                                                         
+                                                                                                                                                                                               
+                }                                                                                                                                                                              
+                else{                                                                                                                                                                          
+                        $moz="";                                                                                                                                                               
+                }                                                                                                                                                                              
+                
+                $alarmeqry = $alarme->Fetch();
+                $tempo = split(";", $alarmeqry->data, 4);
+                $tempoalarme = $alarmeqry->next;// + $mais;
+                $agoranow = date_create();                 
+                $agora  = $agoranow->format("U");          
+                $segundos = split(":", $tempo[3], 2);      
+                if ( $tempoalarme >= $agora | $item->cal_type == 'M'){
+                        if ( $segundos[1] <= 3600 ){                  
+                                $minutos = $segundos[1]/60;           
+                                $VALARME = "\n\nBEGIN:VALARM\n\nTRIGGER;VALUE=DURATION:-PT${minutos}M\n\nDESCRIPTION:DescriÃ§Ã£o padrÃ£o Mozilla\n\nACTION:DISPLAY\n\nEND:VALARM";
+                        }                                                                                                                                                   
+                        else if ( $segundos[1] < 86400   ){                                                                                                                 
+                                $hora = $segundos[1]/60/60;                                                                                                                 
+                                $VALARME = "\n\nBEGIN:VALARM\n\nTRIGGER;VALUE=DURATION:-PT${hora}H\n\nDESCRIPTION:DescriÃ§Ã£o padrÃ£o Mozilla\n\nACTION:DISPLAY\n\nEND:VALARM";   
+                        }                                                                                                                                                   
+                        else if ( $segundos[1] == 604800 ){                                                                                                                 
+                                $VALARME = "\n\nBEGIN:VALARM\n\nTRIGGER;VALUE=DURATION:-P1W\n\nDESCRIPTION:DescriÃ§Ã£o padrÃ£o Mozilla\n\nACTION:DISPLAY\n\nEND:VALARM";          
+                        }                                                                                                                                                   
+                        else{                                                                                                                                               
+                                $dia = $segundos[1]/60/60/24;                                                                                                               
+                                $VALARME = "\n\nBEGIN:VALARM\n\nTRIGGER;VALUE=DURATION:-P${dia}D\n\nDESCRIPTION:DescriÃ§Ã£o padrÃ£o Mozilla\n\nACTION:DISPLAY\n\nEND:VALARM";     
+                        }                                                                                                                                                   
+                }                                                                                                                                                           
+                else{                                                                                                                                                       
+                        $VALARME ="";                                                                                                                                       
+                }                                                                                                                                                           
+        }                                                                                                                                                                   
+        else{                                                                                                                                                               
+                $VALARME ="";                                                                                                                                               
+        }                                                                                                                                                                   
+        //dbg_error_log("GET", "HORA LOCAL  = '%s'",$VALARME);                                                                                                              
+                                                                                                                                                                            
+        /*****************************************                                                                                                                          
+        **** Ajuste para TZ funcionar ************                                                                                                                          
+        *****************************************/                                                                                                                          
+
+        //$HorarioTimeZone = new DateTimeZone(date_default_timezone_get()); 
+        ///$create_data = dataSegundosParaT($item->datetime);               
+        //$retorna = date("Ymd\THis",$create_data);                         
+                                                                            
+
+
+
+
+
+
+
+        /*******************
+         *NOME DA CATEGORIA*
+         *******************/
+        $categories="";
+        //if (strlen($item->category)>2){
+        if ($item->category != "" ){     
+                $x= "select cat_name from phpgw_categories WHERE cat_id = :category";
+                $phpgw_query = new AwlQuery ($x,  array(':category' => $item->category));                                           
+                if ( $phpgw_query->Exec() && $phpgw_query->rows() > 0 ) {                    
+                        while( $id_cale = $phpgw_query->Fetch() ) {                        
+                                 $categories = "\n\nCATEGORIES:".utf8_encode($id_cale->cat_name);             
+                               //$categories = $id_cale->cat_name;                         
+                        }                                                                  
+                }                                                                          
+        }                                                                                  
+        $displayname = $item->title;                                                       
+
+        /**********
+         *CONVITES*
+         **********/
+
+        $cn = array();
+        $mail = array();
+        $estado = array();
+        $members = "";    
+        unset($cn);       
+        unset($mail);     
+        unset($estado);   
+        unset($envia_convite);
+        unset($mails);        
+        $part2= "SELECT cal_login,cal_status FROM phpgw_cal_user WHERE cal_id = :cal_id  AND cal_type = 'u'";
+        $querypart = new AwlQuery( $part2 , array(':cal_id' => $item->cal_id));                                                                         
+        $i=0;                                                                                                       
+        if ( $querypart->Exec("REPORT",__LINE__,__FILE__) && $querypart->rows() > 0 ) {                             
+                while( $id_cale = $querypart->Fetch() ) {                                                           
+                        if ($item->owner != $id_cale->cal_login){                                                   
+                                $filtro = "uidNumber=$id_cale->cal_login" ;                                         
+                                if (strlen($filtro) > 12){                                                          
+                                        $atributos=array("cn","mail");                                              
+                                        $usuario=  ldapDrivers::requestAtributo($filtro,$atributos);                
+                                        if (isset($usuario))                                                        
+                                        {                                                                           
+                                                $cn[$i] = $usuario['cn'];                                           
+                                                $mail[$i] = $usuario['mail'];                                       
+                                                switch ($id_cale->cal_status){                                      
+                                                        case 'A':                                                   
+                                                                $estado[$i] = "ACCEPTED";                           
+                                                                        break;                                      
+                                                        case 'T':                                                   
+                                                                $estado[$i] = "TENTATIVE";                          
+                                                                break;                                              
+                                                        case 'U':                                                   
+                                                                $estado[$i] = "NEEDS-ACTION";                       
+                                                                break;                                              
+                                                        case 'R':                                                   
+                                                                $estado[$i] = "DECLINED";                           
+                                                                break;                                              
+                                                }                                                                   
+                                        }                                                                           
+                                }                                                                                   
+                        }                                                                                           
+                        $i++;                                                                                       
+                }                                                                                                   
+        }                                                                                                           
+
+        if ( isset($item->ex_participants))
+        {                                  
+                $mails = split("[?,]",$item->ex_participants);
+        }                                                     
+        for ($i = 0; $i <= count($cn); $i++) {                
+                if (strlen($mail[$i])>5){                     
+                        $members = $members."\nATTENDEE;RSVP=TRUE;CN=$cn[$i];PARTSTAT=$estado[$i];ROLE=REQ-PARTICIPANT:MAILTO:$mail[$i]\r";
+                }                                                                                                                          
+                dbg_error_log("REPORT", "participantes interno funcao report $members");                                              
+        }                                                                                                                                  
+        for ($i = 0 ; $i <= count($mails); $i++) {                                                                                         
+                if ( $mails[$i] != '' ){                                                                                                   
+                        dbg_error_log("REPORT", "participante externo  = '%s'",$mails[$i]);                                           
+                        $members = $members."\nATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:MAILTO:$mails[$i]\r";         
+                }                                                                                                                          
+        }                                                                                                                                  
+
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);            
+        $convite = new AwlQuery( "SELECT cal_extra_value FROM phpgw_cal_extra WHERE cal_id = :cal_id", array(':cal_id' => $item->cal_id));
+        if ( $convite->Exec("REPORT",__LINE__,__FILE__) && $convite->rows() > 0 ) {                           
+                $xmozconvite = $convite->Fetch();
+                dbg_error_log("REPORT", "estatus de ivio de convite  = '%s'",$xmozconvite->cal_extra_value);
+                if ($xmozconvite->cal_extra_value == "true"){                                                 
+                         $envia_convite = "X-MOZ-SEND-INVITATIONS:TRUE";                                      
+                            $convite = new AwlQuery( "DELETE FROM phpgw_cal_extra WHERE cal_id = :cal_id", array(':cal_id' => $item->cal_id));
+                            $convite->Exec("REPORT",__LINE__,__FILE__);                                         
+                }else{      
+                            $envia_convite = "X-MOZ-SEND-INVITATIONS:FALSE";
+                }                                                                                                 
+        }                                                                                                         
+                                                                                                                  
+
+        /*********
+         *PUBLICO*
+         *********/
+
+        if ( $item->is_public == 0 ){
+                $classe = "PRIVATE";
+                $transp = "\n\nTRANSP:TRANSPARENT";  
+        }                            
+        else{                        
+                $classe = "PUBLIC";  
+                $transp = "\n\nTRANSP:OPAQUE";  
+ 
+       }                            
+                                     
+        /*******************         
+         *EVENTOS REPETIDOS*         
+         *******************/        
+        unset($Rrule); 
+        if ( $item->cal_type == 'M' ){
+                /**Busca exceÃ§Ãµes de eventos repetidos, quando sÃ£o criados novos eventos a partir de uma repetiÃ§Ã£o
+                 * Para ter funcionamento igual ao expresso, escondo o evento repetido.                           
+                 * Essa funÃ§Ã£o apenas adiciona o horÃ¡rio dessa funÃ§Ã£o ao campo EXDATE.                            
+                 */                                                                                               
+                if ( $item->recur_exception != ''){                                                               
+                        $listadeexcecao = array();                                                                
+                        $listadeexcecao = explode("," , $item->recur_exception);                                  
+                        foreach ($listadeexcecao AS $dataexcecao){                                                
+                                $execao = dataSegundosParaT($dataexcecao,1);                                      
+                                $exe = "$exe,$execao";                                                            
+                        }                                                                                         
+                }                                                                                                 
+                $exe = trim($exe, " ,");                                                                          
+                $x= "select datetime from phpgw_cal where reference = :cal_id";                             
+                $phpgw_query = new AwlQuery ($x, array(':cal_id' => $item->cal_id));                                                                  
+                if ( $phpgw_query->Exec() && $phpgw_query->rows() > 0 ) {                                           
+                        while( $id_cale = $phpgw_query->Fetch() ) {                                               
+                                date_default_timezone_set('UTC');                                                 
+                                $data_antiga = date("Ymd\THis",$item->datetime);                                  
+                                $data_nova = date("Ymd\THis",$id_cale->datetime);                                 
+                                date_default_timezone_set('America/Sao_Paulo');                                   
+                                $data_n = new DateTime($data_nova);                                               
+                                $data_a = new DateTime($data_antiga);                                             
+                                if ( $data_n->getOffset() > $data_a->getOffset() )                                
+                                       {                                                                          
+                                        $dias = $data_n->format('z') - $data_a->format('z');                      
+                                        $dias = $item->datetime + ($dias*86400) - 3600;                           
+                                        $data2 = dataSegundosParaT($dias,1);                                      
+                                        $exe = $exe . ",". $data2;                                                
+                                                                                                                  
+                                      }                                                                           
+                                elseif ( $data_n->getOffset() < $data_a->getOffset() )                            
+                                      {                                                                           
+                                        $dias = $data_n->format('z') - $data_a->format('z');                      
+                                        $dias = $item->datetime + ($dias*86400) + 3600;                           
+                                        $data2 = dataSegundosParaT($dias,1);                                      
+                                        $exe = $exe . ",". $data2;                                                
+
+                                      } 
+                                 else   
+                                      { 
+                                        $propstat = new XMLElement( "propstat", array( $prop, $status) );
+                                        $dias = $data_n->format('z') - $data_a->format('z');
+                                        $dias = $item->datetime + ($dias*86400);            
+                                        $data2 = dataSegundosParaT($dias,1);                
+                                        $exe = $exe . ",". $data2;                          
+                                    }                                                       
+                                                                                            
+                        }                                                                   
+                }                                                                           
+                $exe = trim($exe, " ,");                                                    
+                $exe = "\n\nEXDATE:$exe";                                                   
+
+                if (  $item->recur_enddate != 0){
+
+                        $until = dataSegundosParaT($item->recur_enddate,1);
+                        if (substr("$until", 9,2) == "03" ) {              
+                        $until = dataSegundosParaT($item->recur_enddate + 86400,1);
+                        }                                                          
+                }                                                                  
+               
+                $intervalo = $item->recur_interval;
+                switch($item->recur_type){         
+                        case 1:                    
+                                $frequencia = "DAILY";
+                                $Rrule = "FREQ=$frequencia";
+                                if ($until != ""){          
+                                        $Rrule .= ";UNTIL=$until";
+                                }                                 
+                                if ($intervalo != ""){            
+                                        $Rrule .= ";INTERVAL=$intervalo";
+                                }                                        
+                                break;                                   
+                        case 2:                                          
+                                $frequencia = "WEEKLY";                  
+                                $dia_sem = converte_semana($item->recur_data);
+                                $Rrule = "FREQ=$frequencia";                  
+                                if ($until != ""){                            
+                                        $Rrule .= ";UNTIL=$until";            
+                                }                                             
+                                if ($intervalo !=""){                         
+                                        $Rrule .= ";INTERVAL=$intervalo";     
+                                }                                             
+                                if($item->recur_data != ""){                  
+                                        $Rrule .= ";$dia_sem";                
+                                }                                             
+                                break;                                        
+                        case 3:                                               
+                                $frequencia = "MONTHLY";                      
+                                $Rrule = "FREQ=$frequencia";                  
+                                if ($until != ""){                            
+                                        $Rrule .= ";UNTIL=$until";            
+                                }                                             
+                                if ($intervalo !=""){                         
+                                        $Rrule .= ";INTERVAL=$intervalo";     
+                                }                                             
+                                break;                                        
+                        case 4:                                               
+                                $frequencia = "MONTHLY";                      
+                                $Rrule = "FREQ=$frequencia";                  
+                                if ($until != ""){                            
+                                        $Rrule .= ";UNTIL=$until";            
+                                }                                             
+                                if ($intervalo !=""){                         
+                                        $Rrule .= ";INTERVAL=$intervalo";     
+                                }                                             
+                                break;                                        
+                        case 5:                                               
+                                $frequencia = "YEARLY";                       
+                                $Rrule = "FREQ=$frequencia";                  
+                                if ($until != ""){                            
+                                        $Rrule .= ";UNTIL=$until";            
+                                }                                             
+                                if ($intervalo !=""){                         
+                                        $Rrule .= ";INTERVAL=$intervalo";     
+                                }                                             
+                                break;                                        
+                }                                                             
+
+        }
+        if(isset($Rrule))
+           $Rrule = "\n\nRRULE:$Rrule";
+         else
+           $Rrule = ""; 
+        $response = "BEGIN:VCALENDAR\n\nPRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\n\nVERSION:2.0";
+        $response .= "\n\nBEGIN:VTIMEZONE
+TZID:America/Sao_Paulo
+X-LIC-LOCATION:America/Sao_Paulo
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0300
+TZOFFSETTO:-0200
+TZNAME:BRST
+DTSTART:19701018T000000
+RRULE:FREQ=YEARLY;BYDAY=3SU;BYMONTH=10
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0300
+TZOFFSETTO:-0300
+TZNAME:BRT
+DTSTART:19700215T000000
+RRULE:FREQ=YEARLY;BYDAY=3SU;BYMONTH=2
+END:STANDARD
+END:VTIMEZONE";
+        $response .= "\n\nBEGIN:VEVENT\n\nCREATED:$create_data\n\nLAST-MODIFIED:$modify_data\n\nDTSTAMP:$modify_data\n\nUID:$item->cal_id@$item->owner\n\n${members}\n\nSUMMARY:$titulo\n\n$recurrence_id\n\nDTSTART;TZID=America/Sao_Paulo:$start_data\n\nDTEND;TZID=America/Sao_Paulo:${end_data}${local}${categories}\r\nPRIORITY:$item->priority${descricao}${Rrule}${exe}${moz}$VALARME\n\n$envia_convite\n\nEND:VEVENT\n\n";                                                                                                                                                                               
+        $response .= "END:VCALENDAR\n\n\n";                                                                                                                                                                                                
+        $caldav_data = $response;                                                                                                                                                                                                            
+
+  
+        $dav_nam = "{$item->cal_id}{$item->owner}";
+        $dav_etag_nam= md5($item->cal_id.$item->owner.$item->last_update);
+        //$dav_name = "{$item->cal_id}@{$item->owner}@{$item->last_update}";
+        $dav_name = sprintf('/%s/%s@%s.ics',$path,$item->cal_id,$item->owner);
+        $url = ConstructURL($dav_name);                                        
+        $prop = new XMLElement("prop");
+        foreach( $properties AS $k => $v ) {
+                switch( $k ) {              
+				 case 'getcontentlength':
+       					 $contentlength = strlen($caldav_data);
+      				         $prop->NewElement($k, $contentlength );
+     					 break;
+     				 case 'calendar-data':
+       					 $reply->CalDAVElement($prop, $k, $caldav_data );
+      				 	 break;
+    				 case 'getcontenttype':
+        			 	 $prop->NewElement($k, "text/calendar" );
+       				 	 break;
+     				 case 'current-user-principal':
+       					 $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+       					 break;
+     				 case 'displayname':
+       					 $prop->NewElement($k, $displayname );
+       					 break;
+     				 case 'resourcetype':
+       					 $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+      					 break;
+     				 case 'getetag':
+       					 $prop->NewElement($k,  '"'.$dav_etag_nam.'"' );
+       					 break;
+     				 case '"current-user-privilege-set"':
+       					 $prop->NewElement($k, privileges($request->permissions) );
+       					 break;
+     				 case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+       					 $denied[] = $v;
+       					 break;
+        		         default:
+                                         dbg_error_log( 'REPORT', "Request for unsupported property '%s' of calendar item.", $v );
+                                         $unsupported[] = $v;
+                }                                                                                                                                        
+        }   
+
+        $status = new XMLElement("status", "HTTP/1.1 200 OK" );
+        $propstat = new XMLElement( "propstat", array( $prop, $status) );
+        $href = new XMLElement("href", $url );                           
+        $elements = array($href,$propstat);                              
+
+        if ( count($denied) > 0 ) {
+                $status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
+                $noprop = new XMLElement("prop");                             
+                foreach( $denied AS $k => $v ) {                              
+                $noprop->NewElement( strtolower($v) );                        
+        }                                                                     
+        $elements[] = new XMLElement( "propstat", array( $noprop, $status) ); 
+        }                                                                             
+
+  $response = new XMLElement( "response", $elements );
+
+  return $response;
+} 
+
+
+/**
+* Return XML for a single component from the DB
+*
+* @param array $properties The properties for this component
+* @param string $item The DB row data for this component
+*
+* @return string An XML document which is the response for the component
+*/
+
+function component_to_xml( $properties, $item ) {
+  global $session, $c, $request, $reply;
+
+  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+
+  $denied = array();
+  $unsupported = array();
+  $caldav_data = $item->caldav_data;
+  $displayname = preg_replace( '{^.*/}', '', $item->dav_name );
+  $type = 'unknown';
+  $contenttype = 'text/plain';
+  switch( $item->caldav_type ) {
+    case 'VJOURNAL':
+    case 'VEVENT':
+    case 'VTODO':
+      $displayname = $item->summary;
+      $type = 'calendar';
+      $contenttype = 'text/calendar';
+      break;
+
+    case 'VCARD':
+      $displayname = $item->fn;
+      $type = 'vcard';
+      $contenttype = 'text/x-vcard';
+      break;
+  }
+  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+      // the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
+      /** @todo We should examine the ORGANIZER and ATTENDEE fields in the event.  If this person is there then they should see this */
+      if ( $type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+        $ical = new iCalComponent( $caldav_data );
+        $resources = $ical->GetComponents('VTIMEZONE',false);
+        $first = $resources[0];
+
+        // if the event is confidential we fake one that just says "Busy"
+        $confidential = new iCalComponent();
+        $confidential->SetType($first->GetType());
+        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+        $ical->SetComponents(array($confidential),$confidential->GetType());
+
+        $caldav_data = $ical->Render();
+        $displayname = translate('Busy');
+      }
+    }
+  }
+
+  $url = ConstructURL($item->dav_name);
+
+  $prop = new XMLElement("prop");
+  foreach( $properties AS $k => $v ) {
+    switch( $k ) {
+      case 'getcontentlength':
+        $contentlength = strlen($caldav_data);
+        $prop->NewElement($k, $contentlength );
+        break;
+      case 'calendar-data':
+        if ( $type == 'calendar' ) $reply->CalDAVElement($prop, $k, $caldav_data );
+        else $unsupported[] = $k;
+        break;
+      case 'address-data':
+        if ( $type == 'vcard' ) $reply->CardDAVElement($prop, $k, $caldav_data );
+        else $unsupported[] = $k;
+        break;
+      case 'getcontenttype':
+        $prop->NewElement($k, $contenttype );
+        break;
+      case 'current-user-principal':
+        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+        break;
+      case 'displayname':
+        $prop->NewElement($k, $displayname );
+        break;
+      case 'resourcetype':
+        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+        break;
+      case 'getetag':
+        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+        break;
+      case '"current-user-privilege-set"':
+        $prop->NewElement($k, privileges($request->permissions) );
+        break;
+      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+        $denied[] = $k;
+        break;
+      default:
+        dbg_error_log( 'REPORT', "Request for unsupported property '%s' of calendar item.", $v );
+        $unsupported[] = $k;
+    }
+  }
+  $status = new XMLElement("status", "HTTP/1.1 200 OK" );
+
+  $propstat = new XMLElement( "propstat", array( $prop, $status) );
+  $href = new XMLElement("href", $url );
+  $elements = array($href,$propstat);
+
+  if ( count($denied) > 0 ) {
+    $status = new XMLElement("status", "HTTP/1.1 403 Forbidden" );
+    $noprop = new XMLElement("prop");
+    foreach( $denied AS $k => $v ) {
+      $noprop->NewElement( strtolower($v) );
+    }
+    $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
+  }
+
+  if ( count($unsupported) > 0 ) {
+    $status = new XMLElement("status", "HTTP/1.1 404 Not Found" );
+    $noprop = new XMLElement("prop");
+    foreach( $unsupported AS $k => $v ) {
+      $noprop->NewElement( strtolower($v) );
+    }
+    $elements[] = new XMLElement( "propstat", array( $noprop, $status) );
+  }
+
+  $response = new XMLElement( "response", $elements );
+
+  return $response;
+}
+
+if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
+  $calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
+  include("caldav-REPORT-calquery.php");
+}
+elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
+  $mode = 'caldav';
+  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
+  include("caldav-REPORT-multiget.php");
+}
+elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-multiget" ) {
+  $mode = 'carddav';
+  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
+  include("caldav-REPORT-multiget.php");
+}
+elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-query" ) {
+  $cardquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:carddav:addressbook-query/*");
+  include("caldav-REPORT-cardquery.php");
+}
+else {
+  $request->PreconditionFailed( 403, 'DAV::supported-report', sprintf( '"%s" is not a supported report type') );
+}
+
+function converte_semana($sema){                           
+        $retorna = "BYDAY=";                               
+        $param = 1;                                        
+        while( $param <= 64 ){                             
+                $numero = $sema & $param;                  
+                if ( $numero != 0 ){                       
+                        switch ($numero){                  
+                                case 1:                    
+                                        $retorna .= "SU,"; 
+                                        break;             
+                                case 2:
+                                        $retorna .= "MO,";
+                                        break;
+                                case 4:
+                                        $retorna .= "TU,";
+                                        break;
+                                case 8:
+                                        $retorna .= "WE,";
+                                        break;
+                                case 16:
+                                        $retorna .= "TH,";
+                                        break;
+                                case 32:
+                                        $retorna .= "FR,";
+                                        break;
+                                case 64:
+                                        $retorna .= "SA";
+                                        break;
+                                default:
+                                        continue;
+                        }
+                }
+                $param = $param * 2;
+        }
+        $nova = substr($retorna, 0, -1);
+        return $nova;
+}
+
+function dataTzParaSegundos($datatz){
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);
+        return mktime(  substr("$datatz", 9,2),//hora
+        substr("$datatz", 11,2),//min
+        substr("$datatz", 13,2),//segundos
+        substr("$datatz", 4,2),//mes
+        substr("$datatz", 6,2),//dia
+        substr("$datatz", 0,4)) + $HorarioTimeZone->getoffset($HorarioTime);//ano
+}
+
+function dataSegundosParaT($dataseg, $opcao = 0){
+        if ($opcao!=1){
+                $teste = getdate($dataseg);
+                return date("Ymd\THis",$teste[0]);
+        }
+        date_default_timezone_set('UTC');
+        $data_nova = date("Ymd\THis",$dataseg);
+        date_default_timezone_set('America/Sao_Paulo');
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime($data_nova, $HorarioTimeZone);
+        //$teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+        //date_default_timezone_set('UTC');
+        //$retorna = date("Ymd\THis\Z",$dataseg);
+        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+        return date("Ymd\THis\Z",$teste[0]);
+}
+
Index: contrib/davical/inc/drivers_pwauth_pam.php
===================================================================
--- contrib/davical/inc/drivers_pwauth_pam.php	(revision 3733)
+++ contrib/davical/inc/drivers_pwauth_pam.php	(revision 3733)
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Manages PAM repository connection with pwauth
+ *
+ * @package   davical
+ * @category Technical
+ * @subpackage   pwauth
+ * @author    Eric Seigne <eric.seigne@ryxeo.com>,
+ *   Michael B. Trausch <mike@trausch.us>
+ * @copyright Eric Seigne
+ * @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+ *
+ * Based on drivers_squid_pam.php
+ */
+
+require_once("auth-functions.php");
+
+class pwauthPamDrivers
+{
+  /**#@+
+   * @access private
+   */
+
+  /**#@-*/
+
+
+  /**
+   * Constructor.
+   * @param string $config path where pwauth is
+   */
+  function pwauthPamDrivers($config){
+    $this->__construct($config);
+  }
+
+
+  /**
+   * The constructor
+   *
+   * @param string $config path where pwauth is
+   */
+  function __construct($config)
+  {
+    global $c;
+    if(!file_exists($config)) {
+      $c->messages[] =
+	sprintf(i18n('drivers_pwauth_pam : Unable to find %s file'), $config);
+      $this->valid=false;
+      return ;
+    }
+  }
+}
+
+
+/**
+ * Check the username / password against the PAM system
+ */
+function PWAUTH_PAM_check($username, $password) {
+  global $c;
+  $program = $c->authenticate_hook['config']['path'];
+  $email_base = $c->authenticate_hook['config']['email_base'];
+
+  $pipe = popen(escapeshellarg($program), 'w');
+  $authinfo = sprintf("%s\n%s\n", $username, $password);
+  $written = fwrite($pipe, $authinfo);
+  dbg_error_log('pwauth', 'Bytes written: %d of %d', $written,
+		strlen($authinfo));
+  $return_status = pclose($pipe);
+
+  switch($return_status) {
+  case 0:
+    // STATUS_OK: Authentication succeeded.
+    dbg_error_log('pwauth', 'User %s successfully authenticated', $username);
+    if($user = getUserByName($username)) {
+      return($user);
+    } else {
+      dbg_error_log('pwauth', 'User %s does not exist in local db, creating',
+		    $username);
+      $fullname = exec(sprintf('getent passwd %s', escapeshellarg($username)));
+      $fullname = preg_replace('{^[^:]+:[^:]+:\d+:\d+:([^:,]+)(,[^:]*):.*$}',
+			       '$1', $fullname);
+      $user = (object) array('user_no' => 0,
+			     'username' => $username,
+			     'active' => 't',
+			     'email' => sprintf('%s@%s', $username,
+						$email_base),
+			     'updated' => date('%r'),
+			     'fullname' => $fullname);
+
+      UpdateUserFromExternal($user);
+      return($user);
+    }
+    break;
+
+    /*
+     * Note that for system configurations using PAM instead of
+     * reading the password database directly, if PAM is unable to
+     * read the password database, pwauth will return status 1.
+     */
+  case 1:
+  case 2:
+    // (1) STATUS_UNKNOWN: Invalid username or password.
+    // (2) STATUS_INVALID: Invalid password.
+    dbg_error_log('pwauth', 'Invalid username or password (username: %s)',
+		  $username);
+    break;
+
+  case 3:
+    // STATUS_BLOCKED: UID for username is < pwauth's MIN_UNIX_UID
+    dbg_error_log('pwauth', 'UID for username %s is < pwauth MIN_UNIX_UID',
+		  $username);
+    break;
+
+  case 4:
+    // STATUS_EXPIRED: The user account has expired.
+    dbg_error_log('pwauth', 'The account for %s has expired', $username);
+    break;
+
+  case 5:
+    // STATUS_PW_EXPIRED: The user account's password has expired.
+    dbg_error_log('pwauth', 'The account password for user %s has expired',
+		  $username);
+    break;
+
+  case 6:
+    // STATUS_NOLOGIN: Logins to the system are administratively disabled.
+    dbg_error_log('pwauth', 'Logins administratively disabled (%s)', $username);
+    break;
+
+  case 7:
+    // STATUS_MANYFAILS: Too many login failures for user account.
+    dbg_error_log('pwauth', 'Login rejected for %s, too many failures',
+		  $username);
+    break;
+
+  case 50:
+    // STATUS_INT_USER: Configuration error, Web server cannot use pwauth
+    dbg_error_log('pwauth', 'config error: see pwauth man page (%s)',
+		  'STATUS_INT_USER');
+    break;
+
+  case 51:
+    // STATUS_INT_ARGS: pwauth received no username/passwd to check
+    dbg_error_log('pwauth', 'error: pwauth received no username/password');
+    break;
+
+  case 52:
+    // STATUS_INT_ERR: unknown error
+    dbg_error_log('pwauth', 'error: see pwauth man page (%s)',
+		  'STATUS_INT_ERR');
+    break;
+
+  case 53:
+    // STATUS_INT_NOROOT: pwauth could not read the password database
+    dbg_error_log('pwauth', 'config error: cannot read password database (%s)',
+		  'STATUS_INT_NOROOT');
+
+  default:
+    // Unknown error code.
+    dbg_error_log('pwauth', 'An unknown error (%d) has occurred',
+		  $return_status);
+  }
+
+  return(FALSE);
+}
Index: contrib/davical/inc/caldav-BIND.php
===================================================================
--- contrib/davical/inc/caldav-BIND.php	(revision 3733)
+++ contrib/davical/inc/caldav-BIND.php	(revision 3733)
@@ -0,0 +1,85 @@
+<?php
+/**
+* CalDAV Server - handle BIND method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log('BIND', 'method handler');
+require_once('AwlQuery.php');
+
+$request->NeedPrivilege('DAV::bind');
+
+if ( ! $request->IsCollection() ) {
+  $request->PreconditionFailed(403,'DAV::bind-into-collection',translate('The BIND Request-URI MUST identify a collection.'));
+}
+$parent_container = $request->path;
+if ( preg_match( '{[^/]$}', $parent_container ) ) $parent_container .= '/';
+
+require_once('DAVResource.php');
+$parent = new DAVResource( $parent_container );
+if ( ! $parent->Exists() || $parent->IsSchedulingCollection() ) {
+  $request->PreconditionFailed(403, 'DAV::method-not-allowed',translate('The BIND method is not allowed at that location.') );
+}
+
+require_once('XMLDocument.php');
+$reply = new XMLDocument(array( 'DAV:' => '' ));
+
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+
+$segment = $xmltree->GetElements('DAV::segment');
+$segment = $segment[0]->GetContent();
+
+if ( preg_match( '{[/\\\\]}', $segment ) ) {
+  $request->PreconditionFailed(403, 'DAV::name-allowed',translate('That destination name contains invalid characters.') );
+}
+
+$href    = $xmltree->GetElements('DAV::href');
+$href    = $href[0]->GetContent();
+
+$destination_path = $parent_container . $segment .'/';
+$destination = new DAVResource( $destination_path );
+if ( $destination->Exists() ) {
+  $request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
+}
+
+$source = new DAVResource( $href );
+if ( !$source->Exists() ) {
+  $request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
+}
+
+if ( $source->IsPrincipal() || !$source->IsCollection() ) {
+  $request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
+}
+
+/*
+  bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
+  bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
+  parent_container TEXT NOT NULL,
+  dav_name TEXT UNIQUE NOT NULL,
+  dav_displayname TEXT
+*/
+
+$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname )
+VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname )';
+$params = array(
+    ':target_id'    => $source->GetProperty('collection_id'),
+    ':ticket_id'    => (isset($request->ticket) ? $request->ticket->id() : null),
+    ':parent_container' => $parent->dav_name(),
+    ':session_principal' => $session->principal_id,
+    ':dav_name'     => $destination_path,
+    ':displayname'  => $source->GetProperty('displayname')
+);
+$qry = new AwlQuery( $sql, $params );
+if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
+  header('Location: '. ConstructURL($destination_path) );
+  $request->DoResponse(201);
+}
+else {
+  $request->DoResponse(500,translate('Database Error'));
+}
Index: contrib/davical/inc/caldav-GET-addressbook.php
===================================================================
--- contrib/davical/inc/caldav-GET-addressbook.php	(revision 3733)
+++ contrib/davical/inc/caldav-GET-addressbook.php	(revision 3733)
@@ -0,0 +1,224 @@
+<?php
+/**
+* CalDAV Server - handle GET method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("get-addressbook", "GET-ADDRESSBOOK method handler");
+
+require_once("XMLDocument.php");
+require_once('DAVResource.php');
+include_once("drivers_ldap.php");
+require_once('RRule-v2.php');
+
+$dav_resource = new DAVResource($request->path);
+$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
+
+if ( ! $dav_resource->Exists() ) {
+  $request->DoResponse( 404, translate("Resource Not Found.") );
+}
+
+function obfuscated_event( $icalendar ) {
+  // The user is not admin / owner of this calendar looking at his calendar and can not admin the other cal,
+  // or maybe they don't have *read* access but they got here, so they must at least have free/busy access
+  // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
+  $confidential = new iCalComponent();
+  $confidential->SetType($icalendar->GetType());
+  $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+  $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+  $confidential->SetProperties( $icalendar->GetProperties('DTSTART'), 'DTSTART' );
+  $confidential->SetProperties( $icalendar->GetProperties('RRULE'), 'RRULE' );
+  $confidential->SetProperties( $icalendar->GetProperties('DURATION'), 'DURATION' );
+  $confidential->SetProperties( $icalendar->GetProperties('DTEND'), 'DTEND' );
+  $confidential->SetProperties( $icalendar->GetProperties('UID'), 'UID' );
+  $confidential->SetProperties( $icalendar->GetProperties('CREATED'), 'CREATED' );
+
+  return $confidential;
+}
+
+
+if ( $dav_resource->IsCollection() ) {
+  if ( ! $dav_resource->IsAddressbook() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections) ) {
+    /** RFC2616 says we must send an Allow header if we send a 405 */
+    header("Allow: PROPFIND,PROPPATCH,OPTIONS,MKCOL,REPORT,DELETE");
+    $request->DoResponse( 405, translate("GET requests on collections are only supported for calendars.") );
+  }
+  
+  
+  if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
+  $fh = fopen('/tmp/REPORT.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+  }
+
+  $position = 0;
+  $target = new DAVResource($request->path);
+  require_once("iCalendar.php");
+
+  $reportnum = -1;
+  $report = array();
+  $denied = array();
+  $unsupported = array();
+  if ( isset($prop_filter) ) unset($prop_filter);
+
+  $reply = new XMLDocument( array( "DAV:" => "" ) );
+  
+
+ function contacts_to_xml( $properties, $item)                                                                                            
+  {                                                                                                                                        
+  global $c, $request, $reply , $dav_resource;                                                                                                           
+  $path = preg_replace("/^\//","",$request->path);                                                                            
+  //$filtro = "uid=".$request->username;                                                                                                                
+  //$atributos = array("uidNumber");                                                                                                       
+  //$uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);                                                                        
+  //if ($uidnumber == false) {                                                                                                             
+  //             dbg_error_log( "REPORT", "Responding with report error:  usuario nÃ£o encontrado no diretorio");                           
+  //             $request->DoResponse( 501, 'Database error');                                                                             
+  // }                                                                                                                                     
+  // $nome = $uidnumber['uidNumber'];
+   $nome = $dav_resource->GetProperty('user_no');                                                                                                        
+   $mails = array();                                                                                                                     
+   $phones = array();                                                                                                                    
+   unset($VCARD);                                                                                                                        
+   $VCARD = "UID:$item->id_contact@$nome\r\n";                                                                                           
+                                                                                                                                         
+   /***************************                                                                                                          
+   ********* DADOS DEFAULT ****                                                                                                          
+   ****************************/                                                                                                         
+   $VCARD .= "N:$item->family_names;$item->given_names\r\n";                                                                             
+   $VCARD .= "FN:$item->names_ordered\r\n";                                                                                              
+   $VCARD .= "NICKNAME:$item->alias\r\n";                                                                                                
+   $VCARD .= "PHOTO:ENCODING=b;TYPE=JPEG:$item->photo\r\n";                                                                              
+   $VCARD .= "BDAY:$item->birthdate\r\n";                                                                                                
+   $VCARD .= "NOTE:$item->notes\r\n";                                                                                                    
+   /**************************                                                                                                           
+   ********** EMAIL & TEL ****                                                                                                           
+   ***************************/                                                                                                          
+    $qry = new AwlQuery("SELECT * FROM phpgw_cc_contact_conns  inner join phpgw_cc_connections using(id_connection) WHERE  phpgw_cc_contact_conns.id_contact= :id", array(':id' => $item->id_contact));
+    if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ){                                                                                                                                 
+      while( $email_tel = $qry->Fetch() ) {                                                                                                                                                            
+          if ( $email_tel->id_typeof_contact_connection == 1)                                                                                                                                          
+             {                                                                                                                                                                                         
+               if ( $email_tel->connection_name == 'Principal' )                                                                                                                                       
+                  $type=work;                                                                                                                                                                          
+               else                                                                                                                                                                                    
+                  $type=home;                                                                                                                                                                          
+               $VCARD .= "EMAIL;TYPE=$type:$email_tel->connection_value\r\n";                                                                                                                          
+              }                                                                                                                                                                                        
+          else if ( $email_tel->id_typeof_contact_connection == 2)                                                                                                                                     
+             {                                                                                                                                                                                         
+                if ( $email_tel->connection_name == 'Trabalho' )                                                                                                                                       
+                  $type=work;                                                                                                                                                                          
+               else if ( $email_tel->connection_name == 'Casa' )                                                                                                                                       
+                  $type=home;                                                                                                                                                                          
+               else if ( $email_tel->connection_name == 'Celular' )                                                                                                                                    
+                  $type=cell;                                                                                                                                                                          
+               else if ( $email_tel->connection_name == 'Fax' )                                                                                                                                        
+                  $type=fax;                                                                                                                                                                           
+
+              $VCARD .= "TEL;TYPE=$type:$email_tel->connection_value\r\n";
+              }                                                           
+      }                                                                   
+    }                                                                     
+   /**************************                                            
+    ************ ADDR ********                                            
+    **************************/                                           
+    $qry = new AwlQuery("SELECT * FROM phpgw_cc_contact_addrs  inner join phpgw_cc_addresses  using(id_address) WHERE  phpgw_cc_contact_addrs.id_contact= :id", array(':id' => $item->id_contact));
+    if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ){                                                                                                                             
+      while( $addr = $qry->Fetch() ) {                                                                                                                                                             
+          if ( $addr->id_typeof_contact_address == 1)                                                                                                                                              
+             {                                                                                                                                                                                     
+               $type=home;
+               $qryy = new AwlQuery("SELECT city_name,state_symbol FROM phpgw_cc_city  inner join phpgw_cc_state  using(id_country) WHERE  phpgw_cc_city.id_city= :id AND phpgw_cc_state.id_state = :idstate", array(':id' => $addr->id_city,':idstate' => $addr->id_state));
+               if ($qryy->Exec("REPORT",__LINE__,__FILE__) && $qryy->rows() > 0) 
+                 { $locate = $qryy->Fetch();
+                   $VCARD .= "ADR;TYPE=$type:;;$addr->address1;$locate->city_name;$addr->state_symbol;$addr->postal_code;$addr->id_country\r\n";                                                                      
+                  }
+              }                                                                                                                                                                                    
+          else if ( $addr->id_typeof_contact_address == 2)                                                                                                                                         
+             {                                                                                                                                                                                     
+               $type=work;
+               $qryy = new AwlQuery("SELECT city_name,state_symbol FROM phpgw_cc_city  inner join phpgw_cc_state  using(id_country) WHERE  phpgw_cc_city.id_city= :id AND phpgw_cc_state.id_state = :idstate", array(':id' => $addr->id_city,':idstate' => $addr->id_state));
+               if ($qryy->Exec("REPORT",__LINE__,__FILE__) && $qryy->rows() > 0)
+                 { $locate = $qryy->Fetch();
+                   $VCARD .= "ADR;TYPE=$type:;;$addr->address1;$locate->city_name;$addr->state_symbol;$addr->postal_code;$addr->id_country\r\n";
+                  }
+              }                                                                                                                                                                                    
+      }                                                                                                                                                                                            
+    }                                                                                                                                                                                              
+                                                                                                                                                                                                   
+
+   /********** PUBLIC KEY ********
+    *****************************/ 
+
+   
+
+
+   dbg_error_log("REPORT","Building XML Response for item '%s' usuario  '%s'", $item->id_contact,$nome );
+                                                                                                         
+   $response = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Inverse inc.//SOGo Connector 1.0//EN\r\n";      
+   $response .= "$VCARD";                                                                                
+   $response .= "END:VCARD\r\n";                                                                         
+   $caldav_data = $response;                                                                             
+                                                                                                         
+   $dav_nam = "{$item->id_contact}@{$item->id_owner}";                                                   
+   $dav_etag_nam=md5($item->id_contact.$item->id_owner.$item->last_update);                          
+   //$dav_name = "{$item->id_contact}@{$item->id_owner}@{$item->last_update}";                        
+   $dav_name = sprintf('/%s/%s%s.vcf',$path,$item->id_contact,$item->id_owner);    
+   $url = ConstructURL($dav_name);                                                                       
+                                                                                                         
+   //$prop = new XMLElement("prop");
+   //$status = new XMLElement("status", "HTTP/1.1 200 OK" );                                               
+   //$reply->CalDAVElement($prop, 'address-data', $caldav_data );
+   //$prop->NewElement('address-data', $caldav_data );                                         
+   //$propstat = new XMLElement( "propstat", array( $prop, $status) );                                     
+   //$href = new XMLElement("href", $url );                                                                
+
+   //$elements = array($href,$propstat);
+   //$response = new XMLElement( "response", $elements );
+
+   //return $caldav_data;
+  
+   header( 'Content-Length: '.strlen($response) );
+   header( 'Etag: '.$dav_name );
+   $request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $response), 'text/vcard; charset="ISO-8859-1"' );
+  // return $status;                                
+
+  }     
+   dbg_error_log("REPORT","Building XML REQUESTEEEE PATHHHHHHH  '%s'", $request->path );
+   $part= explode('/',$request->path);
+   $id=preg_split('/@/',  preg_replace("/.vcf/","",$part[3]));
+   //$id = $dav_resource->GetProperty('user_no'); 
+  //$mode = 'carddav';
+  //$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
+  //include("caldav-REPORT-multiget-addressbook.php");
+           $sql = "SELECT * FROM phpgw_cc_contact WHERE  id_owner= :nome AND id_contact= :id";
+           $qry = new AwlQuery( $sql,array(':nome' => $id[1],':id' => $id[0]));
+           if ( $qry->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+              while( $contacts_object = $qry->Fetch() ) {
+                //if ( $bound_from != $collection->dav_name() ) {
+                //      $contacts_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $contacts_object->dav_name);
+                //    }
+                //if ( $need_expansion ) {
+                //   $vResource = new vComponent($contacts_object->caldav_data);
+                //   $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                //   $contacts_object->caldav_data = $expanded->Render();
+                //    }
+
+                  //$responses[] = contacts_to_xml( $properties, $contacts_object);
+                  contacts_to_xml( $properties, $contacts_object);
+
+            }
+          }  
+
+  
+
+}
+
+
Index: contrib/davical/inc/caldav-PUT-functions.php
===================================================================
--- contrib/davical/inc/caldav-PUT-functions.php	(revision 3733)
+++ contrib/davical/inc/caldav-PUT-functions.php	(revision 3733)
@@ -0,0 +1,1728 @@
+<?php
+/**
+* CalDAV Server - handle PUT method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later version
+*/
+
+/**
+* Check if the user wants to put just one VEVENT/VTODO or a whole calendar
+* if the collection = calendar = $request_container doesn't exist then create it
+* return true if it's a whole calendar
+*/
+
+require_once('iCalendar.php');
+require_once('DAVResource.php');
+include_once ("drivers_ldap.php");
+$bad_events = null;
+
+/**
+* A regex which will match most reasonable timezones acceptable to PostgreSQL.
+*/
+$tz_regex = ':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z_]+$:i';
+
+/**
+* This function launches an error
+* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
+* @param int $user_no the user who will receive this ics file
+* @param string $path the $path where the PUT failed to store such as /user_foo/home/
+* @param string $message An optional error message to return to the client
+* @param int $error_no An optional value for the HTTP error code
+*/
+function rollback_on_error( $caldav_context, $user_no, $path, $message='', $error_no=500 ) {
+  global $c, $bad_events;
+  if ( !$message ) $message = translate('Database error');
+  $qry = new AwlQuery();
+  $qry->Rollback();
+  if ( $caldav_context ) {
+    if ( isset($bad_events) && isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+      $bad_events[] = $message;
+    }
+    else {
+      global $request;
+      $request->DoResponse( $error_no, $message );
+    }
+    // and we don't return from that, ever...
+  }
+
+  $c->messages[] = sprintf(translate('Status: %d, Message: %s, User: %d, Path: %s'), $error_no, $message, $user_no, $path);
+
+}
+
+
+
+/**
+* Work out the location we are doing the PUT to, and check that we have the rights to
+* do the needful.
+* @param string $username The name of the destination user
+* @param int $user_no The user making the change
+* @param string $path The DAV path the resource is bing PUT to
+* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
+* @param boolean $public Whether the collection will be public, should we need to create it
+*/
+function controlRequestContainer( $username, $user_no, $path, $caldav_context, $public = null ) {
+  global $c, $bad_events;
+
+  // Check to see if the path is like /foo /foo/bar or /foo/bar/baz etc. (not ending with a '/', but contains at least one)
+  if ( preg_match( '#^(.*/)([^/]+)$#', $path, $matches ) ) {//(
+    $request_container = $matches[1];   // get everything up to the last '/'
+  }
+  else {
+    // In this case we must have a URL with a trailing '/', so it must be a collection.
+    $request_container = $path;
+  }
+
+  if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+    $bad_events = array();
+  }
+
+  /**
+  * Before we write the event, we check the container exists, creating it if it doesn't
+  */
+  if ( $request_container == "/$username/" ) {
+    /**
+    * Well, it exists, and we support it, but it is against the CalDAV spec
+    */
+    dbg_error_log( 'WARN', ' Storing events directly in user\'s base folders is not recommended!');
+  }
+  else {
+    $sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
+    $qry = new AwlQuery( $sql, array( ':dav_name' => $request_container) );
+    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) {
+      rollback_on_error( $caldav_context, $user_no, $path );
+    }
+    if ( !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+      if ( $qry->rows() == 0 ) {
+        $request->DoResponse( 405 ); // Method not allowed
+      }
+      return;
+    }
+    if ( $qry->rows() == 0 ) {
+      if ( $public == true ) $public = 't'; else $public = 'f';
+      if ( preg_match( '{^(.*/)([^/]+)/$}', $request_container, $matches ) ) {
+        $parent_container = $matches[1];
+        $displayname = $matches[2];
+      }
+      $sql = 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified, publicly_readable, resourcetypes )
+VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname, TRUE, current_timestamp, current_timestamp, :is_public::boolean, :resourcetypes )';
+      $params = array(
+      ':user_no' => $user_no,
+      ':parent_container' => $parent_container,
+      ':dav_name' => $request_container,
+      ':dav_etag' => md5($user_no. $request_container),
+      ':dav_displayname' => $displayname,
+      ':is_public' => $public,
+      ':resourcetypes' => '<DAV::collection/><urn:ietf:params:xml:ns:caldav:calendar/>'
+      );
+      $qry->QDo( $sql, $params );
+    }
+    else if ( isset($public) ) {
+      $collection = $qry->Fetch();
+      $sql = 'UPDATE collection SET publicly_readable = :is_public::boolean WHERE collection_id = :collection_id';
+      $params = array( ':is_public' => ($public?'t':'f'), ':collection_id' => $collection->collection_id );
+      if ( ! $qry->QDo($sql,$params) ) {
+        rollback_on_error( $caldav_context, $user_no, $path );
+      }
+    }
+  }
+}
+
+
+/**
+* Check if this collection should force all events to be PUBLIC.
+* @param string $user_no the user that owns the collection
+* @param string $dav_name the collection to check
+* @return boolean Return true if public events only are allowed.
+*/
+function public_events_only( $user_no, $dav_name ) {
+  global $c;
+
+  $sql = 'SELECT public_events_only FROM collection WHERE dav_name = :dav_name';
+
+  $qry = new AwlQuery($sql, array(':dav_name' => $dav_name) );
+
+  if( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+    $collection = $qry->Fetch();
+
+    if ($collection->public_events_only == 't') {
+      return true;
+    }
+  }
+
+  // Something went wrong, must be false.
+  return false;
+}
+
+
+/**
+* Deliver scheduling requests to attendees
+* @param iCalComponent $ical the VCALENDAR to deliver
+*/
+function handle_schedule_request( $ical ) {
+  global $c, $session, $request;
+  $resources = $ical->GetComponents('VTIMEZONE',false);
+  $ic = $resources[0];
+  $etag = md5 ( $request->raw_post );
+  $reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C" ) );
+  $responses = array();
+
+  $attendees = $ic->GetProperties('ATTENDEE');
+  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+  if ( count ( $wr_attendees ) > 0 ) {
+    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+    foreach( $wr_attendees AS $k => $v ) {
+      $attendees[] = $v;
+    }
+  }
+  dbg_error_log( "POST", "Attempting to deliver scheduling request for %d attendees", count($attendees) );
+
+  foreach( $attendees AS $k => $attendee ) {
+    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+    if ( $attendee_email == $request->principal->email ) {
+      dbg_error_log( "POST", "not delivering to owner" );
+      continue;
+    }
+    if ( $attendee->GetParameterValue ( 'PARTSTAT' ) != 'NEEDS-ACTION' || preg_match ( '/^[35]\.[3-9]/',  $attendee->GetParameterValue ( 'SCHEDULE-STATUS' ) ) ) {
+      dbg_error_log( "POST", "attendee %s does not need action", $attendee_email );
+      continue;
+    }
+
+    dbg_error_log( "POST", "Delivering to %s", $attendee_email );
+
+    $attendee_principal = new CalDAVPrincipal ( array ('email'=>$attendee_email, 'options'=> array ( 'allow_by_email' => true ) ) );
+    if ( $attendee_principal == false ){
+      $attendee->SetParameterValue ('SCHEDULE-STATUS','3.7;Invalid Calendar User');
+      continue;
+    }
+    $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+
+    $ar = new DAVResource($deliver_path);
+    $priv =  $ar->HavePrivilegeTo('schedule-deliver-invite' );
+    if ( ! $ar->HavePrivilegeTo('schedule-deliver-invite' ) ){
+      $reply = new XMLDocument( array('DAV:' => '') );
+      $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+      // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
+      $reply->NSElement( $privnodes[1], 'schedule-deliver-invite' );
+      $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+      $xmldoc = $reply->Render('error',$xml);
+      $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"');
+    }
+
+
+    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+    $ncal = new iCalComponent (  );
+    $ncal->VCalendar ();
+    $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+    $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+    $content = $ncal->Render();
+    $cid = $ar->GetProperty('collection_id');
+    dbg_error_log('DELIVER', 'to user: %s, to path: %s, collection: %s, from user: %s, caldata %s', $attendee_principal->user_no, $deliver_path, $cid, $request->user_no, $content );
+    write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+      $content , $ar->GetProperty('collection_id'), $request->user_no,
+      md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
+    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+  }
+	// don't write an entry in the out box, ical doesn't delete it or ever read it again
+  $ncal = new iCalComponent (  );
+  $ncal->VCalendar ();
+  $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+  $content = $ncal->Render();
+ 	$deliver_path = preg_replace ( '/^.*caldav.php/','', $request->principal->schedule_inbox_url );
+  $ar = new DAVResource($deliver_path);
+  write_resource( $request->user_no, $deliver_path . $etag . '.ics' ,
+    $content , $ar->GetProperty('collection_id'), $request->user_no,
+    md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
+  //$etag = md5($content);
+  header('ETag: "'. $etag . '"' );
+  header('Schedule-Tag: "'.$etag . '"' );
+  $request->DoResponse( 201, 'Created' );
+}
+
+/**
+* Deliver scheduling replies to organizer and other attendees
+* @param iCalComponent $ical the VCALENDAR to deliver
+* @return false on error
+*/
+function handle_schedule_reply ( $ical ) {
+  global $c, $session, $request;
+  $resources = $ical->GetComponents('VTIMEZONE',false);
+  $ic = $resources[0];
+  $etag = md5 ( $request->raw_post );
+	$organizer = $ic->GetProperties('ORGANIZER');
+	// for now we treat events with out organizers as an error
+	if ( count ( $organizer ) < 1 )
+		return false;
+  $attendees = array_merge($organizer,$ic->GetProperties('ATTENDEE'));
+  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+  if ( count ( $wr_attendees ) > 0 ) {
+    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+    foreach( $wr_attendees AS $k => $v ) {
+      $attendees[] = $v;
+    }
+  }
+  dbg_error_log( "POST", "Attempting to deliver scheduling request for %d attendees", count($attendees) );
+
+  foreach( $attendees AS $k => $attendee ) {
+	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+	  dbg_error_log( "POST", "Delivering to %s", $attendee_email );
+	  $attendee_principal = new CalDAVPrincipal ( array ('email'=>$attendee_email, 'options'=> array ( 'allow_by_email' => true ) ) );
+	  $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+    if ( $attendee_email == $request->principal->email ) {
+      dbg_error_log( "POST", "not delivering to owner" );
+      continue;
+    }
+	  $ar = new DAVResource($deliver_path);
+	  if ( ! $ar->HavePrivilegeTo('schedule-deliver-reply' ) ){
+	    $reply = new XMLDocument( array('DAV:' => '') );
+	     $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+	     // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
+	     $reply->NSElement( $privnodes[1], 'schedule-deliver-reply' );
+	     $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+	     $xmldoc = $reply->Render('error',$xml);
+			 $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+			 continue;
+	  }
+
+	  $ncal = new iCalComponent (  );
+	  $ncal->VCalendar ();
+	  $ncal->AddProperty ( 'METHOD', 'REPLY' );
+	  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+	  $content = $ncal->Render();
+	  write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+	    $content , $ar->GetProperty('collection_id'), $request->user_no,
+	    md5($content), $ncal, $put_action_type='INSERT', $caldav_context=true, $log_action=true, $etag );
+	}
+  $request->DoResponse( 201, 'Created' );
+}
+
+
+
+
+/**
+* Create a scheduling request in the schedule inbox for the
+* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
+* @param iCalProp $attendee The attendee we are scheduling
+* @return float The result of the scheduling request, per caldav-sched #3.5.4
+*/
+function write_scheduling_request( &$resource, $attendee ) {
+  $response = '5.3;'.translate('No scheduling support for user');
+  return '"'.$response.'"';
+}
+
+/**
+* Create scheduling requests in the schedule inbox for the
+* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
+*/
+function create_scheduling_requests( &$resource ) {
+  if ( ! is_object($resource) ) {
+    dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+    return;
+  }
+
+  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+	if ( count ( $wr_attendees ) > 0 ) {
+    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+    foreach( $wr_attendees AS $k => $v ) {
+      $attendees[] = $v;
+    }
+  }
+  if ( count($attendees) == 0 ) {
+    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+    return;
+  }
+
+  dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) );
+  foreach( $attendees AS $attendee ) {
+    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+  }
+}
+
+
+/**
+* Update scheduling requests in the schedule inbox for the
+* @param iCalComponent $resource The VEVENT/VTODO/... resource we are scheduling
+*/
+function update_scheduling_requests( &$resource ) {
+  if ( ! is_object($resource) ) {
+    dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+    return;
+  }
+
+  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+	if ( count ( $wr_attendees ) > 0 ) {
+    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+    foreach( $wr_attendees AS $k => $v ) {
+      $attendees[] = $v;
+    }
+  }
+  if ( count($attendees) == 0 ) {
+    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+    return;
+  }
+
+  dbg_error_log( 'PUT', 'Adding to scheduling inbox %d attendees', count($attendees) );
+  foreach( $attendees AS $attendee ) {
+    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+  }
+}
+
+
+/**
+* This function will import a whole calendar
+* @param string $ics_content the ics file to import
+* @param int $user_no the user wich will receive this ics file
+* @param string $path the $path where it will be store such as /user_foo/home/
+* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
+*
+* Any VEVENTs with the same UID will be concatenated together
+*/
+function import_collection( $ics_content, $user_no, $path, $caldav_context, $appending = false ) {
+  global $c, $session, $tz_regex;
+
+  if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put'])) ) {
+    $fh = fopen('/tmp/PUT-2.txt','w');
+    if ( $fh ) {
+      fwrite($fh,$ics_content);
+      fclose($fh);
+    }
+  }
+
+  $calendar = new iCalComponent($ics_content);
+  $timezones = $calendar->GetComponents('VTIMEZONE',true);
+  $components = $calendar->GetComponents('VTIMEZONE',false);
+
+  $displayname = $calendar->GetPValue('X-WR-CALNAME');
+  if ( !$appending && isset($displayname) ) {
+    $sql = 'UPDATE collection SET dav_displayname = :displayname WHERE dav_name = :dav_name';
+    $qry = new AwlQuery( $sql, array( ':displayname' => $displayname, ':dav_name' => $path) );
+    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+  }
+
+  $tz_ids    = array();
+  foreach( $timezones AS $k => $tz ) {
+    $tz_ids[$tz->GetPValue('TZID')] = $k;
+  }
+
+  /** Build an array of resources.  Each resource is an array of iCalComponent */
+  $resources = array();
+  foreach( $components AS $k => $comp ) {
+    $uid = $comp->GetPValue('UID');
+    if ( $uid == null || $uid == '' ) continue;
+    if ( !isset($resources[$uid]) ) $resources[$uid] = array();
+    $resources[$uid][] = $comp;
+
+    /** Ensure we have the timezone component for this in our array as well */
+    $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
+    if ( !isset($tzid) || $tzid == '' ) $tzid = $comp->GetPParamValue('DUE','TZID');
+    if ( !isset($resources[$uid][$tzid]) && isset($tz_ids[$tzid]) ) {
+      $resources[$uid][$tzid] = $timezones[$tz_ids[$tzid]];
+    }
+  }
+
+
+  $sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
+  $qry = new AwlQuery( $sql, array( ':dav_name' => $path) );
+  if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+  if ( ! $qry->rows() == 1 ) {
+    dbg_error_log( 'ERROR', ' PUT: Collection does not exist at "%s" for user %d', $path, $user_no );
+    rollback_on_error( $caldav_context, $user_no, $path );
+  }
+  $collection = $qry->Fetch();
+
+  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) $qry->Begin();
+  $base_params = array( ':collection_id' => $collection->collection_id );
+  if ( !$appending ) {
+    if ( !$qry->QDo('DELETE FROM calendar_item WHERE collection_id = :collection_id', $base_params)
+      || !$qry->QDo('DELETE FROM caldav_data WHERE collection_id = :collection_id', $base_params) )
+      rollback_on_error( $caldav_context, $user_no, $collection->collection_id );
+  }
+
+  $dav_data_insert = <<<EOSQL
+INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id )
+    VALUES( :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id )
+EOSQL;
+
+  $calitem_insert = <<<EOSQL
+INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp, dtstart, dtend, summary, location, class, transp,
+                    description, rrule, tz_id, last_modified, url, priority, created, due, percent_complete, status, collection_id )
+    VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp, :dtstart, ##dtend##, :summary, :location, :class, :transp,
+                :description, :rrule, :tzid, :modified, :url, :priority, :created, :due, :percent_complete, :status, :collection_id)
+EOSQL;
+
+  $last_tz_locn = '';
+  foreach( $resources AS $uid => $resource ) {
+    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Begin();
+
+    /** Construct the VCALENDAR data */
+    $vcal = new iCalComponent();
+    $vcal->VCalendar();
+    $vcal->SetComponents($resource);
+    create_scheduling_requests($vcal);
+    $icalendar = $vcal->Render();
+
+    /** As ever, we mostly deal with the first resource component */
+    $first = $resource[0];
+
+    $dav_data_params = $base_params;
+    $dav_data_params[':user_no'] = $user_no;
+    $dav_data_params[':dav_name'] = sprintf( '%s%s.ics', $path, $uid );
+    $dav_data_params[':etag'] = md5($icalendar);
+    $calitem_params = $dav_data_params;
+    $dav_data_params[':dav_data'] = $icalendar;
+    $dav_data_params[':caldav_type'] = $first->GetType();
+    $dav_data_params[':session_user'] = $session->user_no;
+    if ( !$qry->QDo($dav_data_insert,$dav_data_params) ) rollback_on_error( $caldav_context, $user_no, $path );
+
+    $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $dav_data_params[':dav_name']));
+    if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+      $dav_id = $row->dav_id;
+    }
+
+    $dtstart = $first->GetPValue('DTSTART');
+    $calitem_params[':dtstart'] = $dtstart;
+    if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+      $dtstart = $first->GetPValue('DUE');
+    }
+
+    $dtend = $first->GetPValue('DTEND');
+    if ( isset($dtend) && $dtend != '' ) {
+      dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+      $calitem_params[':dtend'] = $dtend;
+      $dtend = ':dtend';
+    }
+    else {
+      $dtend = 'NULL';
+      if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+        $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+        $dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
+        $calitem_params[':duration'] = $duration;
+      }
+      elseif ( $first->GetType() == 'VEVENT' ) {
+        /**
+        * From RFC2445 4.6.1:
+        * For cases where a "VEVENT" calendar component specifies a "DTSTART"
+        * property with a DATE data type but no "DTEND" property, the events
+        * non-inclusive end is the end of the calendar date specified by the
+        * "DTSTART" property. For cases where a "VEVENT" calendar component specifies
+        * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+        * the event ends on the same calendar date and time of day specified by the
+        * "DTSTART" property.
+        *
+        * So we're looking for 'VALUE=DATE', to identify the duration, effectively.
+        *
+        */
+        $value_type = $first->GetPParamValue('DTSTART','VALUE');
+        dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
+        if ( isset($value_type) && $value_type == 'DATE' )
+          $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
+        else
+          $dtend = ':dtstart';
+      }
+    }
+
+    $last_modified = $first->GetPValue('LAST-MODIFIED');
+    if ( !isset($last_modified) || $last_modified == '' ) $last_modified = gmdate( 'Ymd\THis\Z' );
+    $calitem_params[':modified'] = $last_modified;
+
+    $dtstamp = $first->GetPValue('DTSTAMP');
+    if ( !isset($dtstamp) || $dtstamp == '' ) $dtstamp = $last_modified;
+    $calitem_params[':dtstamp'] = $dtstamp;
+
+    /** RFC2445, 4.8.1.3: Default is PUBLIC, or also if overridden by the collection settings */
+    $class = ($collection->public_events_only == 't' ? 'PUBLIC' : $first->GetPValue('CLASS') );
+    if ( !isset($class) || $class == '' ) $class = 'PUBLIC';
+    $calitem_params[':class'] = $class;
+
+
+    /** Calculate what timezone to set, first, if possible */
+    $tzid = $first->GetPParamValue('DTSTART','TZID');
+    if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+    if ( isset($tzid) && $tzid != '' ) {
+      if ( isset($resource[$tzid]) ) {
+        $tz = $resource[$tzid];
+        $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+      }
+      else {
+        unset($tz);
+        unset($tz_locn);
+      }
+      if ( ! isset($tz_locn) || ! preg_match( $tz_regex, $tz_locn ) ) {
+        if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) ) {
+          $tz_locn = $matches[1];
+        }
+      }
+      dbg_error_log( 'PUT', ' Using TZID[%s] and location of [%s]', $tzid, (isset($tz_locn) ? $tz_locn : '') );
+      if ( isset($tz_locn) && ($tz_locn != $last_tz_locn) && preg_match( $tz_regex, $tz_locn ) ) {
+        dbg_error_log( 'PUT', ' Setting timezone to %s', $tz_locn );
+        if ( $tz_locn != '' ) {
+          $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+        }
+        $last_tz_locn = $tz_locn;
+      }
+      $params = array( ':tzid' => $tzid);
+      $qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params );
+      if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+        $params[':tzlocn'] = $tz_locn;
+        $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+        $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+      }
+      if ( !isset($tz_locn) || $tz_locn == '' ) $tz_locn = $tzid;
+    }
+    else {
+      $tzid = null;
+    }
+
+    $sql = str_replace( '##dtend##', $dtend, $calitem_insert );
+    $calitem_params[':tzid'] = $tzid;
+    $calitem_params[':uid'] = $first->GetPValue('UID');
+    $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+    $calitem_params[':location'] = $first->GetPValue('LOCATION');
+    $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+    $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+    $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+    $calitem_params[':url'] = $first->GetPValue('URL');
+    $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+    $calitem_params[':due'] = $first->GetPValue('DUE');
+    $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+    $calitem_params[':status'] = $first->GetPValue('STATUS');
+
+    $created = $first->GetPValue('CREATED');
+    if ( $created == '00001231T000000Z' ) $created = '20001231T000000Z';
+    $calitem_params[':created'] = $created;
+
+    if ( !$qry->QDo($sql,$calitem_params) ) rollback_on_error( $caldav_context, $user_no, $path);
+
+    write_alarms($dav_id, $first);
+    write_attendees($dav_id, $first);
+
+    create_scheduling_requests( $vcal );
+    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Commit();
+  }
+
+  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) {
+    if ( ! $qry->Commit() ) rollback_on_error( $caldav_context, $user_no, $path);
+  }
+}
+
+
+/**
+* Given a dav_id and an original iCalComponent, pull out each of the VALARMs
+* and write the values into the calendar_alarm table.
+*
+* @param int $dav_id The dav_id of the caldav_data we're processing
+* @param iCalComponent The VEVENT or VTODO containing the VALARM
+* @return null
+*/
+function write_alarms( $dav_id, $ical ) {
+  $qry = new AwlQuery('DELETE FROM calendar_alarm WHERE dav_id = '.$dav_id );
+  $qry->Exec('PUT',__LINE__,__FILE__);
+
+  $alarms = $ical->GetComponents('VALARM');
+  if ( count($alarms) < 1 ) return;
+
+  $qry->SetSql('INSERT INTO calendar_alarm ( dav_id, action, trigger, summary, description, component, next_trigger )
+          VALUES( '.$dav_id.', :action, :trigger, :summary, :description, :component,
+                                      :related::timestamp with time zone + :related_trigger::interval )' );
+  $qry->Prepare();
+  foreach( $alarms AS $v ) {
+    $trigger = array_merge($v->GetProperties('TRIGGER'));
+    $trigger = $trigger[0];
+    $related = null;
+    $related_trigger = '0M';
+    $trigger_type = $trigger->GetParameterValue('VALUE');
+    if ( !isset($trigger_type) || $trigger_type == 'DURATION' ) {
+      switch ( $trigger->GetParameterValue('RELATED') ) {
+        case 'DTEND':  $related = $ical->GetPValue('DTEND'); break;
+        case 'DUE':    $related = $ical->GetPValue('DUE');   break;
+        default:       $related = $ical->GetPValue('DTSTART');
+      }
+      $duration = $trigger->Value();
+      $minus = (substr($duration,0,1) == '-');
+      $related_trigger = trim(preg_replace( '#[PT-]#', ' ', $duration ));
+      if ( $minus ) {
+        $related_trigger = preg_replace( '{(\d+[WDHMS])}', '-$1 ', $related_trigger );
+      }
+    }
+    $qry->Bind(':action', $v->GetPValue('ACTION'));
+    $qry->Bind(':trigger', $trigger->Render());
+    $qry->Bind(':summary', $v->GetPValue('SUMMARY'));
+    $qry->Bind(':description', $v->GetPValue('DESCRIPTION'));
+    $qry->Bind(':component', $v->Render());
+    $qry->Bind(':related', $related );
+    $qry->Bind(':related_trigger', $related_trigger );
+    $qry->Exec('PUT',__LINE__,__FILE__);
+  }
+}
+
+
+/**
+* Parse out the attendee property and write a row to the
+* calendar_attendee table for each one.
+* @param int $dav_id The dav_id of the caldav_data we're processing
+* @param iCalComponent The VEVENT or VTODO containing the ATTENDEEs
+* @return null
+*/
+function write_attendees( $dav_id, $ical ) {
+  $qry = new AwlQuery('DELETE FROM calendar_attendee WHERE dav_id = '.$dav_id );
+  $qry->Exec('PUT',__LINE__,__FILE__);
+
+  $attendees = $ical->GetProperties('ATTENDEE');
+  if ( count($attendees) < 1 ) return;
+
+  $qry->SetSql('INSERT INTO calendar_attendee ( dav_id, status, partstat, cn, attendee, role, rsvp, property )
+          VALUES( '.$dav_id.', :status, :partstat, :cn, :attendee, :role, :rsvp, :property )' );
+  $qry->Prepare();
+  $processed = array();
+  foreach( $attendees AS $v ) {
+    $attendee = $v->Value();
+    if ( isset($processed[$attendee]) ) {
+      dbg_error_log( 'LOG', 'Duplicate attendee "%s" in resource "%d"', $attendee, $dav_id );
+      dbg_error_log( 'LOG', 'Original:  "%s"', $processed[$attendee] );
+      dbg_error_log( 'LOG', 'Duplicate: "%s"', $v->Render() );
+      continue; /** @TODO: work out why we get duplicate ATTENDEE on one VEVENT */
+    }
+    $qry->Bind(':attendee', $attendee );
+    $qry->Bind(':status',   $v->GetParameterValue('STATUS') );
+    $qry->Bind(':partstat', $v->GetParameterValue('PARTSTAT') );
+    $qry->Bind(':cn',       $v->GetParameterValue('CN') );
+    $qry->Bind(':role',     $v->GetParameterValue('ROLE') );
+    $qry->Bind(':rsvp',     $v->GetParameterValue('RSVP') );
+    $qry->Bind(':property', $v->Render() );
+    $qry->Exec('PUT',__LINE__,__FILE__);
+    $processed[$attendee] = $v->Render();
+  }
+}
+
+
+/**
+* Actually write the resource to the database.  All checking of whether this is reasonable
+* should be done before this is called.
+* @param int $user_no The user_no owning this resource on the server
+* @param string $path The path to the resource being written
+* @param string $caldav_data The actual resource to be written
+* @param int $collection_id The ID of the collection containing the resource being written
+* @param int $author The user_no who wants to put this resource on the server
+* @param string $etag An etag unique for this event
+* @param object $ic The parsed iCalendar object
+* @param string $put_action_type INSERT or UPDATE depending on what we are to do
+* @param boolean $caldav_context True, if we are responding via CalDAV, false for other ways of calling this
+* @param string Either 'INSERT' or 'UPDATE': the type of action we are doing
+* @param boolean $log_action Whether to log the fact that we are writing this into an action log (if configured)
+* @param string $weak_etag An etag that is NOT modified on ATTENDEE changes for this event
+* @return boolean True for success, false for failure.
+*/
+function write_resource( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
+  global $tz_regex;
+
+  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+  if ( !isset($resources[0]) ) {
+    $resource_type = 'Unknown';
+    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+    rollback_on_error( $caldav_context, $user_no, $path, translate('No calendar content'), 412 );
+    return false;
+  }
+  else {
+     if( count($resources) == 2)
+          $first = $resources[1];
+     else
+          $first = $resources[0];
+     $resource_type = $first->GetType();
+  }
+  
+  unset($put_action_type);
+  $nome = substr($user_no, 0, 9);
+  $UID = $first->GetPValue('UID');
+  $CALID = strtok($UID,"@");
+  $proprietario = strtok("@");
+  $qry = new AwlQuery("SELECT * FROM phpgw_cal_user WHERE cal_login=:nome AND cal_id=:CALID AND cal_status='A' AND cal_type='u' ", array(':nome' => $nome , ':CALID' => $CALID));
+  $qry->Exec("PUT");
+  if ($qry->rows() < 1) {
+                $qrycompa = new AwlQuery("SELECT * FROM phpgw_cal  WHERE cal_id=:CALID AND owner=:nome ", array(':CALID' => $CALID, ':nome' => $nome));
+                $qrycompa->Exec("PUT");
+                if ($qrycompa->rows() < 1) {
+                        $put_action_type = 'INSERT';
+                } else {
+                        //$put_action_type = 'UPDATE';
+                        $put_action_type = 'NDA';
+                }
+     }
+  elseif ($qry->rows() == 1) {
+                $put_action_type = 'UPDATE';
+    }
+           
+        $second = $first->GetComponents('VALARM');
+        if(count($second) >= 1)
+        {  
+            $alarme_time = $second[0]->GetPValue('TRIGGER');
+        }
+        $alarme_descarta = $first->GetPValue('X-MOZ-LASTACK');
+        $start = $first->GetPValue('DTSTART');
+        $dateTime = date_create($start);
+        $HJ = date("Y-m-d 00:00:00");
+        $hoje = date_create($HJ);
+        $hj_seg = $hoje->format("U");
+        //date_format( $dateTime, 2000, 12, 12);
+        $DT_START = $dateTime->format("U");
+        $hora = $dateTime->format("G");
+        $horas_seg = $hora * 60 * 60;
+        $minuto = $dateTime->format("i");
+        $min_seg = $minuto * 60;
+        $total_seg = $horas_seg + $min_seg;
+        $alarme_data = $total_seg + $hj_seg;
+
+         $end = $first->GetPValue('DTEND');
+        $dateTime_end = date_create($end);
+        ///date_format( $dateTime, 2000, 12, 12);
+        $DT_END = $dateTime_end->format("U");
+        $execao = $first->GetProperties('EXDATE');
+        $modify = $first->GetPValue('LAST-MODIFIED');
+        $dateTime_mod = date_create($modify);
+        //date_format( $dateTime, 2000, 12, 12);
+        $MOD = $dateTime_mod->format("U");
+        $seg = $alarme_data * 1000000;
+        $snooze = "X-MOZ-SNOOZE-TIME";
+        //if(count($second) >= 1){
+        $adia_alarme = $first->GetPValue("$snooze");
+        //dbg_error_log("PUT", "ADIA alarme: '%s' ", $adia_alarme);
+        //}  
+        $prioridade = $first->GetPValue('PRIORITY');
+        if ($prioridade == "") {
+                $prioridade = 1;
+        }
+        $RECUR = $first->GetPValue('RRULE');
+
+        $categoria_utf8 = $first->GetPValue('CATEGORIES');
+        $categoria = utf8_decode($categoria_utf8);
+        if ($RECUR == null) {
+                $tipo = E;
+        } else {
+                $tipo = M;
+        }
+
+        // EXDATE:20081126T190000Z
+        //Adicionado por Umberto
+        //Permite exclusÃ£o de varias recorrencias.
+        if ( is_array($execao)) {
+                foreach( $execao AS $k => $v ) {
+                        //$dateTime = date_create($deletarecorrencia);
+                        //$EXEC = $dateTime->format("U");
+                        $EXEC = dataTzParaSegundos($v->value());
+                        $TOTAL = "$TOTAL,$EXEC";
+                }
+                $EXEC = trim($TOTAL,",");
+
+        } else {
+                $EXEC = "";
+        }
+        //Fim da ediÃ§Ã£o por Umberto
+        $titulo_utf8 =  $first->GetPValue('SUMMARY');
+        $titulo = utf8_decode($titulo_utf8);
+        $descricao_utf8 = preg_replace('/\n/', "\r\n",  $first->GetPValue('DESCRIPTION'));
+        $descricao = utf8_decode($descricao_utf8);
+        $recur_id = $first->GetPValue('RECURRENCE-ID');
+        $cidade_utf8 =  $first->GetPValue('LOCATION');
+        $cidade = utf8_decode($cidade_utf8);
+        $iporigem = getenv("REMOTE_ADDR");
+        $class =  $first->GetPValue('CLASS');
+        if ($class == 'PRIVATE') {
+                $public = 0;
+        } else {
+                $public = 1;
+        }
+        //############## Categoria ##################                           
+        if ( $categoria != ""){                                                 
+                $x= "select * from phpgw_categories WHERE cat_name = :categoria";
+                $phpgw_query = new AwlQuery($x,array(':categoria' => $categoria));                                   
+                $phpgw_query->Exec();                                              
+                if ( $phpgw_query->rows() == 0 ) {                                   
+                    $qry = new AwlQuery("INSERT INTO phpgw_categories (cat_id,cat_main,cat_parent,cat_level,cat_owner,cat_access,cat_appname,cat_name,cat_description,cat_data,last_mod) VALUES ( default,default,default,default,:nome,'private','calendar',:categoria,'','N;',:mod)", array(':nome' => $nome, ':categoria' => $categoria, ':mod' => $MOD));                                                                                                                                                                                          
+                    if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                           
+                    $x= "select * from phpgw_categories WHERE cat_name = '$categoria'";                                                                                                                                                      
+                    $phpgw_query =  new AwlQuery($x,array(':categoria' => $categoria));                                                                                                                                                                                         
+                    $phpgw_query->Exec();                                                                                                                                                                                                    
+                    $categg = $phpgw_query->Fetch();                                                                                                                                                                                         
+                    $category_id = $categg->cat_id;                                                                                                                                                                                          
+                   }
+                 else
+                   { while ($categgg = $phpgw_query->Fetch())
+                      {                                      
+                        if ( $categgg->cat_access == 'public' || $categgg->cat_owner == "$nome")
+                         {                                                                      
+                            $category_id = $categgg->cat_id;                                    
+                         }                                                                      
+                        else                                                                    
+                         { $category_id ="";                                                    
+                         }                                                                      
+                       }                                                                        
+                   }                                                                            
+         }                                                                                      
+        else                                                                                    
+         {                                                                                      
+            $category_id ="";                                                                   
+          }                                                                                     
+
+        //################# ALARME ##################
+
+        $tipo_data = substr($alarme_time, -1);
+        switch ($tipo_data) {                 
+                case "M":                     
+                        $valor = 60;          
+                        $tamanho = strlen($alarme_time);
+                        if ($tamanho == 6) {            
+                                $minutos = substr($alarme_time, 3, 5);
+                                $reducao = $minutos * $valor;         
+                        } else {                                      
+                                $minutos = substr($alarme_time, 3, 4);
+                                $reducao = $minutos * $valor;         
+                        }                                             
+                        break;                                        
+                case "H":                                             
+                        $valor = 60;                                  
+                        $tamanho = strlen($alarme_time);              
+                        if ($tamanho == 6) {                          
+                                $horas = substr($alarme_time, 3, 5);  
+                                $reducao = $horas * $valor * $valor;  
+                        } else {                                      
+                                $horas = substr($alarme_time, 3, 4);  
+                                $reducao = $horas * $valor * $valor;  
+                        }                                             
+                        break;                                        
+                case "D":                                             
+                        $valor = 60;                                  
+                        $num_horas = 24;                              
+                        $tamanho = strlen($alarme_time);              
+                        if ($tamanho == 5) {                          
+                                $dias = substr($alarme_time, 2, 4);   
+                                $reducao = $dias * $valor * $valor * $num_horas;
+                        } else {                                                
+                                $dias = substr($alarme_time, 2, 3);             
+                                $reducao = $dias * $valor * $valor * $num_horas;
+                        }                                                       
+                        break;                                                  
+                case "W":                                                       
+                        $valor = 60;                                            
+                        $num_horas = 24;                                        
+                        $num_dias = 7;                                          
+                        $tamanho = strlen($alarme_time);                        
+                        if ($tamanho == 5) {                                    
+                                $semanas = substr($alarme_time, 2, 4);          
+                                $reducao = $semanas * $valor * $valor * $num_horas * $num_dias;
+                        } else {                                                               
+                                $semanas = substr($alarme_time, 2, 3);                         
+                                $reducao = $semanas * $valor * $valor * $num_horas * $num_dias;
+                        }                                                                      
+                case "S":                                                                      
+                        continue;                                                              
+        }                                                                                      
+
+        $alarme = $DT_START - $reducao;
+        // ########### participantes ######################
+        $participantes =  $first->GetProperties('ATTENDEE');             
+        //$test = $ic->component->FirstNonTimezone();      
+        //$propriedades = $test->GetProperties('PARTSTAT');
+        //foreach ($propriedades as $k => $c){             
+        //}                                                    
+        dbg_log_array( "PUTTTTTT", 'PARTICIPANTES ', $participantes, true );  
+        $mails = "";
+        $part = array();
+        $estado = array();
+        if (is_array($participantes)) {
+                $email=$nome;          
+                foreach($participantes AS $k => $v) {
+                        $user = $v->content;         
+                        list($aviso, $mail) = split(":", $user, 2);
+                        $filtro = "mail=$mail";                    
+                        $email=$email.",".$mail;                   
+                        $atributos = array("uidNumber");           
+                        //$atrr = new  ldapDrivers($c->authenticate_hook['config']);
+                        if (strlen($filtro) > 5){                                   
+                                $usuario = ldapDrivers::requestAtributo($filtro, $atributos);
+                                if ($usuario != false) {                                     
+                                        $part[$k] = $usuario['uidNumber'];                                         
+                                        $stat = $v->GetParameterValue('PARTSTAT');                                 
+                                        switch ($stat){                                                            
+                                                case "NEEDS-ACTION": $estado[$k] = 'U';                            
+                                                        break;                                                     
+                                                case "ACCEPTED": $estado[$k] = 'A';                                
+                                                        break;                                                     
+                                                case "DECLINED": $estado[$k] = 'R';                                
+                                                        break;                                                     
+                                                case "TENTATIVE": $estado[$k] = 'T';                               
+                                                        break;                                                     
+                                        } 
+                                  dbg_error_log("PUT", "usuario participante: '%s' status '%s' e token '%d' ", $part[$k],$stat,$k);
+                                } else {
+                                        $mails = $mails . "$mail,";                                                
+                                }                                                                                  
+                        }                                                                                          
+                }                                                                                                  
+        } elseif (strlen($participantes) > 1) {                                                                    
+                $part = array();                                                                                   
+                list($aviso, $mail) = split(":", $participantes, 2);                                               
+                $filtro = "mail=$mail";                                                                            
+                $email=$email.",".$mail;                                                                           
+                $atributos = array("uidNumber");                                                                   
+                if (strlen($filtro) > 5){                                                                          
+                        $usuario = ldapDrivers::requestAtributo($filtro, $atributos);                              
+                        if ($usuario != false) {                                                                   
+                                $part[0] = $usuario['uidNumber'];                                                  
+                                $teste = $first->CollectParameterValues('PARTSTAT');                           
+                                foreach( $teste as $k => $v){                                                          
+                                        $stat = $k;                                                                    
+                                        }                                                                              
+                                switch ($stat){                                                                        
+                                        case "NEEDS-ACTION": $estado[0] = 'U';                                         
+                                                break;                                                                 
+                                        case "ACCEPTED": $estado[0] = 'A';                                             
+                                                break;                                                                 
+                                        case "DECLINED": $estado[0] = 'R';                                             
+                                                break;                                                                 
+                                        case "TENTATIVE": $estado[0] = 'T';                                            
+                                                break;                                                                 
+                                }                                                                                      
+
+                        } else {
+                                $mails = $mail;
+                        }                      
+                }                              
+        }
+                   
+        $email = trim($email,',');
+        if ($nome != $proprietario && strlen($proprietario > 5)){
+                if (is_array($part)) {                           
+                        foreach($part AS $k => $v) {             
+                                         if (isset($alarme_descarta) && $part[$k] == $nome) {
+                                                    $laststack = "X-MOZ-LASTACK-PART-$nome"; 
+                                                    $qryusr =  new AwlQuery("select * from phpgw_cal_extra where cal_id =:cal_id AND cal_extra_name=:cal_extra AND cal_extra_value=:cal_extra_value ",array(':cal_id' => $CALID, ':cal_extra' => $laststack,':cal_extra_value' => $alarme_descarta));
+                                                    if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                     
+                                                    if ($qryusr->rows() == 0) {                                                                                                                             
+
+                                                    $qryusr =  new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal_id,:cal_extra,:cal_extra_value)",array(':cal_id' => $CALID, ':cal_extra' => $laststack,':cal_extra_value' => $alarme_descarta));
+                                                    if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                   
+                                                    }                                                                                                                                                   
+                                         }                                                                                                                                                              
+
+                                $qryuppart = new AwlQuery("DELETE from phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID, ':part' => $part[$k]));
+                                if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);       
+                                $qryslt = new AwlQuery("SELECT * FROM phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID, ':part' => $part[$k])); 
+                                $result=$qryslt->Exec("PUT");                                                                              
+                                if ($result->rows() < 1){                                                                                    
+                                        $consulta = "INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id,:part,:estado,'u')";
+                                        $qrypart = new AwlQuery($consulta,array(':cal_id' => $CALID, ':part' => $part[$k], ':estado' => $estado[$k]));                                                                                               
+                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                               
+                                }                                                                                                                                        
+                                else{                                                                                                                                    
+                                        $consulta  = "UPDATE phpgw_cal_user SET ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id,:part,:estado,'u') WHERE cal_id = $CALID AND cal_login = :par";
+                                        $qrypart = new AwlQuery($consulta,array(':cal_id' => $CALID, ':part' => $part[$k], ':estado' => $estado[$k],':par' => $part[$k] ));                                                                                                                                
+
+                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                                }                                                                                                         
+                        }                                                                                                                 
+                }                                                                                                                         
+
+        }
+        else{
+        $hostbanco = "-@{$iporigem}";
+        if ($put_action_type == 'INSERT') {
+                
+                $qry = new AwlQuery("INSERT INTO phpgw_cal ( cal_id, uid, owner, category, datetime, mdatetime, edatetime,
+                  priority,cal_type,is_public, title, location, description, reference ) VALUES ( DEFAULT,:hostbanco, :nome, :category_id, :DT_START, :MOD, :DT_END, :prioridade, :tipo, :public, :titulo, :cidade, :descricao,0)",array( ':hostbanco' => $hostbanco, ':nome' => $nome, ':category_id' =>  $category_id, ':DT_START' => $DT_START, ':MOD' => $MOD, ':DT_END' => $DT_END, ':prioridade' => $prioridade, ':tipo' => $tipo, ':public' => $public, ':titulo' => $titulo, ':cidade' => $cidade, ':descricao' => $descricao));
+
+                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                $qryid = new AwlQuery("SELECT max(cal_id) FROM phpgw_cal WHERE owner = :nome", array(':nome' => $nome));             
+                $qryid->Exec("PUT");                                                                          
+                $cal = $qryid->Fetch();                                                                       
+
+
+                if ($DT_START != $alarme) {
+                        $id = "cal:{$cal->max}:0";
+                        $next = $alarme;          
+                        $time = "i:{$alarme}";    
+                        $method = "calendar.bocalendar.send_alarm";
+                        $data = "a:5:{s:4:\"time\";{$time};s:6:\"offset\";i:{$reducao};s:5:\"owner\";i:{$nome};s:7:\"enabled\";i:1;s:6:\"cal_id\";s:5:\"{$cal->max}\";}";
+                        $account_id = $nome;                                                                                                                             
+                        $qry = new AwlQuery("INSERT INTO phpgw_async ( id, next, times, method, data, account_id) VALUES ( :id, :next, :time, :method, :data, :account_id)", array(':id' => $id,':next' => $next, ':time' => $time,':method' => $method, ':data' =>  $data, ':account_id' => $account_id));
+                        if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+                        if (isset($alarme_descarta)) {                                                                                                                                   
+                                $qryusr = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-LASTACK',:alarme)", array(':cal' => $cal->max, ':alarme' => $alarme_descarta));
+                                if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                          
+                        }                                                                                                                                                                  
+
+                }
+                if ($tipo == 'M') {
+                        $total = preg_split("[;]", $RECUR, -1, PREG_SPLIT_NO_EMPTY);
+                        $numero = count($total);                                    
+                        $recur_enddate = 0;                                         
+                        $recur_data = 0;                                            
+                        $intervalo = 1;                                             
+                        foreach($total as $valor) {                                 
+                                list($ARG, $val) = split('[=]', $valor);            
+                                switch ($ARG) {                                     
+                                        case "FREQ":                                
+                                                switch ($val) {                     
+                                                        case "DAILY":               
+                                                                $tipo = 1;          
+                                                                break;              
+                                                        case "WEEKLY":              
+                                                                $tipo = 2;          
+                                                                break;              
+                                                        case "MONTHLY":             
+                                                                $tipo = 3;          
+                                                                break;              
+                                                        case "YEARLY":              
+                                                                $tipo = 5;          
+                                                                break;              
+                                                }                                   
+                                                break;                              
+                                                        case "UNTIL":               
+                                                                $dateTime_until = date_create($val);
+                                                                $recur_enddate = $dateTime_until->format("U");
+
+                                                                break;
+                                                        case "COUNT": 
+                                                                switch ($tipo) {
+                                                                        case 1: 
+                                                                                $dias = $intervalo * ($val - 1) * 86400;
+                                                                                $recur_enddate = $DT_END + $dias;       
+                                                                                break;                                  
+                                                                        case 2:                                         
+                                                                                $dias = $intervalo * ($val - 1) * 86400 * 7;
+                                                                                $recur_enddate = $DT_END + $dias;           
+                                                                                break;                                      
+                                                                        case 3:                                             
+                                                                                $dias = $intervalo * ($val - 1) * 86400 * 31;
+                                                                                $recur_enddate = $DT_END + $dias;            
+                                                                                break;                                       
+                                                                        case 5:                                              
+                                                                                $dias = $intervalo * ($val - 1) * 86400 * 365;
+                                                                                $recur_enddate = $DT_END + $dias;             
+                                                                                break;                                        
+                                                                }                                                             
+                                                                break;                                                        
+                                                        case "INTERVAL":                                                      
+                                                                $intervalo = $val;                                            
+                                                                break;                                                        
+                                                        case "BYDAY":                                                         
+                                                                                //$semana = preg_split("[,]", $val, -1, PREG_SPLIT_NO_EMPTY);
+                                                                                dbg_error_log("PUT", "diaVAL : '%s' ", $val);                
+                                                                                $semana = explode(",",$val);                                 
+                                                                                if (count($semana , 1) == 1){                                
+                                                                                        array_push($semana , strtoupper(substr(date("D",$DT_START) ,0,2)));
+                                                                                }                                                                          
+                                                                                $recur_data = 0;                                                           
+                                                                                foreach($semana as $dia) {                                                 
+                                                                                        switch ($dia) {                                                    
+                                                                                                case "SU":                                                 
+                                                                                                        $recur_data = $recur_data + 1;                     
+                                                                                                        break;                                             
+                                                                                                case "MO":                                                 
+                                                                                                        $recur_data = $recur_data + 2;                     
+                                                                                                        break;                                             
+                                                                                                case "TU":                                                 
+                                                                                                        $recur_data = $recur_data + 4;                     
+                                                                                                        break;                                             
+                                                                                                case "WE":                                                 
+                                                                                                        $recur_data = $recur_data + 8;                     
+                                                                                                        break;                                             
+                                                                                                case "TH":                                                 
+                                                                                                        $recur_data = $recur_data + 16;                    
+                                                                                                        break;                                             
+                                                                                                case "FR":                                                 
+                                                                                                        $recur_data = $recur_data + 32;                    
+                                                                                                        break;                                             
+                                                                                                case "SA":                                                 
+                                                                                                        $recur_data = $recur_data + 64;                    
+                                                                                                        break;                                             
+                                                                                        }                                                                  
+                                                                                }                                                                          
+                                                                break;                                                                                     
+                                                        default:                                                                                           
+                                                                continue;                                                                                  
+                                }                                                                                                                          
+                                if ($recur_data == 62)                                                                                                     
+                                $tipo = 2;                                                                                                                 
+                        }                                                                                                                                  
+                        $qryrepet = new AwlQuery("INSERT INTO phpgw_cal_repeats ( cal_id,recur_type, recur_use_end, recur_enddate, recur_interval,recur_data, recur_exception ) VALUES ( :cal,:tipo,0,:recur,:intervalo,:data,:exec)", array( ':cal' => $cal->max,':tipo' => $tipo, ':recur' => $recur_enddate, ':intervalo' => $intervalo, ':data' => $recur_data, ':exec' => $EXEC));                                                                                                                                                                                                               
+                        if (!$qryrepet->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                  
+                }                                                                                                                                                                                                                            
+                $qryusr = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:nome,'A','u')", array(':cal' => $cal->max, ':nome' => $nome));                                                                                      
+                if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                            
+                if (is_array($part)) {                                                                                                                                                                                                       
+                        foreach($part AS $k => $v) {
+                                $qrypart = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:part,'U','u')", array(':cal' => $cal->max, ':part' => $part[$k]));                                                                 
+                                if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                           
+                        }                                                                                                                                                                                                                    
+                }                                                                                                                                                                                                                            
+                if (isset($mails)) {                                                                                                                                                                                                         
+                        $qrypart_ex = new AwlQuery("UPDATE phpgw_cal SET ex_participants=:expart WHERE owner=:nome AND cal_id=:cal", array(':expart' => $mails, ':nome' => $nome, ':cal' => $cal->max));                                                                                            
+                        if (!$qrypart_ex->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                
+                }                                                                                                                                                                                                                            
+
+                if ( $first->GetPValue('X-MOZ-SEND-INVITATIONS') == 'TRUE' ){
+                        $qrypart = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-SEND-INVITATIONS','true')", array(':cal' => $cal->max));
+                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                         
+                }                                                                                                                                                                  
+        } elseif ($put_action_type == 'UPDATE') {                                                                                                                                  
+                if ( $recur_id != null) {                                                                                                                                                                 
+                                $reference = substr( $UID ,0, strpos( $UID ,'@'));                                                                                                                                  
+                                $tipo = "E";                                                                                                                                                                        
+                                $qry = new AwlQuery("INSERT INTO phpgw_cal                                                                                                                                          
+                                        ( cal_id, uid, owner, category, datetime, mdatetime, edatetime,  priority,                                                                                                  
+                                        cal_type , is_public , title, location, description, reference )                                                                                                            
+                                        VALUES ( DEFAULT,:hostbanco,:nome,:category_id,:DTSTART,:MOD,:DTEND,:prioridade,:tipo,:public,:titulo,:cidade,:descricao,:ref)",array(':hostbanco' => $hostbanco, ':nome' => $nome,':category_id' => $category_id,':DTSTART' => $DT_START, ':MOD' => $MOD, ':DTEND' => $DT_END, ':prioridade' => $prioridade, ':tipo' => $tipo, ':public' => $public, ':titulo' => $titulo, ':cidade' => $cidade, ':descricao' => $descricao,':ref' => $reference));                                                                                                                                                                                                                                          
+                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                                 
+                                $qryid = new AwlQuery("SELECT max(cal_id) FROM phpgw_cal WHERE owner = :nome", array(':nome' => $nome));                                                                                                     
+                                $qryid->Exec("PUT");                                                                                                                                                                                         
+                                $call = $qryid->Fetch();                                                                                                                                                                                     
+                                $CALID = $call->max;
+                                $qry = new AwlQuery("INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal,:nome,'A','u');",array(':cal' => $call->max,':nome' => $nome));                                     
+                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                                 
+                                $id_cal = "cal:$reference:0";                                                                                                                                                                                
+                                $qryid = new Awlquery("SELECT * FROM phpgw_async WHERE id = :id_cal", array(':id_cal' => $id_cal));                                                                                                          
+                                dbg_error_log("PUT", "argumento : '%s' igual '%s' ", $DT_START, $alarme);                                                                                                                                    
+                                if ( $qryid->Exec("PUT",__LINE__,__FILE__) && $qryid->rows() > 0 && $DT_START != $alarme)                                                                                                                    
+                                   {  $alarmew = $qryid->Fetch();                                                                                                                                                                            
+                                      $data_seg = dataTParaSegundos($recur_id["DTSTART"]);                                                                                                                                                   
+                                      $tempo_ant = $data_seg - $reducao;                                                                                                                                                                     
+                                      $id_cal_novo = "cal:$call->max:0";                                                                                                                                                                     
+                                      $timess = "i:$tempo_ant";                                                                                                                                                                              
+                                      $dados_alarme= "a:5:{s:4:\"time\";i:$tempo_ant;s:6:\"offset\";i:$reducao;s:5:\"owner\";i:$nome;s:7:\"enabled\";i:1;s:6:\"cal_id\";s:5:\"$call->max\";}";                                               
+                                      $qrysy = new PgQuery("INSERT INTO phpgw_async ( id, next, times, method, data, account_id) VALUES ( :id,:next,:times,'calendar.bocalendar.send_alarm',:data,:account);", array(':id' => $id_cal_novo,':next' => $tempo_ant,':times' => $timess,':data' => $dados_alarme,':account' => $nome));                                                                                                                                                                                                                                                                                                                                                                                                   
+                                      if (!$qrysy->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                         
+                                                                                                                                                                                                                                             
+                                }
+                               /* 
+                               if (is_array($part)) {
+                                      foreach($part AS $k => $v) {
+                                             if ($estado[$k] != 'U'){
+                                                       $qryuppart = new AwlQuery("DELETE from phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID,':part' => $part[$k])); 
+                                                       if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                                                       $qryslt = new AwlQuery("SELECT * FROM phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID,':part' => $part[$k]));                                                $
+                                                       $result=$qryslt->Exec("PUT");
+                                                       if ($result->rows < 1){
+                                                                     $consulta = "INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id ,:part,:estado,'u')";
+                                                                     $qrypart = new AwlQuery($consulta, array(':cal_id' => $CALID,':part' => $part[$k],':estado' => $estado[$k]));                                                                               $
+                                                                     if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                                                                  }
+                                                       else{
+                                                               $consulta  = "UPDATE phpgw_cal_user SET ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id ,:part,:estado,'u') WHERE cal_id = $CALID AND cal_login = :par";                        $
+                                                               $qrypart = new AwlQuery($consulta, array(':cal_id' => $CALID,':part' => $part[$k],':estado' => $estado[$k],':par' => $part[$k]));                                                           $
+                                                               if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                                                       }
+                                             }
+                                      }
+                                }
+                               if (isset($mails)) {
+                                             $qrypart_ex = new AwlQuery("UPDATE phpgw_cal SET ex_participants=:expart WHERE owner=:nome AND cal_id=:cal_id", array(':expart' => $mails,':nome' =>  $nome,':cal_id' => $call->max));
+                                             if (!$qrypart_ex->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                               } 
+                              */                                                                                                                                                                                                           
+                 }
+              else
+                 { 
+              //if( isset($alarme_descarta) || isset($adia_alarme))
+              //   { }
+              // else 
+              //  {  
+                 $qry = new AwlQuery("UPDATE phpgw_cal SET cal_id=:cal ,uid=:hostbanco ,owner=:nome ,category=:category_id , datetime=:DT_START, mdatetime=:MOD, edatetime=:DT_END, priority=:prioridade, cal_type=:tipo, is_public=:public, title=:titulo , description=:descricao, location=:location, ex_participants=:expart  WHERE owner=:nome AND cal_id=:cal_id", array( ':cal' => $CALID  ,':hostbanco' => $hostbanco, ':nome' => $nome, ':category_id' =>  $category_id, ':DT_START' => $DT_START, ':MOD' => $MOD, ':DT_END' => $DT_END, ':prioridade' => $prioridade, ':tipo' => $tipo, ':public' => $public, ':titulo' => $titulo,':descricao' => $descricao,':location' => $cidade,':expart' => $mails,':nome' => $nome,':cal_id' => $CALID ));                                                               
+                 if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+              //  }                                                                                                                               
+                if ($DT_START != $alarme) {                                                                                                                                                                                                  
+                        $id = "cal:$CALID:0";                                                                                                                                                                                                
+                        $next = "$alarme";                                                                                                                                                                                                   
+                        $time = "i:$alarme";                                                                                                                                                                                                 
+                        $method = "calendar.bocalendar.send_alarm";                                                                                                                                                                          
+                        $data = "a:5:{s:4:\"time\";$time;s:6:\"offset\";i:$reducao;s:5:\"owner\";i:$nome;s:7:\"enabled\";i:1;s:6:\"cal_id\";s:5:\"$CALID\";}";                                                                               
+                        $account_id = "$nome";                                                                                                                                                                                               
+                        $qry = new AwlQuery("UPDATE  phpgw_async set next=:next,  times=:time,  data=:data,  account_id=:account WHERE id=:id", array(':next' => $next,':time' => $time, ':data' => $data, ':account' => $account_id, ':id' => $id));                                                                            
+                        $qry->Exec("PUT");                                                                                                                                                                                                   
+                        $qry = new AwlQuery("select * from phpgw_cal_extra WHERE cal_id=:cal_id", array(':cal_id' => $CALID));                                                                                                                                          
+                        $qry->Exec("PUT");                                                                                                                                                                                                   
+                        dbg_error_log("PUT", "argumento : '%s' valor : '%s' ", $alarme_descarta);                                                                                                                              
+                        if ($qry->rows() != 0) {                                                                                                                                                                                               
+                                if (isset($alarme_descarta)) {                                                                                                                                                                               
+                                        $qryusr = new AwlQuery("UPDATE phpgw_cal_extra set cal_extra_value=:alarme WHERE cal_id=:cal_id AND cal_extra_name = 'X-MOZ-LASTACK'",array(':alarme' =>  $alarme_descarta, ':cal_id' => $CALID));                                                 
+                                        $qryusr->Exec("PUT");                                                                                                                                                                                
+                                }                                                                                                                                                                                                            
+                                if (isset($adia_alarme)) {                                                                                                                                                                                   
+                                        $qryusr = new AwlQuery("UPDATE phpgw_cal_extra set cal_extra_value=:calextravalue WHERE cal_id=:cal AND cal_extra_name =:cal_extra ", array(':calextravalue' => $adia_alarme ,':cal' => $CALID, ':cal_extra' => $snooze));                                                          
+                                        $qryusr->Exec("PUT");                                                                                                                                                                                
+                                }                                                                                                                                                                                                            
+
+                        } else {
+                                  
+                                if (isset($alarme_descarta)) {
+                                        $qryusr = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-LASTACK',:alarme)", array(':cal' => $CALID,':alarme' =>  $alarme_descarta));
+                                        if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                       
+                                }                                                                                                                                                               
+                                if (isset($adia_alarme)) {                                                                                                                                      
+                                        $qryusr = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,:snooze,:adia)", array(':cal' => $CALID,':snooze' =>  $snooze, ':adia' => $adia_alarme));         
+                                        if (!$qryusr->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                       
+                                }                                                                                                                                                               
+                                                                                                                                                                                                
+                        }                                                                                                                                                                       
+                                                                                                                                                                                                
+                        $qry = new AwlQuery("select id from phpgw_async WHERE id=:id", array(':id' => $id));                                                                                                       
+                        $qry->Exec("PUT");                                                                                                                                                      
+                        if ($qry->rows() == 0) {                                                                                                                                                  
+                                $qry = new AwlQuery("INSERT INTO phpgw_async ( id, next, times, method, data, account_id) VALUES (:id, :next, :time, :method, :data, :account_id)",array(':id' => $id,':next' => $next,':time' => $time,':method' => $method, ':data' => $data, ':account_id' => $account_id));
+                                if (!$qry->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                   
+                        }                                                                                                                                                                        
+                }                                                                                                                                                                                
+
+                if ((strlen($alarme_time) == 0) && ($tipo == 'E')) {                                                                                                                             
+                        $id = "cal:$CALID:0";                                                                                                                                                    
+                        $qry = new AwlQuery("DELETE FROM phpgw_async WHERE id=:id", array(':id' => $id));                                                                                                           
+                        $qry->Exec("PUT");                                                                                                                                                       
+                }                                                                                                                                                                                
+                if ($tipo == 'M') {                                                                                                                                                              
+                        $total = preg_split("[;]", $RECUR, -1, PREG_SPLIT_NO_EMPTY);                                                                                                                                                         
+                        $numero = count($total);                                                                                                                                                                                             
+                        $recur_enddate = 0;                                                                                                                                                                                                  
+                        $recur_data = 0;                                                                                                                                                                                                     
+                        foreach($total as $valor) {                                                                                                                                                                                          
+                                list($ARG, $val) = split('[=]', $valor);                                                                                                                                                                     
+                                dbg_error_log("PUT", "argumento : '%s' valor : '%s' ", $ARG, $val);                                                                                                                                          
+                                switch ($ARG) {                                                                                                                                                                                              
+                                        case "FREQ":                                                                                                                                                                                         
+                                                switch ($val) {                                                                                                                                                                              
+                                                        case "DAILY":                                                                                                                                                                        
+                                                                $tipo = 1;                                                                                                                                                                   
+                                                                break;                                                                                                                                                                       
+                                                        case "WEEKLY":                                                                                                                                                                       
+                                                                $tipo = 2;                                                                                                                                                                   
+                                                                break;                                                                                                                                                                       
+                                                        case "MONTHLY":                                                                                                                                                                      
+                                                                $tipo = 3;                                                                                                                                                                   
+                                                                break;                                                                                                                                                                       
+                                                        case "YEARLY":                                                                                                                                                                       
+                                                                $tipo = 5;                                                                                                                                                                   
+                                                                break;                                                                                                                                                                       
+                                                }                                                                                                                                                                                            
+                                                break;                                                                                                                                                                                       
+                                                        case "UNTIL":                                                                                                                                                                        
+                                                                //$recur_enddate = dataTDiaSeguinteSegundos($val ,1);                                                                                                                        
+                                                                $dateTime_until = date_create($val);                                                                                                                                         
+                                                                $recur_enddate = $dateTime_until->format("U");                                                                                                                               
+                                                                break;                                                                                                                                                                       
+                                                        case "COUNT":                                                                                                                                                                        
+                                                                switch ($tipo) {                                                                                                                                                             
+                                                                        case 1:                                                                                                                                                              
+                                                                                $dias = $intervalo * $val * 86400;                                                                                                                           
+                                                                                $recur_enddate = $DT_END + $dias;                                                                                                                            
+                                                                                break;                                                                                                                                                       
+                                                                        case 2:                                                                                                                                                              
+                                                                                $dias = $intervalo * $val * 86400 * 7;                                                                                                                       
+                                                                                $recur_enddate = $DT_END + $dias;                                                                                                                            
+                                                                                break;                                                                                                                                                       
+                                                                        case 3:                                                                                                                                                              
+                                                                                $dias = $intervalo * $val * 86400 * 31;                                                                                                                      
+                                                                                $recur_enddate = $DT_END + $dias;                                                                                                                            
+                                                                                break;                                                                                                                                                       
+                                                                        case 5:                                                                                                                                                              
+                                                                                $dias = $intervalo * $val * 86400 * 365;                                                                                                                     
+                                                                                $recur_enddate = $DT_END + $dias;                                                                                                                            
+                                                                                break;                                                                                                                                                       
+                                                                }                                                                                                                                                                            
+                                                                break;                                                                                                                                                                       
+                                                                        case "INTERVAL":                                                                                                                                                     
+                                                                                $intervalo = $val;                                                                                                                                           
+                                                                        case "BYDAY":                                                                                                                                                        
+                                                                                $semana = preg_split("[,]", $val, -1, PREG_SPLIT_NO_EMPTY);                                                                                                  
+                                                                                if (count($semana , 1) == 1){                                                                                                                                
+                                                                                        dbg_error_log("PUT", "dia1 : '%s' ", count($semana));                                                                                                
+                                                                                        array_push($semana , strtoupper(substr(date("D",$DT_START) ,0,2)));                                                                                  
+                                                                                }                                                                                                                                                            
+                                                                                $recur_data=0;                                                                                                                                               
+                                                                                foreach($semana as $dia) {                                                                                                                                   
+                                                                                        dbg_error_log("PUT", "dia 2 : '%s' ", $dia);                                                                                                         
+                                                                                        switch ($dia) {                                                                                                                                      
+                                                                                                case "SU":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 1;                                                                                                       
+                                                                                                        break;                                                                                                                               
+                                                                                                case "MO":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 2;                                                                                                       
+                                                                                                        break;                                                                                                                               
+                                                                                                case "TU":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 4;                                                                                                       
+                                                                                                        break;                                                                                                                               
+                                                                                                case "WE":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 8;                                                                                                       
+                                                                                                        break;                                                                                                                               
+                                                                                                case "TH":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 16;                                                                                                      
+                                                                                                        break;                                                                                                                               
+                                                                                                case "FR":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 32;                                                                                                      
+                                                                                                        break;                                                                                                                               
+                                                                                                case "SA":                                                                                                                                   
+                                                                                                        $recur_data = $recur_data + 64;                                                                                                      
+                                                                                                        break;                                                                                                                               
+                                                                                        }                                                                                                                                                    
+                                                                                }                                                                                                                                                            
+                                                                                break;                                                                                                                                                       
+                                                                                                default:                                                                                                                                     
+                                                                                                        continue;                                                                                                                            
+                                }                                                                                                                                                                                                            
+                                if ($recur_data == 62)                                                                                                                                                                                       
+                                $tipo = 2;                                                                                                                                                                                                   
+                        }                                                                                                                                                                                                                    
+                        $qryrepet = new AwlQuery("UPDATE  phpgw_cal_repeats set  cal_id=:cal,recur_type=:recur , recur_enddate=:data, recur_interval=:intervalo, recur_data=:recurdata , recur_exception=:exception where cal_id = :cal_id", array(':cal' => $CALID,':recur' => $tipo, ':data' => $recur_enddate, ':intervalo' =>  $intervalo,':recurdata' =>  $recur_data, ':exception' => $EXEC, ':cal_id' => $CALID));                                                                                                                                                                                                                     
+                        if (!$qryrepet->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                  
+                }
+                //$qryuppart = new PgQuery("DELETE from phpgw_cal_user WHERE cal_id = ? AND cal_status = 'U' AND cal_login = ?",$CALID,$nome);                                                                                               
+                //if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                                       
+                if (is_array($part)) {                                                                                                                                                                                                       
+                        foreach($part AS $k => $v) {                                                                                                                                                                                         
+                                if ($estado[$k] != 'R'){                                                                                                                                                                                     
+                                        $qryuppart = new AwlQuery("DELETE from phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID,':part' => $part[$k]));                                                                                          
+                                        if (!$qryuppart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                                 
+                                        $qryslt = new AwlQuery("SELECT * FROM phpgw_cal_user WHERE cal_id = :cal_id AND cal_login = :part",array(':cal_id' => $CALID,':part' => $part[$k]));                                                                                           
+                                        $result=$qryslt->Exec("PUT");                                                                                                                                                                        
+                                        if ($result->rows < 1){                                                                                                                                                                              
+                                                $consulta = "INSERT INTO phpgw_cal_user ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id ,:part,:estado,'u')";                                                            
+                                                $qrypart = new AwlQuery($consulta, array(':cal_id' => $CALID,':part' => $part[$k],':estado' => $estado[$k]));                                                                                                                                                           
+                                                if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                                                                                           
+                                        }                                                                                                                                                                                                    
+                                        else{                                                                                                                                                                                                
+                                                $consulta  = "UPDATE phpgw_cal_user SET ( cal_id, cal_login, cal_status, cal_type) VALUES ( :cal_id ,:part,:estado,'u') WHERE cal_id = $CALID AND cal_login = :par";                           
+                                                $qrypart = new AwlQuery($consulta, array(':cal_id' => $CALID,':part' => $part[$k],':estado' => $estado[$k],':par' => $part[$k]));                                                                                                                                                           
+
+                                                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                                        }                                                                                                                 
+                                }                                                                                                                         
+                        }                                                                                                                                 
+                }                                                                                                                                         
+                if (isset($mails)) {                                                                                                                      
+                        $qrypart_ex = new AwlQuery("UPDATE phpgw_cal SET ex_participants=:expart WHERE owner=:nome AND cal_id=:cal_id", array(':expart' => $mails,':nome' =>  $nome,':cal_id' => $CALID));         
+                        if (!$qrypart_ex->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);                             
+                }
+                if ( $first->GetPValue('X-MOZ-SEND-INVITATIONS') == 'TRUE' ){
+                        $qrypart = new AwlQuery("INSERT INTO phpgw_cal_extra ( cal_id, cal_extra_name, cal_extra_value) VALUES ( :cal,'X-MOZ-SEND-INVITATIONS','true')", array(':cal' => $CALID));
+                        if (!$qrypart->Exec("PUT")) rollback_on_error($caldav_context, $user_no, $path);
+                }
+              }                                                                                                                                           
+            }
+        }
+        return true;
+
+}
+function write_resource_old( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
+  global $tz_regex;
+
+  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+  if ( !isset($resources[0]) ) {
+    $resource_type = 'Unknown';
+    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+    rollback_on_error( $caldav_context, $user_no, $path, translate('No calendar content'), 412 );
+    return false;
+  }
+  else {
+    $first = $resources[0];
+    $resource_type = $first->GetType();
+  }
+  dbg_log_array('PUT','VTIMEZONE',$first,true);
+  $qry = new AwlQuery();
+  $qry->Begin();
+  $params = array(
+      ':dav_name' => $path,
+      ':user_no' => $user_no,
+      ':etag' => $etag,
+      ':dav_data' => $caldav_data,
+      ':caldav_type' => $resource_type,
+      ':session_user' => $author,
+      ':weak_etag' => $weak_etag
+  );
+  if ( $put_action_type == 'INSERT' ) {
+    create_scheduling_requests($vcal);
+    $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
+            VALUES( :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )';
+    $params[':collection_id'] = $collection_id;
+  }
+  else {
+    update_scheduling_requests($vcal);
+    $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
+            modified=current_timestamp, weak_etag=:weak_etag WHERE user_no=:user_no AND dav_name=:dav_name';
+  }
+  if ( !$qry->QDo($sql,$params) ) {
+    rollback_on_error( $caldav_context, $user_no, $path);
+    return false;
+  }
+
+  $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
+  if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+    $dav_id = $row->dav_id;
+  }
+
+   
+  $calitem_params = array(
+      ':dav_name' => $path,
+      ':user_no' => $user_no,
+      ':etag' => $etag
+  );
+  $dtstart = $first->GetPValue('DTSTART');
+  $calitem_params[':dtstart'] = $dtstart;
+  if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+    $dtstart = $first->GetPValue('DUE');
+  }
+
+  $dtend = $first->GetPValue('DTEND');
+  if ( isset($dtend) && $dtend != '' ) {
+    dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+    $calitem_params[':dtend'] = $dtend;
+    $dtend = ':dtend';
+  }
+  else {
+    $dtend = 'NULL';
+    if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+      $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+      $dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
+      $calitem_params[':duration'] = $duration;
+    }
+    elseif ( $first->GetType() == 'VEVENT' ) {
+      /**
+      * From RFC2445 4.6.1:
+      * For cases where a "VEVENT" calendar component specifies a "DTSTART"
+      * property with a DATE data type but no "DTEND" property, the events
+      * non-inclusive end is the end of the calendar date specified by the
+      * "DTSTART" property. For cases where a "VEVENT" calendar component specifies
+      * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+      * the event ends on the same calendar date and time of day specified by the
+      * "DTSTART" property.
+      *
+      * So we're looking for 'VALUE=DATE', to identify the duration, effectively.
+      *
+      */
+     
+      $value_type = $first->GetPParamValue('DTSTART','VALUE');
+      dbg_error_log('PUT','DTSTART without DTEND. DTSTART value type is %s', $value_type );
+      if ( isset($value_type) && $value_type == 'DATE' )
+        $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
+      else
+        $dtend = ':dtstart';
+    }
+  }
+
+  $last_modified = $first->GetPValue('LAST-MODIFIED');
+  if ( !isset($last_modified) || $last_modified == '' ) {
+    $last_modified = gmdate( 'Ymd\THis\Z' );
+  }
+  $calitem_params[':modified'] = $last_modified;
+
+  $dtstamp = $first->GetPValue('DTSTAMP');
+  if ( !isset($dtstamp) || $dtstamp == '' ) {
+    $dtstamp = $last_modified;
+  }
+  $calitem_params[':dtstamp'] = $dtstamp;
+
+  $class = $first->GetPValue('CLASS');
+  /* Check and see if we should over ride the class. */
+  /** @TODO: is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */
+  if ( public_events_only($user_no, $path) ) {
+    $class = 'PUBLIC';
+  }
+
+  /*
+   * It seems that some calendar clients don't set a class...
+   * RFC2445, 4.8.1.3:
+   * Default is PUBLIC
+   */
+  if ( !isset($class) || $class == '' ) {
+    $class = 'PUBLIC';
+  }
+  $calitem_params[':class'] = $class;
+
+
+  /** Calculate what timezone to set, first, if possible */
+  $last_tz_locn = 'Turkmenikikamukau';  // I really hope this location doesn't exist!
+  $tzid = $first->GetPParamValue('DTSTART','TZID');
+  if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+  $timezones = $ic->GetComponents('VTIMEZONE');
+  foreach( $timezones AS $k => $tz ) {
+    if ( $tz->GetPValue('TZID') != $tzid ) {
+      /**
+      * We'll pretend they didn't forget to give us a TZID and that they
+      * really hope the server is running in the timezone they supplied... but be noisy about it.
+      */
+      dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
+      $tzid = $tz->GetPValue('TZID');
+    }
+    // This is the one
+    $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+    if ( ! isset($tz_locn) ) {
+      if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) )
+        $tz_locn = $matches[1];
+      else if ( isset($tzid) && $tzid != '' ) {
+        dbg_error_log( 'ERROR', ' Couldn\'t guess Olsen TZ from TZID[%s].  This may end in tears...', $tzid );
+      }
+    }
+    else {
+      if ( ! preg_match( $tz_regex, $tz_locn ) ) {
+        if ( preg_match( '#([^/]+/[^/]+)$#', $tzid, $matches ) ) $tz_locn = $matches[1];
+      }
+    }
+
+    dbg_error_log( 'PUT', ' Using TZID[%s] and location of [%s]', $tzid, (isset($tz_locn) ? $tz_locn : '') );
+    if ( isset($tz_locn) && ($tz_locn != $last_tz_locn) && preg_match( $tz_regex, $tz_locn ) ) {
+      dbg_error_log( 'PUT', ' Setting timezone to %s', $tz_locn );
+      if ( $tz_locn != '' ) {
+        $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+      }
+      $last_tz_locn = $tz_locn;
+    }
+    $params = array( ':tzid' => $tzid);
+    $qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params );
+    if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+      $params[':tzlocn'] = $tz_locn;
+      $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+      $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+    }
+    if ( !isset($tz_locn) || $tz_locn == '' ) $tz_locn = $tzid;
+
+  }
+
+  $created = $first->GetPValue('CREATED');
+  if ( $created == '00001231T000000Z' ) $created = '20001231T000000Z';
+  $calitem_params[':created'] = $created;
+
+  $calitem_params[':tzid'] = $tzid;
+  $calitem_params[':uid'] = $first->GetPValue('UID');
+  $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+  $calitem_params[':location'] = $first->GetPValue('LOCATION');
+  $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+  $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+  $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+  $calitem_params[':url'] = $first->GetPValue('URL');
+  $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+  $calitem_params[':due'] = $first->GetPValue('DUE');
+  $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+  $calitem_params[':status'] = $first->GetPValue('STATUS');
+  if ( $put_action_type == 'INSERT' ) {
+    $sql = <<<EOSQL
+INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
+                dtstart, dtend, summary, location, class, transp,
+                description, rrule, tz_id, last_modified, url, priority,
+                created, due, percent_complete, status, collection_id )
+   VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp,
+                :dtstart, $dtend, :summary, :location, :class, :transp,
+                :description, :rrule, :tzid, :modified, :url, :priority,
+                :created, :due, :percent_complete, :status, $collection_id )
+EOSQL;
+    $sync_change = 201;
+  }
+  else {
+    $sql = <<<EOSQL
+UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
+                dtstart=:dtstart, dtend=$dtend, summary=:summary, location=:location, class=:class, transp=:transp,
+                description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
+                created=:created, due=:due, percent_complete=:percent_complete, status=:status
+       WHERE user_no=:user_no AND dav_name=:dav_name
+EOSQL;
+    $sync_change = 200;
+  }
+
+  write_alarms($dav_id, $first);
+  write_attendees($dav_id, $first);
+
+  if ( $log_action && function_exists('log_caldav_action') ) {
+    log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+  }
+  else if ( $log_action  ) {
+    dbg_error_log( 'PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.',
+            $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+  }
+  
+  $qry = new AwlQuery( $sql, $calitem_params );
+  if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
+    rollback_on_error( $caldav_context, $user_no, $path);
+    return false;
+  }
+  $qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
+  $qry->Commit();
+
+  dbg_error_log( 'PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path);
+
+  return true;  // Success!
+}
+
+
+
+/**
+* A slightly simpler version of write_resource which will make more sense for calling from
+* an external program.  This makes assumptions that the collection and user do exist
+* and bypasses all checks for whether it is reasonable to write this here.
+* @param string $path The path to the resource being written
+* @param string $caldav_data The actual resource to be written
+* @param string $put_action_type INSERT or UPDATE depending on what we are to do
+* @return boolean True for success, false for failure.
+*/
+function simple_write_resource( $path, $caldav_data, $put_action_type, $write_action_log = false ) {
+
+  $etag = md5($caldav_data);
+  $ic = new iCalComponent( $caldav_data );
+
+  /**
+  * We pull the user_no & collection_id out of the collection table, based on the resource path
+  */
+  $collection_path = preg_replace( '#/[^/]*$#', '/', $path );
+  $qry = new AwlQuery( 'SELECT user_no, collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => $collection_path ) );
+  if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+    $collection = $qry->Fetch();
+    $user_no = $collection->user_no;
+
+    return write_resource( $user_no, $path, $caldav_data, $collection->collection_id, $user_no, $etag, $ic, $put_action_type, false, $write_action_log );
+  }
+  return false;
+}
+function dataTParaSegundos($datatz){
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);
+        return mktime(  substr("$datatz", 9,2),//hora
+        substr("$datatz", 11,2),//min
+        substr("$datatz", 13,2),//segundos
+        substr("$datatz", 4,2),//mes
+        substr("$datatz", 6,2),//dia
+        substr("$datatz", 0,4));//ano
+}
+
+function dataTzParaSegundos($datatz){
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);
+        return mktime(  substr("$datatz", 9,2),//hora
+        substr("$datatz", 11,2),//min
+        substr("$datatz", 13,2),//segundos
+        substr("$datatz", 4,2),//mes
+        substr("$datatz", 6,2),//dia
+        substr("$datatz", 0,4)) + $HorarioTimeZone->getoffset($HorarioTime);//ano
+}
+
+function dataSegundosParaTZ($dataseg){
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);
+        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+        return date("Ymd\THis\Z",$teste[0]);
+}
+
+function dataTDiaSeguinteSegundos($datatz , $data_inicio){
+        if ((int)substr("$data_inicio", 9,2) < 5){
+                $ajuste_dia=86400;
+        }
+        elseif((int)substr("$data_inicio", 9,2) > 16){
+                $ajuste_dia=-86400;
+        }
+        $teste = (int)mktime(0,0,0,
+        substr("$datatz", 4,2),//mes
+        substr("$datatz", 6,2),
+        substr("$datatz", 0,4)) + $ajuste_dia;//ano
+        return $teste;
+
+}
+
Index: contrib/davical/inc/caldav-REPORT-sync-collection.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-sync-collection.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-sync-collection.php	(revision 3733)
@@ -0,0 +1,140 @@
+<?php
+/**
+* CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+$responses = array();
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
+$sync_token = $sync_tokens[0]->GetContent();
+if ( !isset($sync_token) ) $sync_token = 0;
+$sync_token = intval($sync_token);
+dbg_error_log( 'sync', " sync-token: %s", $sync_token );
+
+
+$props = $xmltree->GetElements('DAV::prop');
+$v = $props[0];
+$props = $v->GetContent();
+$proplist = array();
+foreach( $props AS $k => $v ) {
+  $proplist[] = $v->GetTag();
+}
+
+function display_status( $status_code ) {
+  return sprintf( 'HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code) );
+}
+
+$collection = new DAVResource( $request->path );
+
+$params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
+$sql = "SELECT new_sync_token( :sync_token, :collection_id)";
+$qry = new AwlQuery($sql, $params);
+if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
+  $request->DoResponse( 500, translate("Database error") );
+}
+$row = $qry->Fetch();
+
+if ( !isset($row->new_sync_token) ) {
+  /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */
+  $sync_token =0;
+  $params[':sync_token'] = $sync_token;
+  if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
+    $request->DoResponse( 500, translate("Database error") );
+  }
+  $row = $qry->Fetch();
+}
+$new_token = $row->new_sync_token;
+
+if ( $sync_token == 0 ) {
+  $sql = <<<EOSQL
+SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, 201 AS sync_status FROM collection
+            LEFT JOIN caldav_data USING (collection_id)
+            LEFT JOIN calendar_item USING (dav_id)
+                         LEFT JOIN addressbook_resource USING (dav_id)
+            WHERE collection.collection_id = :collection_id
+   ORDER BY collection.collection_id, caldav_data.dav_id
+EOSQL;
+  unset($params[':sync_token']);
+}
+else {
+  $sql = <<<EOSQL
+SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, sync_changes.*
+  FROM collection LEFT JOIN sync_changes USING(collection_id)
+                         LEFT JOIN caldav_data USING (collection_id,dav_id)
+                         LEFT JOIN calendar_item USING (collection_id,dav_id)
+                         LEFT JOIN addressbook_resource USING (dav_id)
+                         WHERE collection.collection_id = :collection_id
+       AND sync_time > (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token)
+   ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time
+EOSQL;
+}
+$qry = new AwlQuery($sql, $params );
+
+$last_dav_name = '';
+$first_status = 0;
+
+if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
+  while( $object = $qry->Fetch() ) {
+    if ( $object->dav_name == $last_dav_name ) {
+      /** The complex case: this is the second or subsequent for this dav_id */
+      if ( $object->sync_status == 404 ) {
+        array_pop($responses);
+        $resultset = array(
+          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+          new XMLElement( 'status', display_status($object->sync_status) )
+        );
+        $responses[] = new XMLElement( 'sync-response', $resultset );
+        $first_status = 404;
+      }
+      else if ( $object->sync_status == 201 && $first_status == 404 ) {
+        // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete
+        array_pop($responses);
+        $resultset = array(
+          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+          new XMLElement( 'status', display_status($object->sync_status) )
+        );
+        $responses[] = new XMLElement( 'sync-response', $resultset );
+      }
+      /** Else:
+       *    the object existed at start and we have multiple modifications,
+       *  or,
+       *    the object didn't exist at start and we have subsequent modifications,
+       *  but:
+       *    in either case we simply stick with our first report.
+       */
+    }
+    else {
+      /** The simple case: this is the first one for this dav_id */
+      $resultset = array(
+        new XMLElement( 'href', ConstructURL($object->dav_name) ),
+        new XMLElement( 'status', display_status($object->sync_status) )
+      );
+      if ( $object->sync_status != 404 ) {
+        $dav_resource = new DAVResource($object);
+        $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) );
+      }
+      $response_tag = 'response';
+      if ( isset($c->use_old_sync_response_tag) && $c->use_old_sync_response_tag ) $response_tag = 'sync-response';
+      $responses[] = new XMLElement( $response_tag, $resultset );
+      $first_status = $object->sync_status;
+      $last_dav_name  = $object->dav_name;
+    }
+  }
+  $responses[] = new XMLElement( 'sync-token', $new_token );
+}
+else {
+  $request->DoResponse( 500, translate("Database error") );
+}
+
+$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/auth-functions.php
===================================================================
--- contrib/davical/inc/auth-functions.php	(revision 3733)
+++ contrib/davical/inc/auth-functions.php	(revision 3733)
@@ -0,0 +1,209 @@
+<?php
+/**
+* The authentication handling plugins can be used by the Session class to
+* provide authentication.
+*
+* Each authenticate hook needs to:
+*   - Accept a username / password
+*   - Confirm the username / password are correct
+*   - Create (or update) a 'usr' record in our database
+*   - Return the 'usr' record as an object
+*   - Return === false when authentication fails
+*
+* It can expect that:
+*   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+*
+* In order to be called:
+*   - This file should be included
+*   - $c->authenticate_hook['call'] should be set to the name of the plugin
+*   - $c->authenticate_hook['config'] should be set up with any configuration data for the plugin
+*
+* @package   davical
+* @subpackage   authentication
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst IT Ltd, Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+require_once("DataUpdate.php");
+
+
+/**
+* Create a default home calendar for the user.
+* @param string $username The username of the user we are creating relationships for.
+*/
+function CreateHomeCalendar( $username ) {
+  global $session, $c;
+  if ( ! isset($c->home_calendar_name) || strlen($c->home_calendar_name) == 0 ) return true;
+
+  $usr = getUserByName( $username );
+  $parent_path = "/".$username."/";
+
+  /************** Modificado ****************   
+  */
+  $calendar_path = $parent_path . $c->home_calendar_name."/";
+  //$calendar_path = $parent_path;
+  
+  $dav_etag = md5($usr->user_no . $calendar_path);
+  $sql = 'INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar, created, modified, resourcetypes) ';
+  $sql .= 'VALUES( :user_no, :parent_container, :calendar_path, :dav_etag, :displayname, true, current_timestamp, current_timestamp, :resourcetypes );';
+  $params = array(
+      ':user_no' => $usr->user_no,
+      ':parent_container' => $parent_path,
+      ':calendar_path' => $calendar_path,
+      ':dav_etag' => $dav_etag,
+      ':displayname' => $usr->fullname,
+      ':resourcetypes' => '<DAV::collection/><urn:ietf:params:xml:ns:caldav:calendar/>'
+  );
+  $qry = new AwlQuery( $sql, $params );
+  if ( $qry->Exec() ) {
+    $c->messages[] = i18n("Home calendar added.");
+    dbg_error_log("User",":Write: Created user's home calendar at '%s'", $calendar_path );
+  }
+  else {
+    $c->messages[] = i18n("There was an error writing to the database.");
+    return false;
+  }
+  return true;
+}
+
+
+/**
+* Defunct function for creating default relationships.
+* @param string $username The username of the user we are creating relationships for.
+*/
+function CreateDefaultRelationships( $username ) {
+  return true;
+}
+
+
+/**
+* Update the local cache of the remote user details
+* @param object $usr The user details we read from the remote.
+*/
+function UpdateUserFromExternal( &$usr ) {
+  global $c;
+  /**
+  * When we're doing the create we will usually need to generate a user number
+  */
+  if ( !isset($usr->user_no) || intval($usr->user_no) == 0 ) {
+    $qry = new AwlQuery( "SELECT nextval('usr_user_no_seq');" );
+    $qry->Exec('Login',__LINE__,__FILE__);
+    $sequence_value = $qry->Fetch(true);  // Fetch as an array
+    $usr->user_no = $sequence_value[0];
+  }
+
+  $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no) );
+  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+    $type = "UPDATE";
+    if ( $old = $qry->Fetch() ) {
+      $changes = false;
+      foreach( $usr AS $k => $v ) {
+        if ( $old->{$k} != $v ) {
+          $changes = true;
+          dbg_error_log("Login","User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v );
+          break;
+        }
+      }
+      if ( !$changes ) {
+        dbg_error_log("Login","No changes to user record for '%s' - leaving as-is.", $usr->username );
+        if ( isset($usr->active) && $usr->active == 'f' ) return false;
+        return; // Normal case, if there are no changes
+      }
+      else {
+        dbg_error_log("Login","Changes to user record for '%s' - updating.", $usr->username );
+      }
+    }
+  }
+  else
+    $type = "INSERT";
+
+  $params = array();
+  if ( $type != 'INSERT' ) $params[':user_no'] = $usr->user_no;
+  $qry = new AwlQuery( sql_from_object( $usr, $type, 'usr', 'WHERE user_no= :user_no' ), $params );
+  $qry->Exec('Login',__LINE__,__FILE__);
+
+  /**
+  * We disallow login by inactive users _after_ we have updated the local copy
+  */
+  if ( isset($usr->active) && ($usr->active === 'f' || $usr->active === false) ) return false;
+
+  if ( $type == 'INSERT' ) {
+    $qry = new AwlQuery( 'INSERT INTO principal( type_id, user_no, displayname, default_privileges) SELECT 1, user_no, fullname, :privs::INT::BIT(24) FROM usr WHERE username=:username',
+                          array( ':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username) );
+    $qry->Exec('Login',__LINE__,__FILE__);
+    CreateHomeCalendar($usr->username);
+  }
+}
+
+
+/**
+* Authenticate against a different PostgreSQL database which contains a usr table in
+* the AWL format.
+*
+* Use this as in the following example config snippet:
+*
+* require_once('auth-functions.php');
+*  $c->authenticate_hook = array(
+*      'call'   => 'AuthExternalAwl',
+*      'config' => array(
+*           // A PgSQL database connection string for the database containing user records
+*          'connection[]' => 'dbname=wrms host=otherhost port=5433 user=general',
+*           // Which columns should be fetched from the database
+*          'columns'    => "user_no, active, email_ok, joined, last_update AS updated, last_used, username, password, fullname, email",
+*           // a WHERE clause to limit the records returned.
+*          'where'    => "active AND org_code=7"
+*      )
+*  );
+*
+*/
+function AuthExternalAWL( $username, $password ) {
+  global $c;
+
+  $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent'];
+
+  if ( isset($c->authenticate_hook['config']['columns']) )
+    $cols = $c->authenticate_hook['config']['columns'];
+  else
+    $cols = '*';
+
+  if ( isset($c->authenticate_hook['config']['where']) )
+    $andwhere = ' AND '.$c->authenticate_hook['config']['where'];
+  else
+    $andwhere = '';
+
+  $qry = new AwlQuery('SELECT '.$cols.' FROM usr WHERE lower(username) = :username '. $andwhere, array( ':username' => strtolower($username) ));
+  $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], ($persistent ? array(PDO::ATTR_PERSISTENT => true) : null));
+  if ( ! $authconn ) {
+    echo <<<EOERRMSG
+  <html><head><title>Database Connection Failure</title></head><body>
+  <h1>Database Error</h1>
+  <h3>Could not connect to PostgreSQL database</h3>
+  </body>
+  </html>
+EOERRMSG;
+    exit(1);
+  }
+
+  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+    $usr = $qry->Fetch();
+    if ( session_validate_password( $password, $usr->password ) ) {
+      UpdateUserFromExternal($usr);
+
+      /**
+      * We disallow login by inactive users _after_ we have updated the local copy
+      */
+      if ( isset($usr->active) && $usr->active == 'f' ) return false;
+
+      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username) );
+      if ( $qry->Exec() && $qry->rows() == 1 ) {
+        $principal = $qry->Fetch();
+        return $principal;
+      }
+      return $usr; // Somewhat optimistically
+    }
+  }
+
+  return false;
+
+}
Index: contrib/davical/inc/page-footer.php
===================================================================
--- contrib/davical/inc/page-footer.php	(revision 3733)
+++ contrib/davical/inc/page-footer.php	(revision 3733)
@@ -0,0 +1,13 @@
+</body><?php
+  if ( isset($c->scripts) && is_array($c->scripts) ) {
+    foreach ( $c->scripts AS $script ) {
+      echo "<script language=\"JavaScript\" src=\"$script\"></script>\n";
+    }
+  }
+  if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+    $script_time = microtime(true) - $c->script_start_time;
+    @dbg_error_log("statistics", "Method: %s, Status: %d, Script: %5.3lfs, Queries: %5.3lfs, URL: %s",
+                        $_SERVER['REQUEST_METHOD'], 200, $script_time, $c->total_query_time, $_SERVER['REQUEST_URI']);
+  }
+?>
+</html>
Index: contrib/davical/inc/other_translated_strings.php
===================================================================
--- contrib/davical/inc/other_translated_strings.php	(revision 3733)
+++ contrib/davical/inc/other_translated_strings.php	(revision 3733)
@@ -0,0 +1,53 @@
+<?php
+/**
+* This file is not included anywhere.  It exists as a way of ensuring some
+* strings, which may come from the database, or from other places, are
+* present in the translation files.
+*
+* It is also used to add extra entries to the translations which document
+* the sense of a word or phrase to assist with translations.
+*/
+
+// Groups, from the database
+i18n("Admin <!-- in the sense of 'systems admin' -->");
+i18n("Admin");
+
+i18n("Group <!-- in the sense of a group of people -->");
+i18n("Group");
+
+i18n("Public <!-- in the sense of being available to all users -->");
+i18n("Public");
+
+i18n("Resource <!-- a thing which might be booked: a room, a carpark, a projector... -->");
+i18n("Resource");
+
+i18n("No. <!-- short for 'Number' -->");
+i18n("No.");
+
+i18n("No <!-- not 'Yes' -->");
+i18n("No");
+i18n("Yes");
+
+i18n("Id <!-- short for 'Identifier' -->");
+i18n("Id");
+
+i18n('View this user record');
+i18n('Edit this user record');
+i18n('You are viewing');
+i18n('You are editing');
+
+i18n("forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option allowing people to log in automatically in future -->");
+
+// Date format types
+i18n('European');
+i18n('US Format');
+i18n('ISO Format');
+
+// Calendar schedule transparency
+i18n('Opaque');
+i18n('Transparent');
+
+// Unspecified timezone value
+i18n('*** Unknown ***');
+
+i18n('Person');
Index: contrib/davical/inc/caldav-POST.php
===================================================================
--- contrib/davical/inc/caldav-POST.php	(revision 3733)
+++ contrib/davical/inc/caldav-POST.php	(revision 3733)
@@ -0,0 +1,156 @@
+<?php
+/**
+* CalDAV Server - handle PUT method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("POST", "method handler");
+
+require_once("XMLDocument.php");
+require_once("iCalendar.php");
+include_once('caldav-PUT-functions.php');
+include_once('freebusy-functions.php');
+
+if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
+  && ! $request->AllowedTo("CALDAV:schedule-send-invite")
+  && ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
+  // $request->DoResponse(403);
+  dbg_error_log( "WARN", ": POST: permissions not yet checked" );
+}
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['post'])) ) {
+  $fh = fopen('/tmp/POST.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+
+function handle_freebusy_request( $ic ) {
+  global $c, $session, $request;
+
+  $reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C" ) );
+  $responses = array();
+
+  $fbq_start = $ic->GetPValue('DTSTART');
+  $fbq_end   = $ic->GetPValue('DTEND');
+  if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
+    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
+  }
+
+  $range_start = new RepeatRuleDateTime($fbq_start);
+  $range_end   = new RepeatRuleDateTime($fbq_end);
+
+  $attendees = $ic->GetProperties('ATTENDEE');
+  if ( preg_match( '# iCal/\d#', $_SERVER['HTTP_USER_AGENT']) ) {
+    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+    foreach( $wr_attendees AS $k => $v ) {
+      $attendees[] = $v;
+    }
+  }
+  dbg_error_log( "POST", "Responding with free/busy for %d attendees", count($attendees) );
+
+  foreach( $attendees AS $k => $attendee ) {
+    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+    dbg_error_log( "POST", "Calculating free/busy for %s", $attendee_email );
+
+    /** @TODO: Refactor this so we only do one query here and loop through the results */
+    //$params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email );
+    //$qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
+    //if ( !$qry->Exec('POST',__LINE__,__FILE__) ) $request->DoResponse( 501, 'Database error');
+    //if ( $qry->rows() > 1 ) {
+      // Unlikely, but if we get more than one result we'll do an exact match instead.
+    //  if ( !$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params ) )
+    //    $request->DoResponse( 501, 'Database error');
+    //  if ( $qry->rows() == 0 ) {
+        /** Sigh... Go back to the original case-insensitive match */
+    //    $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
+    //  }
+    //}
+
+    $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
+    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+
+    //if ( $qry->rows() == 0 ) {
+    //  $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
+    //  $reply->CalDAVElement($response, "calendar-data" );
+    //  $responses[] = $response;
+    //  continue;
+    //}
+    //if ( ! $attendee_usr = $qry->Fetch() ) $request->DoResponse( 501, 'Database error');
+    //if ( (privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0 ) {
+    //  $reply->CalDAVElement($response, "request-status", "3.8;No authority" );
+    //  $reply->CalDAVElement($response, "calendar-data" );
+    //  $responses[] = $response;
+    //  continue;
+    //}
+    $attendee_path_match = '^/'.$attendee_usr->username.'/';
+    $fb = get_freebusy( $attendee_path_match, $attendee_email,$range_start, $range_end, bindec($attendee_usr->p) );
+
+    $fb->AddProperty( 'UID',       $ic->GetPValue('UID') );
+    $fb->SetProperties( $ic->GetProperties('ORGANIZER'), 'ORGANIZER');
+    $fb->AddProperty( $attendee );
+
+    $vcal = new iCalComponent();
+    $vcal->VCalendar( array('METHOD' => 'REPLY') );
+    $vcal->AddComponent( $fb );
+
+    $response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+    $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+    $reply->CalDAVElement($response, "calendar-data", $vcal->Render() );
+    $responses[] = $response;
+  }
+
+  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav' );
+  $request->XMLResponse( 200, $response );
+}
+
+
+function handle_cancel_request( $ic ) {
+  global $c, $session, $request;
+
+  $reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C" ) );
+
+  $responses[] = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+  $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray() );
+  $request->XMLResponse( 200, $response );
+}
+
+$ical = new iCalComponent( $request->raw_post );
+$method =  $ical->GetPValue('METHOD');
+
+$resources = $ical->GetComponents('VTIMEZONE',false);
+$first = $resources[0];
+switch ( $method ) {
+  case 'REQUEST':
+    dbg_error_log('POST', 'Handling iTIP "REQUEST" method with "%s" component.', $method, $first->GetType() );
+    if ( $first->GetType() == 'VFREEBUSY' )
+      handle_freebusy_request( $first );
+    elseif ( $first->GetType() == 'VEVENT' ) {
+      handle_schedule_request( $ical );
+    }
+    else {
+      dbg_error_log('POST', 'Ignoring iTIP "REQUEST" with "%s" component.', $first->GetType() );
+    }
+    break;
+  case 'REPLY':
+    dbg_error_log('POST', 'Handling iTIP "REPLY" with "%s" component.', $first->GetType() );
+    handle_schedule_reply ( $ical );
+    break;
+
+  case 'CANCEL':
+    dbg_error_log("POST", "Handling iTIP 'CANCEL'  method.", $method );
+    handle_cancel_request( $first );
+    break;
+
+  default:
+    dbg_error_log("POST", "Unhandled '%s' method in request.", $method );
+}
Index: contrib/davical/inc/DAVTicket.php
===================================================================
--- contrib/davical/inc/DAVTicket.php	(revision 3733)
+++ contrib/davical/inc/DAVTicket.php	(revision 3733)
@@ -0,0 +1,132 @@
+<?php
+/**
+* An object representing a DAV 'ticket'
+*
+* @package   davical
+* @subpackage   DAVTicket
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once('AwlQuery.php');
+
+
+/**
+* A class for things to do with a DAV Ticket
+*
+* @package   davical
+*/
+class DAVTicket
+{
+  /**
+  * @var The ID of the ticket
+  */
+  private $ticket_id;
+
+  /**
+  * @var dav_name
+  */
+  private $dav_name;
+
+  /**
+  * @var The collection_id of/containing the ticketed resource
+  */
+  private $target_collection_id;
+
+  /**
+  * @var The resource_id of the ticketed resource, if it is not a collection
+  */
+  private $target_resource_id;
+
+  /**
+  * @var The expiry of the ticket
+  */
+  private $expiry;
+
+  /**
+  * @var The ID of the principal who owns this ticket
+  */
+  private $dav_owner_id;
+
+  /**
+  * @var A bit mask representing the privileges provided by this ticket
+  */
+  private $privileges;
+
+  /**
+  * @var A bit mask representing the privileges granted to the ticket owner to the collection (or container of this) resource.
+  */
+  private $grantor_collection_privileges;
+
+  /**
+  * Constructor
+  * @param string $ticket_id
+  */
+  function __construct( $ticket_id ) {
+    global $c;
+
+    $this->dav_name             = null;
+    $this->target_collection_id = null;
+    $this->target_resource_id   = null;
+    $this->expiry               = null;
+    $this->expired              = true;
+    $this->dav_owner_id         = null;
+    $this->ticket_id            = $ticket_id;
+    $this->privileges           = 0;
+    $this->grantor_collection_privileges = 0;
+
+    $qry = new AwlQuery(
+        'SELECT access_ticket.*, collection.dav_name, (access_ticket.expires < current_timestamp) AS expired,
+                path_privs(access_ticket.dav_owner_id,collection.dav_name,:scan_depth) AS grantor_collection_privileges
+           FROM access_ticket JOIN collection ON (target_collection_id = collection_id)
+          WHERE ticket_id = :ticket_id::text',
+        array(':ticket_id' => $ticket_id, ':scan_depth' => $c->permission_scan_depth)
+    );
+    if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $t = $qry->Fetch() ) {
+      if ( ! $t->expired ) {
+        foreach( $t AS $k => $v ) {
+          $this->{$k} = $v;
+        }
+        $this->expired = false;
+        $this->privileges = bindec($this->privileges);
+        $this->grantor_collection_privileges = bindec($this->grantor_collection_privileges);
+        dbg_error_log( 'DAVTicket', 'Found a current ticket for "%s"', implode(', ',bits_to_privilege($this->privileges())) );
+      }
+      else {
+        dbg_error_log( 'DAVTicket', 'Found an expired ticket: %s - %s', $ticket_id, $t->expires );
+      }
+    }
+    if ( isset($this->target_resource_id) ) {
+      $qry = new AwlQuery( 'SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id', array(':dav_id' => $this->target_resource_id ) );
+      if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $r = $qry->Fetch() ) {
+        $this->dav_name = $r->dav_name;
+      }
+    }
+  }
+
+
+  function dav_name() {
+    return $this->dav_name;
+  }
+
+
+  function id() {
+    return $this->ticket_id;
+  }
+
+
+  function privileges() {
+    return ($this->privileges & $this->grantor_collection_privileges);
+  }
+
+
+  function MatchesPath( $test_path ) {
+    $length = strlen($this->dav_name);
+    return (substr($test_path, 0, $length) == $this->dav_name);
+  }
+
+  function MatchesResource( $test_resource_id ) {
+    return ($test_resource_id == $this->target_collection_id || $test_resource_id == $this->target_resource_id);
+  }
+}
Index: contrib/davical/inc/caldav-REPORT-principal.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-principal.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-principal.php	(revision 3733)
@@ -0,0 +1,79 @@
+<?php
+
+$responses = array();
+
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search');
+dbg_log_array( "principal", "SEARCH", $searches, true );
+
+$clause_joiner = " AND ";
+$CS_search_test = $xmltree->GetAttribute('test');
+if ( isset($CS_search_test) && $CS_search_test == 'anyof' ) {
+  $clause_joiner = " OR ";
+}
+
+$params = array();
+$where = "";
+foreach( $searches AS $k => $search ) {
+  $qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*');  // There may be many
+  $match     = $search->GetPath('/DAV::property-search/DAV::match');   // There may only be one
+  dbg_log_array( "principal", "MATCH", $match, true );
+  $match = $match[0]->GetContent();
+  $subwhere = "";
+  foreach( $qry_props AS $k1 => $v1 ) {
+    if ( $subwhere != "" ) $subwhere .= " OR ";
+    switch( $v1->GetTag() ) {
+      case 'DAV::displayname':
+        $subwhere .= ' displayname ILIKE :displayname_match ';
+        $params[':displayname_match'] = '%'.$match.'%';
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+        $match = preg_replace('{^.*/caldav.php/([^/]+)(/.*)?$}', '\\1', $match);
+        $match = preg_replace('{^mailto:}', '', $match);
+        $subwhere .= ' (email ILIKE :user_address_match OR username ILIKE :user_address_match) ';
+        $params[':user_address_match'] = '%'.$match.'%';
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+        $match = preg_replace('{^.*/caldav.php}', '', $match);
+        $subwhere .= ' dav_name LIKE :calendar_home_match ';
+        $params[':calendar_home_match'] = $match.'%';
+        break;
+
+      default:
+        /**
+        * @todo We should handle a lot more properties here.  principal-URL seems a likely one to be used.
+        * @todo We should catch the unsupported properties in the query and fire back an error indicating so.
+        */
+        dbg_error_log("principal", "Unhandled tag '%s' to match '%s'\n", $v1->GetTag(), $match );
+    }
+  }
+  if ( $subwhere != "" ) {
+    $where .= sprintf( "%s(%s)", ($where == "" ? "" : $clause_joiner), $subwhere );
+  }
+}
+if ( $where != "" ) $where = "WHERE $where";
+$sql = "SELECT * FROM dav_principal $where ORDER BY principal_id LIMIT 100";
+$qry = new AwlQuery($sql, $params);
+
+
+$get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*');
+$properties = array();
+foreach( $get_props AS $k1 => $v1 ) {
+  $properties[] = $v1->GetTag();
+}
+
+if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+  while( $row = $qry->Fetch() ) {
+    $principal = new DAVResource($row);
+    $responses[] = $principal->RenderAsXML( $properties, $reply );
+  }
+}
+
+$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/PublicSession.php
===================================================================
--- contrib/davical/inc/PublicSession.php	(revision 3733)
+++ contrib/davical/inc/PublicSession.php	(revision 3733)
@@ -0,0 +1,84 @@
+<?php
+/**
+* A Class for faking sessions which are anonymous access to a resource
+*
+* @package davical
+* @subpackage PublicSession
+* @author Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+/**
+* A Class for handling a public (anonymous) session
+*
+* @package davical
+*/
+class PublicSession {
+  /**#@+
+  * @access private
+  */
+
+  /**
+  * User ID number
+  * @var user_no int
+  */
+  var $user_no;
+
+  /**
+  * Principal ID
+  * @var principal_id int
+  */
+  var $principal_id;
+
+  /**
+  * User e-mail
+  * @var email string
+  */
+  var $email;
+
+  /**
+  * User full name
+  * @var fullname string
+  */
+  var $fullname;
+
+  /**
+  * Group rights
+  * @var groups array
+  */
+  var $groups;
+  /**#@-*/
+
+  /**
+  * The constructor, which just calls the actual type configured
+  */
+  function PublicSession() {
+    global $c;
+
+    $this->user_no = -1;
+    $this->principal_id = -1;
+    $this->email = null;
+    $this->username = 'guest';
+    $this->fullname = 'Anonymous';
+    $this->groups = ( isset($c->public_groups) ? $c->public_groups : array() );
+    $this->roles = array( 'Public' => true );
+    $this->logged_in = false;
+  }
+
+
+  /**
+  * Checks whether a user is allowed to do something.
+  *
+  * The check is performed to see if the user has that role.
+  *
+  * @param string $whatever The role we want to know if the user has.
+  * @return boolean Whether or not the user has the specified role.
+  */
+  function AllowedTo ( $whatever ) {
+    dbg_error_log('session', 'Checking whether "Public" is allowed to "%s"', $whatever);
+    return ( isset($this->roles[$whatever]) && $this->roles[$whatever] );
+  }
+
+}
+
Index: contrib/davical/inc/DAVResource.php
===================================================================
--- contrib/davical/inc/DAVResource.php	(revision 3733)
+++ contrib/davical/inc/DAVResource.php	(revision 3733)
@@ -0,0 +1,1763 @@
+<?php
+/**
+* An object representing a DAV 'resource'
+*
+* @package   davical
+* @subpackage   Resource
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once('AwlQuery.php');
+require_once('DAVTicket.php');
+include_once ("drivers_ldap.php");
+
+/**
+* A class for things to do with a DAV Resource
+*
+* @package   davical
+*/
+class DAVResource
+{
+  /**
+  * @var The partial URL of the resource within our namespace, which this resource is being retrieved as
+  */
+  protected $dav_name;
+
+  /**
+  * @var Boolean: does the resource actually exist yet?
+  */
+  protected $exists;
+
+  /**
+  * @var The unique etag associated with the current version of the resource
+  */
+  protected $unique_tag;
+
+  /**
+  * @var The actual resource content, if it exists and is not a collection
+  */
+  protected $resource;
+
+  /**
+  * @var The parent of the resource, which will always be a collection
+  */
+  protected $parent;
+
+  /**
+  * @var The types of the resource, possibly multiple
+  */
+  protected $resourcetypes;
+
+  /**
+  * @var The type of the content
+  */
+  protected $contenttype;
+
+  /**
+  * @var The canonical name which this resource exists at
+  */
+  protected $bound_from;
+
+  /**
+  * @var An object which is the collection record for this resource, or for it's container
+  */
+  private $collection;
+
+  /**
+  * @var An object which is the principal for this resource, or would be if it existed.
+  */
+  private $principal;
+
+  /**
+  * @var A bit mask representing the current user's privileges towards this DAVResource
+  */
+  private $privileges;
+
+  /**
+  * @var True if this resource is a collection of any kind
+  */
+  private $_is_collection;
+
+  /**
+  * @var True if this resource is a principal-URL
+  */
+  private $_is_principal;
+
+  /**
+  * @var True if this resource is a calendar collection
+  */
+  private $_is_calendar;
+
+  /**
+  * @var True if this resource is a binding to another resource
+  */
+  private $_is_binding;
+
+  /**
+  * @var True if this resource is an addressbook collection
+  */
+  private $_is_addressbook;
+
+  /**
+  * @var True if this resource is, or is in, a proxy collection
+  */
+  private $_is_proxy_request;
+
+  /**
+  * @var An array of the methods we support on this resource.
+  */
+  private $supported_methods;
+
+  /**
+  * @var An array of the reports we support on this resource.
+  */
+  private $supported_reports;
+
+  /**
+  * @var An array of the dead properties held for this resource
+  */
+  private $dead_properties;
+
+  /**
+  * @var An array of the component types we support on this resource.
+  */
+  private $supported_components;
+
+  /**
+  * @var An array of DAVTicket objects if any apply to this resource, such as via a bind.
+  */
+  private $tickets;
+
+  /**
+  * Constructor
+  * @param mixed $parameters If null, an empty Resourced is created.
+  *     If it is an object then it is expected to be a record that was
+  *     read elsewhere.
+  */
+  function __construct( $parameters = null ) {
+    $this->exists        = null;
+    $this->bound_from    = null;
+    $this->dav_name      = null;
+    $this->unique_tag    = null;
+    $this->resource      = null;
+    $this->collection    = null;
+    $this->principal     = null;
+    $this->parent        = null;
+    $this->resourcetypes = null;
+    $this->contenttype   = null;
+    $this->privileges    = null;
+    $this->dead_properties   = null;
+    $this->supported_methods = null;
+    $this->supported_reports = null;
+
+    $this->_is_collection    = false;
+    $this->_is_principal     = false;
+    $this->_is_calendar      = false;
+    $this->_is_binding       = false;
+    $this->_is_addressbook   = false;
+    $this->_is_proxy_request = false;
+    if ( isset($parameters) && is_object($parameters) ) {
+      $this->FromRow($parameters);
+    }
+    else if ( isset($parameters) && is_array($parameters) ) {
+      if ( isset($parameters['path']) ) {
+        $this->FromPath($parameters['path']);
+      }
+    }
+    else if ( isset($parameters) && is_string($parameters) ) {
+      $this->FromPath($parameters);
+    }
+ 
+  }
+
+
+  /**
+  * Initialise from a database row
+  * @param object $row The row from the DB.
+  */
+  function FromRow($row) {
+    global $c;
+
+    if ( $row == null ) return;
+
+    $this->exists = true; 
+    $this->dav_name = $row->dav_name; 
+    $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+    $this->_is_collection = preg_match( '{/$}', $this->dav_name );
+
+
+    if ( $row->owner)
+    { 
+    $this->exists = true;
+    // $this->dav_name = $row->dav_name;
+    $this->dav_name = $row->owner;
+    //$this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+    $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->owner);
+    $this->_is_collection = preg_match( '{/$}', $this->dav_name );
+    }
+    else if ( $row->id_owner) 
+     {
+          $this->exists = true;
+          // $this->dav_name = $row->dav_name;
+          $this->dav_name = $row->id_owner;
+          //$this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+          $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->id_owner);
+          $this->_is_collection = preg_match( '{/$}', $this->dav_name );
+     }  
+    //else
+    // { return;
+    // } 
+    if ( $this->_is_collection ) {
+      $this->contenttype = 'httpd/unix-directory';
+      $this->collection = (object) array();
+      $this->resource_id = $row->collection_id;
+
+      $this->_is_principal = preg_match( '{^/[^/]+/$}', $this->dav_name );
+      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
+        $this->collection->dav_name = $matches[1].'/';
+        $this->collection->type = 'principal_link';
+        $this->_is_principal = true;
+      }
+    }
+    else {
+      $this->resource = (object) array();
+      //if ( isset($row->dav_id) ) $this->resource_id = $row->dav_id;
+      if ( isset($row->cal_id) ) $this->resource_id = $row->cal_id;
+      if ( isset($row->id_contact) ) $this->resource_id = $row->id_contact;
+       
+    }
+
+    dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->dav_name, ($this->_is_collection?'':' not') );
+
+
+    foreach( $row AS $k => $v ) {
+      if ( $this->_is_collection )
+        $this->collection->{$k} = $v;
+      else
+        $this->resource->{$k} = $v;
+      switch ( $k ) {
+        case 'last_update':
+                           $lastupdate = $v;  
+      }
+    } 
+    foreach( $row AS $k => $v ) {
+      if ( $this->_is_collection )
+        $this->collection->{$k} = $v;
+      else
+        $this->resource->{$k} = $v;
+      switch ( $k ) {
+        case 'created':
+        case 'modified':
+        case 'resourcetypes':
+          $this->{$k} = $v;
+          break;
+
+        case 'cal_id':
+          $this->resourcetypes = '';
+          $this->unique_tag = '"'.md5($v.$this->dav_name.$lastupdate).'"'; 
+          break;
+        case 'id_contact':
+          $this->resourcetypes = '';
+          $this->unique_tag ='"'.md5($v.$this->dav_name.$lastupdate).'"'; 
+          break;
+        
+        case 'dav_etag':
+          $this->unique_tag = '"'.$v.'"';
+          break;
+
+      }
+    }
+
+    if ( $this->_is_collection ) {
+      if ( !isset( $this->collection->type ) || $this->collection->type == 'collection' ) {
+        if ( $this->_is_principal )
+          $this->collection->type = 'principal';
+        else if ( $row->is_calendar == 't' ) {
+          $this->collection->type = 'calendar';
+        }
+        else if ( $row->is_addressbook == 't' ) {
+          $this->collection->type = 'addressbook';
+        }
+        else if ( isset($row->is_proxy) && $row->is_proxy == 't' ) {
+          $this->collection->type = 'proxy';
+        }
+        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+        else if ( $this->dav_name == '/' )
+          $this->collection->type = 'root';
+        else
+          $this->collection->type = 'collection';
+      }
+
+      $this->_is_calendar     = ($this->collection->is_calendar == 't');
+      $this->_is_addressbook  = ($this->collection->is_addressbook == 't');
+      $this->_is_proxy_request= ($this->collection->type == 'proxy');
+      if ( $this->_is_principal && !isset($this->resourcetypes) ) {
+        $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+      }
+      else if ( $this->_is_proxy_request ) {
+        $this->resourcetypes  = $this->collection->resourcetypes;
+      }
+      if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+    }
+    else {
+      $this->resourcetypes = '';
+      //if ( isset($this->resource->caldav_data) ) {
+      if ( isset($this->resource->owner) ) {
+        //if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) $this->contenttype = 'text/calendar';
+        $this->contenttype = 'text/calendar';
+        $this->resource->displayname = $this->resource->title;
+      }
+      else
+        {  
+        $this->contenttype = 'text/vcard';
+        $this->resource->displayname = $this->resource->given_names;
+       } 
+          
+    }
+ }
+
+
+  /**
+  * Initialise from a path
+  * @param object $inpath The path to populate the resource data from
+  */
+  function FromPath($inpath) {
+    global $c;
+
+    $this->dav_name = DeconstructURL($inpath);
+
+    $this->FetchCollection();
+    if ( $this->_is_collection ) {
+      if ( $this->_is_principal || $this->collection->type == 'principal' )   $this->FetchPrincipal();
+     }  
+  else {
+      $this->FetchResource();
+    }
+    dbg_error_log( 'DAVResource', ':FromPath: Path "%s" is%s a collection%s.',
+               $this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
+  }
+
+
+  /**
+  * Find the collection associated with this resource.
+  */
+  function FetchCollection() {
+    global $c, $session, $request;
+    /**
+    * RFC4918, 8.3: Identifiers for collections SHOULD end in '/'
+    *    - also discussed at more length in 5.2
+    *
+    * So we look for a collection which matches one of the following URLs:
+    *  - The exact request.
+    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+    *  - The request URL truncated to the last '/'
+    * The collection URL for this request is therefore the longest row in the result, so we
+    * can "... ORDER BY LENGTH(dav_name) DESC LIMIT 1"
+    */
+    dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->dav_name );
+
+    $this->collection = (object) array(
+      'collection_id' => -1,
+      'type' => 'nonexistent',
+      'is_calendar' => false, 'is_principal' => false, 'is_addressbook' => false
+    );
+
+    $base_sql = 'SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), ';
+    $base_sql .= 'p.principal_id, p.type_id AS principal_type_id, ';
+    $base_sql .= 'p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges, ';
+    $base_sql .= 'time_zone.tz_spec ';
+    $base_sql .= 'FROM collection LEFT JOIN principal p USING (user_no) ';
+    $base_sql .= 'LEFT JOIN time_zone ON (collection.timezone=time_zone.tz_id) ';
+    $base_sql .= 'WHERE ';
+    $sql = $base_sql .'collection.dav_name = :raw_path ';
+    $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+    if ( !preg_match( '#/$#', $this->dav_name ) ) {
+      $sql .= ' OR collection.dav_name = :up_to_slash OR collection.dav_name = :plus_slash ';
+      $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+      $params[':plus_slash']  = $this->dav_name.'/';
+    }
+    $sql .= 'ORDER BY LENGTH(collection.dav_name) DESC LIMIT 1';
+    $qry = new AwlQuery( $sql, $params );
+    if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+      $this->collection = $row;
+      $this->collection->exists = true;
+      if ( $row->is_calendar == 't' )
+        $this->collection->type = 'calendar';
+      else if ( $row->is_addressbook == 't' )
+        $this->collection->type = 'addressbook';
+      else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+        $this->collection->type = 'schedule-'. $matches[3]. 'box';
+      else
+        $this->collection->type = 'collection';
+    }
+    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->dav_name, $matches ) ) {
+      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+      $params = array( ':username' => $matches[3], ':parent_container' => $matches[2], ':dav_name' => $matches[1] );
+      $params[':boxname'] = ($matches[4] == 'in' ? ' Inbox' : ' Outbox');
+      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+      $sql = <<<EOSQL
+INSERT INTO collection ( user_no, parent_container, dav_name, dav_displayname, is_calendar, created, modified, dav_etag, resourcetypes )
+    VALUES( (SELECT user_no FROM usr WHERE username = :username),
+            :parent_container, :dav_name,
+            (SELECT fullname FROM usr WHERE username = :username) || :boxname,
+             FALSE, current_timestamp, current_timestamp, '1', :resourcetypes )
+EOSQL;
+      $qry = new AwlQuery( $sql, $params );
+      $qry->Exec('DAVResource');
+      dbg_error_log( 'DAVResource', 'Created new collection as "%s".', trim($params[':boxname']) );
+
+      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+      $qry = new AwlQuery( $base_sql . ' dav_name = :raw_path', $params );
+      if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+        $this->collection = $row;
+        $this->collection->exists = true;
+        $this->collection->type = $this->collection_type;
+      }
+    }
+    else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
+      $this->collection->type = 'proxy';
+      $this->_is_proxy_request = true;
+      $this->proxy_type = $matches[3];
+      $this->collection->dav_name = $this->dav_name;
+      $this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
+      $this->collection->exists = true;
+      $this->collection->parent_container = $matches[1] . '/';
+    }
+    else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches)
+           || preg_match( '#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
+      $this->_is_principal = true;
+      $this->FetchPrincipal();
+    }
+    else if ( $this->dav_name == '/' ) {
+      $this->collection->dav_name = '/';
+      $this->collection->type = 'root';
+      $this->collection->exists = true;
+      $this->collection->displayname = $c->system_name;
+      $this->collection->default_privileges = (1 | 16 | 32);
+      $this->collection->parent_container = '/';
+    }
+    else {
+      $sql = <<<EOSQL
+SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), p.principal_id,
+    p.type_id AS principal_type_id, p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges,
+    time_zone.tz_spec, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container,
+    dav_binding.dav_displayname, owner.dav_name AS bind_owner_url, dav_binding.dav_name AS bound_to
+FROM dav_binding
+    LEFT JOIN collection ON (collection.collection_id=bound_source_id)
+    LEFT JOIN principal p USING (user_no)
+    LEFT JOIN dav_principal owner ON (dav_binding.dav_owner_id=owner.principal_id)
+    LEFT JOIN time_zone ON (collection.timezone=time_zone.tz_id)
+ WHERE dav_binding.dav_name = :raw_path
+EOSQL;
+      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+      if ( !preg_match( '#/$#', $this->dav_name ) ) {
+        $sql .= ' OR dav_binding.dav_name = :up_to_slash OR collection.dav_name = :plus_slash ';
+        $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+        $params[':plus_slash']  = $this->dav_name.'/';
+      }
+      $sql .= ' ORDER BY LENGTH(dav_binding.dav_name) DESC LIMIT 1';
+      $qry = new AwlQuery( $sql, $params );
+      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+        $this->collection = $row;
+        $this->collection->exists = true;
+        $this->_is_binding = true;
+        $this->collection->parent_set = $row->parent_container;
+        $this->collection->parent_container = $row->bind_parent_container;
+        $this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
+        $this->collection->bound_from = $row->dav_name;
+        $this->collection->dav_name = $row->bound_to;
+        if ( isset($row->access_ticket_id) ) {
+          if ( !isset($this->tickets) ) $this->tickets = array();
+          $this->tickets[] = new DAVTicket($row->access_ticket_id);
+        }
+        if ( $row->is_calendar == 't' )
+          $this->collection->type = 'calendar';
+        else if ( $row->is_addressbook == 't' )
+          $this->collection->type = 'addressbook';
+        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+        else
+          $this->collection->type = 'collection';
+      }
+      else {
+        dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
+        $this->collection->exists = false;
+        $this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
+      }
+    }
+
+    @dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s" davname "%s".', $this->collection->dav_name, $this->collection->type,$this->dav_name );
+
+    $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' || preg_match('{addressbook}',$this->dav_name) );
+    if ( $this->_is_collection ) {
+      $this->dav_name = $this->collection->dav_name;
+      $this->resource_id = $this->collection->collection_id;
+      $this->_is_calendar    = ($this->collection->type == 'calendar');
+      $this->_is_addressbook = ($this->collection->type == 'addressbook');
+      $this->contenttype = 'httpd/unix-directory';
+      if ( !isset($this->exists) && isset($this->collection->exists) ) {
+        // If this seems peculiar it's because we only set it to false above...
+        $this->exists = $this->collection->exists;
+      }
+      if ( $this->exists ) {
+        if ( isset($this->collection->dav_etag) ) $this->unique_tag = '"'.$this->collection->dav_etag.'"';
+        if ( isset($this->collection->created) )  $this->created = $this->collection->created;
+        if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
+        if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+      }
+      else {
+        if ( !isset($this->parent) ){ 
+            $this->FetchParentContainer();
+           }
+        $this->user_no = $this->parent->GetProperty('user_no');
+      }
+      if ( isset($this->collection->resourcetypes) )
+        $this->resourcetypes = $this->collection->resourcetypes;
+      else {
+        $this->resourcetypes = '<DAV::collection/>';
+        if ( $this->_is_principal )   $this->resourcetypes .= '<DAV::principal/>';
+        if ( $this->_is_addressbook ) $this->resourcetypes .= '<urn:ietf:params:xml:ns:carddav:addressbook/>';
+        if ( $this->_is_calendar )    $this->resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+      }
+    }
+  }
+
+
+  /**
+  * Find the principal associated with this resource.
+  */
+  function FetchPrincipal() {
+    if ( isset($this->principal) ) return;
+    $this->principal = new CalDAVPrincipal( array( "path" => $this->bound_from() ) );
+    if ( $this->_is_principal && $this->principal->Exists() ) {
+      $this->exists = true;
+      $this->unique_tag = $this->principal->dav_etag;
+      $this->created = $this->principal->created;
+      $this->modified = $this->principal->modified;
+      $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+      $this->resource_id = $this->principal->principal_id;
+      $this->collection = $this->principal->AsCollection();
+      $this->user_no = $this->principal->user_no;
+    }
+    elseif ( $this->_is_principal ) {
+      $this->exists = false;
+      $this->collection->dav_name = $this->dav_name;
+      $this->collection->type = 'principal';
+    }
+  }
+
+
+  /**
+  * Retrieve the actual resource.
+  */
+  function FetchResource() {
+    global $c, $session;
+
+    if ( isset($this->exists) ) return;   // True or false, we've got what we can already
+    if ( $this->_is_collection ) return;   // We have all we're going to read
+
+    $sql = <<<EOQRY
+SELECT * FROM caldav_data LEFT JOIN calendar_item USING (collection_id,dav_id)
+     WHERE caldav_data.dav_name = :dav_name
+EOQRY;
+    $params = array( ':dav_name' => $this->bound_from() );
+
+    $qry = new AwlQuery( $sql, $params );
+    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+      $this->exists = true;
+      $this->resource = $qry->Fetch();
+      $this->unique_tag = $this->resource->dav_etag;
+      $this->created = $this->resource->created;
+      $this->modified = $this->resource->modified;
+      $this->resource_id = $this->resource->dav_id;
+      if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
+        $this->contenttype = 'text/calendar';
+      }
+      $this->resourcetypes = '';
+    }
+    else {
+      $this->exists = false;
+    }
+  }
+
+
+  /**
+  * Fetch any dead properties for this URL
+  */
+  function FetchDeadProperties() {
+    if ( isset($this->dead_properties) ) return;
+
+    $this->dead_properties = array();
+    $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
+    if ( $qry->Exec('DAVResource') ) {
+      while ( $property = $qry->Fetch() ) {
+        $this->dead_properties[$property->property_name] = $property->property_value;
+      }
+    }
+  }
+
+
+  /**
+  * Fetch the parent to this resource.
+  */
+  function FetchParentContainer() {
+    if ( $this->dav_name == '/' ) return null;
+    if ( !isset($this->parent) ) {
+      if ( $this->_is_collection ) {
+        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+        $this->parent = new DAVResource( $this->parent_path() );
+      }
+      else {
+        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+        $this->parent = new DAVResource($this->collection->dav_name);
+      }
+    }
+    return $this->parent;
+  }
+
+
+  /**
+  * Build permissions for this URL
+  */
+  function FetchPrivileges() {
+    global $session, $request;
+
+    if ( $this->dav_name == '/' || $this->dav_name == '' ) {
+      $this->privileges = (1 | 16 | 32); // read + read-acl + read-current-user-privilege-set
+      dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
+      return;
+    }
+
+    if ( $session->AllowedTo('Admin') ) {
+      $this->privileges = privilege_to_bits('all');
+      dbg_error_log( 'DAVResource', 'Full permissions for an administrator.' );
+      return;
+    }
+
+    if ( $this->IsPrincipal() ) {
+      if ( !isset($this->principal) ) $this->FetchPrincipal();
+      $this->privileges = $this->principal->Privileges();
+      dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing "%s"', $this->privileges, $this->principal->username() );
+      return;
+    }
+
+    if ( ! isset($this->collection) ) $this->FetchCollection();
+    $this->privileges = 0;
+    //if ( !isset($this->collection->path_privs) ) {
+     if ( !isset($this->parent) ) $this->FetchParentContainer();
+     // $this->collection->path_privs = $this->parent->Privileges();
+     // $this->collection->user_no = $this->parent->GetProperty('user_no');
+     // $this->collection->principal_id = $this->parent->GetProperty('principal_id');
+     
+       //$path = preg_replace("/\/|addressbook/","",$this->collection->dav_name);
+       $path = preg_split('/\//', $this->collection->dav_name);
+       dbg_error_log( 'DAVResource', 'NOVA PASTA "%s"',$path[1]);
+       $filtro = "uid=".$path[1];
+       $atributos = array("uidNumber");
+       $uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+       $this->collection->user_no = $uidnumber['uidNumber'];  
+     //}
+   if ( $session->username != $path[1] && $this->collection->is_calendar ) {
+         $this->privileges = 0;
+         $newpath = $this->collection->user_no;
+         $filtro = "uid=".$session->username;
+         $atributos = array("uidNumber");
+         $accountt = ldapDrivers::requestAtributo($filtro, $atributos);
+         $account = $accountt['uidNumber'];
+         $qry = new AwlQuery("select acl_rights from phpgw_acl where acl_location = :account and acl_account = :path" ,array(':account' => $account ,':path' => $newpath));
+         if ( $qry->Exec('Request',__LINE__,__FILE__) &&  $qry->rows() == 1 && $permission_result = $qry->Fetch()){
+               switch( $permission_result->acl_rights ) {
+                     case '1' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+                                  dbg_error_log( "caldav", "Basic read permissions for user accessing " );
+                                  break;
+                     case '3' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write'));
+                                  dbg_error_log( "caldav", "Basic read/write permissions for user accessing " );
+                                  break;
+                     case '7' :   $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write'));
+                                  dbg_error_log( "caldav", "Basic read/write/edit permissions for user accessing " );
+                                  break;
+                     case '15' :  $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl','write','schedule-send'));
+                                  dbg_error_log( "caldav", "Basic read/write/edit/delete  permissions for user accessing  " );
+                                  break;
+                     case '31' :  $this->privileges = privilege_to_bits('all');
+                                  dbg_error_log( "caldav", "Full permissions for %s for path %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator"), $this->dav_name );
+                                  break;
+             }
+
+         }
+      }
+    else if (  $session->username == $path[1] )
+      {  
+         $this->privileges = privilege_to_bits('all');             
+     }
+    //$this->privileges = privilege_to_bits('all');//$this->collection->path_privs;
+    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+
+    dbg_error_log( 'DAVResource', 'Privileges of "%s" for user "%s" accessing "%s"',
+                       decbin($this->privileges), $session->username, $this->dav_name() );
+
+    if ( isset($request->ticket) && $request->ticket->MatchesPath($this->bound_from()) ) {
+      $this->privileges |= $request->ticket->privileges();
+      dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
+    }
+
+    if ( isset($this->tickets) ) {
+      if ( !isset($this->resource_id) ) $this->FetchResource();
+      foreach( $this->tickets AS $k => $ticket ) {
+        if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
+          $this->privileges |= $ticket->privileges();
+          dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
+        }
+      }
+    }
+  }
+
+
+  /**
+  * Return the privileges bits for the current session user to this resource
+  */
+  function Privileges() {
+    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+    return $this->privileges;
+  }
+
+
+  /**
+  * Is the user has the privileges to do what is requested.
+  * @param $do_what mixed The request privilege name, or array of privilege names, to be checked.
+  * @param $any boolean Whether we accept any of the privileges. The default is true, unless the requested privilege is 'all', when it is false.
+  * @return boolean Whether they do have one of those privileges against this resource.
+  */
+  function HavePrivilegeTo( $do_what, $any = null ) {
+    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+    if ( !isset($any) ) $any = ($do_what != 'all');
+    $test_bits = privilege_to_bits( $do_what );
+    dbg_error_log( 'DAVResource', 'Testing %s privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', ($any?'any':'exactly'),
+        $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+    if ( $any ) {
+      return ($this->privileges & $test_bits) > 0;
+    }
+    else {
+      return ($this->privileges & $test_bits) == $test_bits;
+    }
+  }
+
+
+  /**
+  * Check if we have the needed privilege or send an error response.
+  *
+  * @param string $privilege The name of the needed privilege.
+  */
+  function NeedPrivilege( $privilege, $any = null ) {
+    global $request;
+
+    if ( $this->HavePrivilegeTo($privilege, $any) ) return;
+
+    $request->NeedPrivilege( $privilege, $this->dav_name );
+    exit(0);  // Unecessary, but might clarify things
+  }
+
+
+  /**
+  * Returns the array of privilege names converted into XMLElements
+  */
+  function BuildPrivileges( $privilege_names=null, &$xmldoc=null ) {
+    if ( $privilege_names == null ) {
+      if ( !isset($this->privileges) ) $this->FetchPrivileges();
+      $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type : null ) );
+    }
+    return privileges_to_XML( $privilege_names, $xmldoc);
+  }
+
+
+  /**
+  * Returns the array of supported methods
+  */
+  function FetchSupportedMethods( ) {
+    if ( isset($this->supported_methods) ) return $this->supported_methods;
+
+    $this->supported_methods = array(
+      'OPTIONS' => '',
+      'PROPFIND' => '',
+      'REPORT' => '',
+      'DELETE' => '',
+      'LOCK' => '',
+      'UNLOCK' => '',
+      'MOVE' => ''
+    );
+    if ( $this->IsCollection() ) {
+/*      if ( $this->IsPrincipal() ) {
+        $this->supported_methods['MKCALENDAR'] = '';
+        $this->supported_methods['MKCOL'] = '';
+      } */
+      switch ( $this->collection->type ) {
+        case 'root':
+        case 'email':
+          // We just override the list completely here.
+          $this->supported_methods = array(
+            'OPTIONS' => '',
+            'PROPFIND' => '',
+            'REPORT' => ''
+          );
+          break;
+
+        case 'schedule-outbox':
+          $this->supported_methods = array_merge(
+            $this->supported_methods,
+            array(
+              'POST' => '', 'PROPPATCH' => '', 'MKTICKET' => '', 'DELTICKET' => ''
+            )
+          );
+          break;
+        case 'schedule-inbox':
+        case 'calendar':
+          $this->supported_methods['GET'] = '';
+          $this->supported_methods['PUT'] = '';
+          $this->supported_methods['HEAD'] = '';
+          $this->supported_methods['MKTICKET'] = '';
+          $this->supported_methods['DELTICKET'] = '';
+          break;
+        case 'collection':
+          $this->supported_methods['MKTICKET'] = '';
+          $this->supported_methods['DELTICKET'] = '';
+        case 'principal':
+          $this->supported_methods['GET'] = '';
+          $this->supported_methods['PUT'] = '';
+          $this->supported_methods['HEAD'] = '';
+          $this->supported_methods['MKCOL'] = '';
+          $this->supported_methods['MKCALENDAR'] = '';
+          $this->supported_methods['PROPPATCH'] = '';
+          break;
+      }
+    }
+    else {
+      $this->supported_methods = array_merge(
+        $this->supported_methods,
+        array(
+          'GET' => '', 'HEAD' => '', 'PUT' => '', 'MKTICKET' => '', 'DELTICKET' => ''
+        )
+      );
+    }
+
+    return $this->supported_methods;
+  }
+
+
+  /**
+  * Returns the array of supported methods converted into XMLElements
+  */
+  function BuildSupportedMethods( ) {
+    if ( !isset($this->supported_methods) ) $this->FetchSupportedMethods();
+    $methods = array();
+    foreach( $this->supported_methods AS $k => $v ) {
+//      dbg_error_log( 'DAVResource', ':BuildSupportedMethods: Adding method "%s" which is "%s".', $k, $v );
+      $methods[] = new XMLElement( 'supported-method', null, array('name' => $k) );
+    }
+    return $methods;
+  }
+
+
+  /**
+  * Returns the array of supported reports
+  */
+  function FetchSupportedReports( ) {
+    if ( isset($this->supported_reports) ) return $this->supported_reports;
+   /************* comentado por incompatibilidade do sync-collection 
+    $this->supported_reports = array(
+      'DAV::principal-property-search' => '',
+      'DAV::principal-search-property-set' => '',
+      'DAV::expand-property' => '',
+      'DAV::sync-collection' => ''
+    ); */
+    $this->supported_reports = array(
+      'DAV::principal-property-search' => '',
+      'DAV::principal-search-property-set' => '',
+      'DAV::expand-property' => ''
+       
+    );
+
+    if ( !isset($this->collection) ) $this->FetchCollection();
+
+    if ( $this->collection->is_calendar ) {
+      $this->supported_reports = array_merge(
+        $this->supported_reports,
+        array(
+          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+        )
+      );
+    }
+    if ( $this->collection->is_addressbook ) {
+      $this->supported_reports = array_merge(
+        $this->supported_reports,
+        array(
+          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+        )
+      );
+    }
+    return $this->supported_reports;
+  }
+
+
+  /**
+  * Returns the array of supported reports converted into XMLElements
+  */
+  function BuildSupportedReports( &$reply ) {
+    if ( !isset($this->supported_reports) ) $this->FetchSupportedReports();
+    $reports = array();
+    foreach( $this->supported_reports AS $k => $v ) {
+      dbg_error_log( 'DAVResource', ':BuildSupportedReports: Adding supported report "%s" which is "%s".', $k, $v );
+      $report = new XMLElement('report');
+      $reply->NSElement($report, $k );
+      $reports[] = new XMLElement('supported-report', $report );
+    }
+    return $reports;
+  }
+
+
+  /**
+  * Fetches an array of the access_ticket records applying to this path
+  */
+  function FetchTickets( ) {
+    global $c;
+    if ( isset($this->access_tickets) ) return;
+    $this->access_tickets = array();
+
+    $sql =
+'SELECT access_ticket.*, COALESCE( resource.dav_name, collection.dav_name) AS target_dav_name,
+        (access_ticket.expires < current_timestamp) AS expired,
+        dav_principal.dav_name AS principal_dav_name,
+        EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds,
+        path_privs(access_ticket.dav_owner_id,collection.dav_name,:scan_depth) AS grantor_collection_privileges
+    FROM access_ticket JOIN collection ON (target_collection_id = collection_id)
+        JOIN dav_principal ON (dav_owner_id = principal_id)
+        LEFT JOIN caldav_data resource ON (resource.dav_id = access_ticket.target_resource_id)
+  WHERE target_collection_id = :collection_id ';
+    $params = array(':collection_id' => $this->collection->collection_id, ':scan_depth' => $c->permission_scan_depth);
+    if ( $this->IsCollection() ) {
+      $sql .= 'AND target_resource_id IS NULL';
+    }
+    else {
+      if ( !isset($this->exists) ) $this->FetchResource();
+      $sql .= 'AND target_resource_id = :dav_id';
+      $params[':dav_id'] = $this->resource->dav_id;
+    }
+    if ( isset($this->exists) && !$this->exists ) return;
+
+    $qry = new AwlQuery( $sql, $params );
+    if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
+      while( $ticket = $qry->Fetch() ) {
+        $this->access_tickets[] = $ticket;
+      }
+    }
+  }
+
+
+  /**
+  * Returns the array of tickets converted into XMLElements
+  *
+  * If the current user does not have DAV::read-acl privilege on this resource they
+  * will only get to see the tickets where they are the owner, or which they supplied
+  * along with the request.
+  *
+  * @param &XMLDocument $reply A reference to the XMLDocument used to construct the reply
+  * @return XMLTreeFragment A fragment of an XMLDocument to go in the reply
+  */
+  function BuildTicketinfo( &$reply ) {
+    global $session, $request;
+
+    if ( !isset($this->access_tickets) ) $this->FetchTickets();
+    $tickets = array();
+    $show_all = $this->HavePrivilegeTo('DAV::read-acl');
+    foreach( $this->access_tickets AS $meh => $trow ) {
+      if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) ) continue;
+      dbg_error_log( 'DAVResource', ':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
+      $ticket = new XMLElement( $reply->Tag( 'ticketinfo', 'http://www.xythos.com/namespaces/StorageServer', 'TKT' ) );
+      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
+      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
+      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf( 'Seconds-%d', $trow->seconds) : 'infinity') );
+      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:visits', 'infinity' );
+      $privs = array();
+      foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
+        $privs[] = $reply->NewXMLElement($v);
+      }
+      $reply->NSElement($ticket, 'DAV::privilege', $privs );
+      $tickets[] = $ticket;
+    }
+    return $tickets;
+  }
+
+
+  /**
+  * Checks whether the resource is locked, returning any lock token, or false
+  *
+  * @todo This logic does not catch all locking scenarios.  For example an infinite
+  * depth request should check the permissions for all collections and resources within
+  * that.  At present we only maintain permissions on a per-collection basis though.
+  */
+  function IsLocked( $depth = 0 ) {
+    if ( !isset($this->_locks_found) ) {
+      $this->_locks_found = array();
+      /**
+      * Find the locks that might apply and load them into an array
+      */
+      $sql = 'SELECT * FROM locks WHERE :this_path::text ~ (\'^\'||dav_name||:match_end)::text';
+      $qry = new AwlQuery($sql, array( ':this_path' => $this->dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
+      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) ) {
+        while( $lock_row = $qry->Fetch() ) {
+          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+        }
+      }
+      else {
+        $this->DoResponse(500,i18n("Database Error"));
+        // Does not return.
+      }
+    }
+
+    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
+        return $lock_token;
+      }
+    }
+
+    return false;  // Nothing matched
+  }
+
+
+  /**
+  * Checks whether this resource is a collection
+  */
+  function IsCollection() {
+    return $this->_is_collection;
+  }
+
+
+  /**
+  * Checks whether this resource is a principal
+  */
+  function IsPrincipal() {
+    return $this->_is_collection && $this->_is_principal;
+  }
+
+
+  /**
+  * Checks whether this resource is a calendar
+  */
+  function IsCalendar() {
+    return $this->_is_collection && $this->_is_calendar;
+  }
+
+
+  /**
+  * Checks whether this resource is a calendar
+  * @param string $type The type of scheduling collection, 'read', 'write' or 'any'
+  */
+  function IsSchedulingCollection( $type = 'any' ) {
+    if ( $this->_is_collection && preg_match( '{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
+      return ($type == 'any' || $type == $matches[1]);
+    }
+    return false;
+  }
+
+
+  /**
+  * Checks whether this resource is an addressbook
+  */
+  function IsAddressbook() {
+    return $this->_is_collection && $this->_is_addressbook;
+  }
+
+
+  /**
+  * Checks whether this resource is a bind to another resource
+  */
+  function IsBinding() {
+    return $this->_is_binding;
+  }
+
+
+  /**
+  * Checks whether this resource actually exists, in the virtual sense, within the hierarchy
+  */
+  function Exists() {
+    if ( ! isset($this->exists) ) {
+      if ( $this->IsPrincipal() ) {
+        if ( !isset($this->principal) ) $this->FetchPrincipal();
+        $this->exists = $this->principal->Exists();
+      }
+      else if ( ! $this->IsCollection() ) {
+        if ( !isset($this->resource) ) $this->FetchResource();
+      }
+    }
+    //dbg_error_log('DAVResource',' Checking whether "%s" exists.  It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
+    return $this->exists;
+  }
+
+
+  /**
+  * Checks whether the container for this resource actually exists, in the virtual sense, within the hierarchy
+  */
+  function ContainerExists() {
+    if ( $this->collection->dav_name != $this->dav_name ) {
+      return $this->collection->exists;
+    }
+    $parent = $this->FetchParentContainer();
+    return $parent->Exists();
+  }
+
+
+  /**
+  * Returns the dav_name of the resource in our internal namespace
+  */
+  function dav_name() {
+    if ( isset($this->dav_name) ) return $this->dav_name;
+    return null;
+  }
+
+
+  /**
+  * Returns the dav_name of the resource we are bound to, within our internal namespace
+  */
+  function bound_from() {
+    if ( isset($this->bound_from) ) return $this->bound_from;
+    return $this->dav_name();
+  }
+
+
+  /**
+  * Sets the dav_name of the resource we are bound as
+  */
+  function set_bind_location( $new_dav_name ) {
+    if ( !isset($this->bound_from) && isset($this->dav_name) ) {
+      $this->bound_from = $this->dav_name;
+    }
+    $this->dav_name = $new_dav_name;
+    return $this->dav_name;
+  }
+
+
+  /**
+  * Returns the dav_name of the resource in our internal namespace
+  */
+  function parent_path() {
+    if ( $this->IsCollection() ) {
+      if ( !isset($this->collection) ) $this->FetchCollection();
+      if ( !isset($this->collection->parent_container) ) {
+        $this->collection->parent_container = preg_replace( '{[^/]+/$}', '', $this->bound_from());
+      }
+      return $this->collection->parent_container;
+    }
+    return preg_replace( '{[^/]+$}', '', $this->bound_from());
+  }
+
+
+
+  /**
+  * Returns the principal-URL for this resource
+  */
+  function principal_url() {
+    if ( !isset($this->principal) ) $this->FetchPrincipal();
+    if ( $this->principal->Exists() ) {
+      return $this->principal->principal_url;
+    }
+    return null;
+  }
+
+
+  /**
+  * Returns the database row for this resource
+  */
+  function resource() {
+    if ( !isset($this->resource) ) $this->FetchResource();
+    return $this->resource;
+  }
+
+
+  /**
+  * Returns the unique_tag (ETag ) for this resource
+  */
+  function unique_tag() {
+    if ( isset($this->unique_tag) ) return $this->unique_tag;
+    if ( isset($this->unique_tag) ) 
+             {
+         $tag = md5(Date("U"));
+         return $tag;
+         }   
+    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+
+    if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
+     
+     $tag = md5(Date("U"));
+     return $tag;
+
+     //return $this->unique_tag;
+  }
+  /**
+  * Returns the unique_tag (getctag) for this resource
+  */
+  function unique_ctag() {
+    //if ( isset($this->unique_tag) ) return $this->unique_tag;
+    if ( isset($this->unique_tag) ) 
+             {
+         $tag = md5(Date("U"));
+         return $tag;
+         }   
+    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+
+    if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
+     
+     $tag = md5(Date("U"));
+     return $tag;
+
+     //return $this->unique_tag;
+  }
+
+
+  /**
+  * Returns the definitive resource_id for this resource - usually a dav_id
+  */
+  function resource_id() {
+    if ( isset($this->resource_id) ) return $this->resource_id;
+    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+
+    if ( $this->exists !== true || !isset($this->resource_id) ) $this->resource_id = null;
+
+    return $this->resource_id;
+  }
+
+
+  /**
+  * Checks whether the target collection is publicly_readable
+  */
+  function IsPublic() {
+    return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' );
+  }
+
+
+  /**
+  * Return the type of whatever contains this resource, or would if it existed.
+  */
+  function ContainerType() {
+    if ( $this->IsPrincipal() ) return 'root';
+    if ( !$this->IsCollection() ) return $this->collection->type;
+
+    if ( ! isset($this->collection->parent_container) ) return null;
+
+    if ( isset($this->parent_container_type) ) return $this->parent_container_type;
+
+    if ( preg_match('#/[^/]+/#', $this->collection->parent_container) ) {
+      $this->parent_container_type = 'principal';
+    }
+    else {
+      $qry = new AwlQuery('SELECT * FROM collection WHERE dav_name = :parent_name',
+                                array( ':parent_name' => $this->collection->parent_container ) );
+      if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
+        if ( $parent->is_calendar == 't' )
+          $this->parent_container_type = 'calendar';
+        else if ( $parent->is_addressbook == 't' )
+          $this->parent_container_type = 'addressbook';
+        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+          $this->parent_container_type = 'schedule-'. $matches[3]. 'box';
+        else
+          $this->parent_container_type = 'collection';
+      }
+      else
+        $this->parent_container_type = null;
+    }
+    return $this->parent_container_type;
+  }
+
+
+  /**
+  * BuildACE - construct an XMLElement subtree for a DAV::ace
+  */
+  function BuildACE( &$xmldoc, $privs, $principal ) {
+    $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type : 'resource'));
+    $privileges = array();
+    foreach( $privilege_names AS $k ) {
+      $privilege = new XMLElement('privilege');
+      if ( isset($xmldoc) )
+        $xmldoc->NSElement($privilege,$k);
+      else
+        $privilege->NewElement($k);
+      $privileges[] = $privilege;
+    }
+    $ace = new XMLElement('ace', array(
+                new XMLElement('principal', $principal),
+                new XMLElement('grant', $privileges ) )
+              );
+    return $ace;
+  }
+
+  /**
+  * Return ACL settings
+  */
+  function GetACL( &$xmldoc ) {
+    global $c, $session;
+
+    if ( !isset($this->principal) ) $this->FetchPrincipal();
+    $default_privs = $this->principal->default_privileges;
+    if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
+
+    $acl = array();
+    $acl[] = $this->BuildACE($xmldoc, pow(2,25) - 1, new XMLElement('property', new XMLElement('owner')) );
+
+    $qry = new AwlQuery('SELECT dav_principal.dav_name, grants.* FROM grants JOIN dav_principal ON (to_principal=principal_id) WHERE by_collection = :collection_id OR by_principal = :principal_id ORDER BY by_collection',
+                                array( ':collection_id' => $this->collection->collection_id, ':principal_id' => $this->principal->principal_id ) );
+    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+      $by_collection = null;
+      while( $grant = $qry->Fetch() ) {
+        if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
+        if ( $by_collection &&  !isset($grant->by_collection) ) break;
+        $acl[] = $this->BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
+      }
+    }
+
+    $acl[] = $this->BuildACE($xmldoc, $default_privs, new XMLElement('authenticated') );
+
+    return $acl;
+
+  }
+
+
+  /**
+  * Return general server-related properties, in plain form
+  */
+  function GetProperty( $name ) {
+    global $c, $session;
+
+    //dbg_error_log( 'DAVResource', ':GetProperty: Fetching "%s".', $name );
+    $value = null;
+
+    switch( $name ) {
+      case 'collection_id':
+        return $this->collection->collection_id;
+        break;
+
+      case 'resourcetype':
+        if ( isset($this->resourcetypes) ) {
+          $this->resourcetypes = preg_replace('{^\s*<(.*)/>\s*$}', '$1', $this->resourcetypes);
+          $type_list = preg_split('{(/>\s*<|\n)}', $this->resourcetypes);
+          foreach( $type_list AS $k => $resourcetype ) {
+            if ( preg_match( '{^([^:]+):([^:]+) \s+ xmlns:([^=]+)="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
+              $type_list[$k] = $matches[4] .':' .$matches[2];
+            }
+            else if ( preg_match( '{^([^:]+) \s+ xmlns="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
+              $type_list[$k] = $matches[2] .':' .$matches[1];
+            }
+          }
+          return $type_list;
+        }
+
+      default:
+        if ( isset($this->{$name}) ) return $this->{$name};
+        if ( $this->_is_principal ) {
+          if ( !isset($this->principal) ) $this->FetchPrincipal();
+          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+        }
+        else if ( $this->_is_collection ) {
+          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+        }
+        else {
+          if ( !isset($this->resource) ) $this->FetchResource();
+          if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
+          if ( !isset($this->principal) ) $this->FetchPrincipal();
+          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+        }
+      //  dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
+    }
+
+    return $value;
+  }
+
+
+  /**
+  * Return an array which is an expansion of the DAV::allprop
+  */
+  function DAV_AllProperties() {
+    if ( isset($this->dead_properties) ) $this->FetchDeadProperties();
+    $allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()),
+      (isset($include_properties)?$include_properties:array()),
+      array(
+        'DAV::getcontenttype', 'DAV::resourcetype', 'DAV::getcontentlength', 'DAV::displayname', 'DAV::getlastmodified',
+        'DAV::creationdate', 'DAV::getetag', 'DAV::getcontentlanguage', 'DAV::supportedlock', 'DAV::lockdiscovery',
+        'DAV::owner', 'DAV::principal-URL', 'DAV::current-user-principal',
+        'urn:ietf:params:xml:ns:carddav:max-resource-size', 'urn:ietf:params:xml:ns:carddav:supported-address-data',
+        'urn:ietf:params:xml:ns:carddav:addressbook-description', 'urn:ietf:params:xml:ns:carddav:addressbook-home-set'
+      ) );
+
+    return $allprop;
+  }
+
+
+  /**
+  * Return general server-related properties for this URL
+  */
+  function ResourceProperty( $tag, $prop, &$reply, &$denied ) {
+    global $c, $session, $request;
+
+//    dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
+
+    if ( $reply === null ) $reply = $GLOBALS['reply'];
+
+    switch( $tag ) {
+      case 'DAV::allprop':
+        $property_list = $this->DAV_AllProperties();
+        $discarded = array();
+        foreach( $property_list AS $k => $v ) {
+          $this->ResourceProperty($v, $prop, $reply, $discarded);
+        }
+        break;
+
+      case 'DAV::href':
+        $prop->NewElement('href', ConstructURL($this->dav_name) );
+        break;
+
+      case 'DAV::resource-id':
+        if ( $this->resource_id > 0 )
+          $reply->DAVElement( $prop, 'resource-id', $reply->href(ConstructURL('/.resources/'.$this->resource_id) ) );
+        else
+          return false;
+        break;
+
+      case 'DAV::parent-set':
+        $sql = <<<EOQRY
+SELECT b.parent_container FROM dav_binding b JOIN collection c ON (b.bound_source_id=c.collection_id)
+ WHERE regexp_replace( b.dav_name, '^.*/', c.dav_name ) = :bound_from
+EOQRY;
+        $qry = new AwlQuery($sql, array( ':bound_from' => $this->bound_from() ) );
+        $parents = array();
+        if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+          while( $row = $qry->Fetch() ) {
+            $parents[$row->parent_container] = true;
+          }
+        }
+        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->bound_from())] = true;
+        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->dav_name())] = true;
+
+        $parent_set = $reply->DAVElement( $prop, 'parent-set' );
+        foreach( $parents AS $parent => $v ) {
+          if ( preg_match( '{^(.*)?/([^/]+)/?$}', $parent, $matches ) ) {
+            $reply->DAVElement($parent_set, 'parent', array(
+                                new XMLElement( 'href', ConstructURL($matches[1])),
+                                new XMLElement( 'segment', $matches[2])
+                              ));
+          }
+          else if ( $parent == '/' ) {
+            $reply->DAVElement($parent_set, 'parent', array(
+                                new XMLElement( 'href', '/'),
+                                new XMLElement( 'segment', ( ConstructURL('/') == '/caldav.php/' ? 'caldav.php' : ''))
+                              ));
+          }
+        }
+        break;
+
+      case 'DAV::getcontenttype':
+        if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+        //if ( !isset($this->contenttype)  && !isset($this->resource) ) $this->FetchResource();
+        $prop->NewElement('getcontenttype', $this->contenttype );
+        break;
+
+      case 'DAV::resourcetype':
+        $resourcetypes = $prop->NewElement('resourcetype' );
+        $type_list = $this->GetProperty('resourcetype');
+        if ( !is_array($type_list) ) return true;
+//        dbg_error_log( 'DAVResource', ':ResourceProperty: "%s" are "%s".', $tag, implode(', ',$type_list) );
+        foreach( $type_list AS $k => $v ) {
+          if ( $v == '' ) continue;
+          $reply->NSElement( $resourcetypes, $v );
+        }
+        if ( $this->_is_binding ) {
+          $reply->NSElement( $resourcetypes, 'http://xmlns.davical.org/davical:webdav-binding' );
+        }
+        break;
+
+      case 'DAV::getlastmodified':
+        /** peculiarly, it seems that getlastmodified is HTTP Date format! */
+        $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
+        break;
+
+      case 'DAV::creationdate':
+        /** bizarrely, it seems that creationdate is ISO8601 format */
+        $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
+        break;
+
+      case 'DAV::getcontentlength':
+        if ( $this->_is_collection ) return false;
+        if ( !isset($this->resource) ) $this->FetchResource();
+        if ( isset($this->resource) ) {
+          $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
+        }
+        break;
+
+      case 'DAV::getcontentlanguage':
+        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+        $reply->NSElement($prop, $tag, $locale );
+        break;
+
+      case 'DAV::acl-restrictions':
+        $reply->NSElement($prop, $tag, array( new XMLElement('grant-only'), new XMLElement('no-invert') ) );
+        break;
+
+      case 'DAV::inherited-acl-set':
+        $inherited_acls = array();
+        if ( ! $this->_is_collection ) {
+          $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
+        }
+        $reply->NSElement($prop, $tag, $inherited_acls );
+        break;
+
+      case 'DAV::owner':
+        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+        $owner_url = ( isset($this->_is_binding) && $this->_is_binding ? $this->collection->bind_owner_url : $this->principal_url() );
+        $reply->DAVElement( $prop, 'owner', $reply->href( $owner_url ) );
+        break;
+
+      // Empty tag responses.
+      case 'DAV::group':
+      case 'DAV::alternate-URI-set':
+        $reply->NSElement($prop, $tag );
+        break;
+
+      case 'DAV::getetag':
+        if ( $this->_is_collection ) return false;
+        $reply->NSElement($prop, $tag, $this->unique_tag() );
+        break;
+
+      case 'http://calendarserver.org/ns/:getctag':
+        if ( ! $this->_is_collection ) return false;
+        $reply->NSElement($prop, $tag, $this->unique_ctag() );
+        break;
+
+      case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
+        $proxy_type = 'read';
+      case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
+        if ( !isset($proxy_type) ) $proxy_type = 'write';
+        $reply->CalendarserverElement($prop, 'calendar-proxy-'.$proxy_type.'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
+        break;
+
+      case 'DAV::current-user-privilege-set':
+        if ( $this->HavePrivilegeTo('DAV::read-current-user-privilege-set') ) {
+        //if(true){   
+          $reply->NSElement($prop, $tag, $this->BuildPrivileges() );
+        }
+        else {
+          $denied[] = $tag;
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
+        if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() ) return false;
+        $reply->NSElement($prop, $tag, 'text/calendar' );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
+        if ( ! $this->_is_collection ) return false;
+        if ( $this->IsCalendar() )
+          $set_of_components = array( 'VEVENT', 'VTODO', 'VJOURNAL', 'VTIMEZONE', 'VFREEBUSY' );
+        else if ( $this->IsSchedulingCollection() )
+          $set_of_components = array( 'VEVENT', 'VTODO', 'VFREEBUSY' );
+        else return false;
+        $components = array();
+        foreach( $set_of_components AS $v ) {
+          $components[] = $reply->NewXMLElement( 'comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
+        }
+        $reply->CalDAVElement($prop, 'supported-calendar-component-set', $components );
+        break;
+
+      case 'DAV::supported-method-set':
+        $prop->NewElement('supported-method-set', $this->BuildSupportedMethods() );
+        break;
+
+      case 'DAV::supported-report-set':
+        $prop->NewElement('supported-report-set', $this->BuildSupportedReports( $reply ) );
+        break;
+
+      case 'DAV::supportedlock':
+        $prop->NewElement('supportedlock',
+          new XMLElement( 'lockentry',
+            array(
+              new XMLElement('lockscope', new XMLElement('exclusive')),
+              new XMLElement('locktype',  new XMLElement('write')),
+            )
+          )
+        );
+        break;
+
+      case 'DAV::supported-privilege-set':
+        $prop->NewElement('supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
+        break;
+
+      case 'DAV::current-user-principal':
+        $prop->NewElement('current-user-principal', $reply->href( $request->principal->principal_url ) );
+        break;
+
+      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+        $denied[] = $reply->Tag($tag);
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+        if ( ! $this->_is_collection ) return false;
+        if ( !isset($this->collection->tz_spec) || $this->collection->tz_spec == '' ) return false;
+
+        $cal = new iCalComponent();
+        $cal->VCalendar();
+        $cal->AddComponent( new iCalComponent($this->collection->tz_spec) );
+        $reply->NSElement($prop, $tag, $cal->Render() );
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:address-data':
+      case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+        if ( $this->_is_collection ) return false;
+        if ( !isset($this->resource) ) $this->FetchResource();
+        $reply->NSElement($prop, $tag, $this->resource->caldav_data );
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
+        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+        $reply->NSElement($prop, $tag, 65500 );
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
+        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+        $address_data = $reply->NewXMLElement( 'address-data', false,
+                      array( 'content-type' => 'text/vcard', 'version' => '3.0'), 'urn:ietf:params:xml:ns:carddav');
+        $reply->NSElement($prop, $tag, $address_data );
+        break;
+
+      case 'DAV::acl':
+        if ( $this->HavePrivilegeTo('DAV::read-acl') ) {
+          $reply->NSElement($prop, $tag, $this->GetACL( $reply ) );
+        }
+        else {
+          $denied[] = $tag;
+        }
+        break;
+
+      case 'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery':
+      case 'DAV::ticketdiscovery':
+        $reply->NSElement($prop,'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply) );
+        break;
+
+      default:
+        $property_value = $this->GetProperty(preg_replace('{^.*:}', '', $tag));
+        if ( isset($property_value) ) {
+          $reply->NSElement($prop, $tag, $property_value );
+        }
+        else {
+          if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
+          if ( isset($this->dead_properties[$tag]) ) {
+            $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
+          }
+          else {
+//            dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
+            return false;
+          }
+        }
+    }
+
+    return true;
+  }
+
+
+  /**
+  * Construct XML propstat fragment for this resource
+  *
+  * @param array of string $properties The requested properties for this resource
+  *
+  * @return string An XML fragment with the requested properties for this resource
+  */
+  function GetPropStat( $properties, &$reply, $props_only = false ) {
+    global $request;
+
+    dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
+
+    $prop = new XMLElement('prop');
+    $denied = array();
+    $not_found = array();
+    foreach( $properties AS $k => $tag ) {
+      if ( is_object($tag) ) {
+        dbg_error_log( 'DAVResource', ':GetPropStat: "$properties" should be an array of text. Assuming this object is an XMLElement!.' );
+        $tag = $tag->GetTag();
+      }
+      $found = $this->ResourceProperty($tag, $prop, $reply, $denied );
+      if ( !$found ) {
+        if ( !isset($this->principal) ) $this->FetchPrincipal();
+        $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
+      }
+      if ( ! $found ) {
+        dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
+        $not_found[] = $reply->Tag($tag);
+      }
+    }
+    if ( $props_only ) return $prop;
+
+    $status = new XMLElement('status', 'HTTP/1.1 200 OK' );
+
+    $elements = array( new XMLElement( 'propstat', array($prop,$status) ) );
+
+    if ( count($denied) > 0 ) {
+      $status = new XMLElement('status', 'HTTP/1.1 403 Forbidden' );
+      $noprop = new XMLElement('prop');
+      foreach( $denied AS $k => $v ) {
+        $reply->NSElement($noprop, $v);
+      }
+      $elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
+    }
+
+    if ( count($not_found) > 0 ) {
+      $status = new XMLElement('status', 'HTTP/1.1 404 Not Found' );
+      $noprop = new XMLElement('prop');
+      foreach( $not_found AS $k => $v ) {
+        $noprop->NewElement($v);
+      }
+      $elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
+    }
+    return $elements;
+  }
+
+
+  /**
+  * Render XML for this resource
+  *
+  * @param array $properties The requested properties for this principal
+  * @param reference $reply A reference to the XMLDocument being used for the reply
+  *
+  * @return string An XML fragment with the requested properties for this principal
+  */
+  function RenderAsXML( $properties, &$reply, $bound_parent_path = null ) {
+    global $session, $c, $request;
+
+    dbg_error_log('DAVResource',':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
+
+    if ( !$this->Exists() ) return null;
+    $elements = $this->GetPropStat( $properties, $reply );
+    if ( isset($bound_parent_path) ) {
+      $dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
+      $dav_name = "/".$this->dav_name;    
+     }
+    else {
+      $dav_name =  "/".$this->dav_name;
+    }
+
+    array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
+
+    $response = new XMLElement( 'response', $elements );
+
+    return $response;
+  }
+
+}
Index: contrib/davical/inc/Privileges
===================================================================
--- contrib/davical/inc/Privileges	(revision 3733)
+++ contrib/davical/inc/Privileges	(revision 3733)
@@ -0,0 +1,4360 @@
+always.php.in:$c->script_start_time = microtime(true);
+always.php.in:// An ultra-simple exception handler to catch errors that occur
+always.php.in:  echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
+always.php.in:  $trace = array_reverse($e->getTrace());
+always.php.in:$c->sysabbr     = 'davical';
+always.php.in:$c->admin_email = 'admin@davical.example.com';
+always.php.in:$c->system_name = 'DAViCal CalDAV Server';
+always.php.in:$c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+always.php.in:$c->save_time_zone_defs = true;
+always.php.in:$c->collections_always_exist = false;
+always.php.in:$c->allow_get_email_visibility = false;
+always.php.in:$c->permission_scan_depth = 2;
+always.php.in:$c->expand_pdo_parameters = true;
+always.php.in:$c->home_calendar_name = 'home';
+always.php.in:$c->enable_row_linking = true;
+always.php.in:$c->enable_scheduling = false;
+always.php.in:$c->http_auth_mode = 'Basic';
+always.php.in:// $c->default_locale = array('es_MX', 'es_AR', 'es', 'pt');  // An array of locales to try, or just a single locale
+always.php.in:// $c->local_tzid = 'Pacific/Auckland';  // Perhaps we should read from /etc/timezone - I wonder how standard that is?
+always.php.in:$c->default_locale = 'en';
+always.php.in:$c->locale_path = '../locale';
+always.php.in:$c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
+always.php.in:$c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
+always.php.in:$c->default_privileges = array('read-free-busy', 'schedule-deliver');
+always.php.in:$c->stylesheets = array( $c->base_url.'/davical.css' );
+always.php.in:$c->images      = $c->base_url . '/images';
+always.php.in:$c->template_usr = array( 'active' => true,
+always.php.in:                          'email_ok' => date('Y-m-d')
+always.php.in:$c->hide_TODO = true;                      // VTODO only visible to collection owner
+always.php.in:$c->readonly_webdav_collections = true;    // WebDAV access is readonly
+always.php.in:$c->total_query_time = 0;
+always.php.in:$c->dbg = array();
+always.php.in:  @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration.  Using IP address meanhwhile..." );
+always.php.in:$c->protocol_server_port = sprintf( '%s://%s%s',
+always.php.in:$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
+always.php.in:if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+always.php.in:  include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+always.php.in:else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+always.php.in:  include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+always.php.in:if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
+always.php.in:else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
+always.php.in:         $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
+always.php.in:putenv("LANG=". $c->default_locale);
+always.php.in:awl_set_locale($c->default_locale);
+always.php.in:init_gettext( 'davical', $c->locale_path );
+always.php.in:$c->code_version = 0;
+always.php.in:$c->version_string = '0.9.8.3'; // The actual version # is replaced into that during the build /release process
+always.php.in:if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
+always.php.in:  $c->code_major = $matches[1];
+always.php.in:  $c->code_minor = $matches[2];
+always.php.in:  $c->code_patch = $matches[3];
+always.php.in:  $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
+always.php.in:  dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
+always.php.in:  header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
+always.php.in:$_SERVER['SERVER_NAME'] = $c->domain_name;
+always.php.in:$c->want_dbversion = array(1,2,8);
+always.php.in:$c->schema_version = 0;
+always.php.in:if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
+always.php.in:  $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
+always.php.in:  $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
+always.php.in:  $c->schema_major = $row->schema_major;
+always.php.in:  $c->schema_minor = $row->schema_minor;
+always.php.in:  $c->schema_patch = $row->schema_patch;
+always.php.in:  if ( $c->schema_version < $c->wanted_version ) {
+always.php.in:    $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
+always.php.in:             $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
+always.php.in:  if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) $qry->QDo('SET TIMEZONE TO \'Pacific/Auckland\'');
+always.php.in:  if ( isset($session->principal_id) ) {
+always.php.in:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+always.php.in:  if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
+always.php.in:    if ( isset($session->principal_id) ) {
+always.php.in:      $_known_users_name[$row->username]      = $row;
+always.php.in:      $_known_users_id[$row->user_no]         = $row;
+always.php.in:      $_known_users_pid[$row->principal_id]   = $row;
+always.php.in:* Return a user record identified by e-mail address, caching it for any subsequent lookup
+always.php.in:    /** We don't actually maintain a cache-access by e-mail, since it's rare */
+always.php.in:      if ( strtolower($email) == strtolower($v->email) ) return $v;
+always.php.in:    case 203:  $ans = 'Non-Authoritative Information';        break;
+always.php.in:    case 207:  $ans = 'Multi-Status';                         break;
+always.php.in:    case 414:  $ans = 'Request-URI Too Long';                 break;
+always.php.in:  if ( ! isset($c->_url_script_path) ) {
+always.php.in:    $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
+always.php.in:    $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
+always.php.in:  $url = $c->_url_script_path;
+always.php.in:  if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
+always.php.in:  if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
+always.php.in:    if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
+always.php.in:      case 'write-properties'                : $out_priv |=     2;  break;
+always.php.in:      case 'write-content'                   : $out_priv |=     4;  break;
+always.php.in:      case 'read-acl'                        : $out_priv |=    16;  break;
+always.php.in:      case 'read-current-user-privilege-set' : $out_priv |=    32;  break;
+always.php.in:      case 'write-acl'                       : $out_priv |=   256;  break;
+always.php.in:      case 'read-free-busy'                  : $out_priv |=   512;  break;
+always.php.in:      case 'schedule-deliver-invite'         : $out_priv |=  1024;  break;
+always.php.in:      case 'schedule-deliver-reply'          : $out_priv |=  2048;  break;
+always.php.in:      case 'schedule-query-freebusy'         : $out_priv |=  4096;  break;
+always.php.in:      case 'schedule-send-invite'            : $out_priv |=  8192;  break;
+always.php.in:      case 'schedule-send-reply'             : $out_priv |= 16384;  break;
+always.php.in:      case 'schedule-send-freebusy'          : $out_priv |= 32768;  break;
+always.php.in:      case 'schedule-deliver'                : $out_priv |=  7168;  break; // 1024 + 2048 + 4096
+always.php.in:      case 'schedule-send'                   : $out_priv |= 57344;  break; // 8192 + 16384 + 32768
+always.php.in:  if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
+always.php.in:  if ( ($raw_bits &  16) != 0 ) $out_priv[] = 'DAV::read-acl';
+always.php.in:  if ( ($raw_bits &  32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
+always.php.in:  if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
+always.php.in:  if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
+always.php.in:    if ( ($raw_bits &   2) != 0 ) $out_priv[] = 'DAV::write-properties';
+always.php.in:    if ( ($raw_bits &   4) != 0 ) $out_priv[] = 'DAV::write-content';
+always.php.in:  if ( $resourcetype == 'schedule-inbox' && ($raw_bits & 7168) != 0 ) {
+always.php.in:    if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
+always.php.in:    if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
+always.php.in:    if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
+always.php.in:    if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
+always.php.in:  if ( $resourcetype == 'schedule-outbox' && ($raw_bits & 57344) != 0 ) {
+always.php.in:    if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
+always.php.in:    if ( ($raw_bits &  8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
+always.php.in:    if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
+always.php.in:    if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
+always.php.in:      $xmldoc->NSElement($privilege,$k);
+always.php.in:      $privilege->NewElement($k);
+auth-functions.php:*   - Accept a username / password
+auth-functions.php:*   - Confirm the username / password are correct
+auth-functions.php:*   - Create (or update) a 'usr' record in our database
+auth-functions.php:*   - Return the 'usr' record as an object
+auth-functions.php:*   - Return === false when authentication fails
+auth-functions.php:*   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+auth-functions.php:*   - This file should be included
+auth-functions.php:*   - $c->authenticate_hook['call'] should be set to the name of the plugin
+auth-functions.php:*   - $c->authenticate_hook['config'] should be set up with any configuration data for the plugin
+auth-functions.php:  if ( ! isset($c->home_calendar_name) || strlen($c->home_calendar_name) == 0 ) return true;
+auth-functions.php:  //$calendar_path = $parent_path . $c->home_calendar_name."/";
+auth-functions.php:  $dav_etag = md5($usr->user_no . $calendar_path);
+auth-functions.php:      ':user_no' => $usr->user_no,
+auth-functions.php:      ':displayname' => $usr->fullname,
+auth-functions.php:  if ( $qry->Exec() ) {
+auth-functions.php:    $c->messages[] = i18n("Home calendar added.");
+auth-functions.php:    $c->messages[] = i18n("There was an error writing to the database.");
+auth-functions.php:  if ( !isset($usr->user_no) || intval($usr->user_no) == 0 ) {
+auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:    $sequence_value = $qry->Fetch(true);  // Fetch as an array
+auth-functions.php:    $usr->user_no = $sequence_value[0];
+auth-functions.php:  $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no) );
+auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+auth-functions.php:    if ( $old = $qry->Fetch() ) {
+auth-functions.php:        if ( $old->{$k} != $v ) {
+auth-functions.php:          dbg_error_log("Login","User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v );
+auth-functions.php:        dbg_error_log("Login","No changes to user record for '%s' - leaving as-is.", $usr->username );
+auth-functions.php:        if ( isset($usr->active) && $usr->active == 'f' ) return false;
+auth-functions.php:        dbg_error_log("Login","Changes to user record for '%s' - updating.", $usr->username );
+auth-functions.php:  if ( $type != 'INSERT' ) $params[':user_no'] = $usr->user_no;
+auth-functions.php:  $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:  if ( isset($usr->active) && ($usr->active === 'f' || $usr->active === false) ) return false;
+auth-functions.php:                          array( ':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username) );
+auth-functions.php:    $qry->Exec('Login',__LINE__,__FILE__);
+auth-functions.php:    CreateHomeCalendar($usr->username);
+auth-functions.php:* require_once('auth-functions.php');
+auth-functions.php:*  $c->authenticate_hook = array(
+auth-functions.php:  $persistent = isset($c->authenticate_hook['config']['use_persistent']) && $c->authenticate_hook['config']['use_persistent'];
+auth-functions.php:  if ( isset($c->authenticate_hook['config']['columns']) )
+auth-functions.php:    $cols = $c->authenticate_hook['config']['columns'];
+auth-functions.php:  if ( isset($c->authenticate_hook['config']['where']) )
+auth-functions.php:    $andwhere = ' AND '.$c->authenticate_hook['config']['where'];
+auth-functions.php:  $authconn = $qry->SetConnection($c->authenticate_hook['config']['connection'], ($persistent ? array(PDO::ATTR_PERSISTENT => true) : null));
+auth-functions.php:  if ( $qry->Exec('Login',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+auth-functions.php:    $usr = $qry->Fetch();
+auth-functions.php:    if ( session_validate_password( $password, $usr->password ) ) {
+auth-functions.php:      if ( isset($usr->active) && $usr->active == 'f' ) return false;
+auth-functions.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $usr->username) );
+auth-functions.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+auth-functions.php:        $principal = $qry->Fetch();
+caldav-ACL.php:* CalDAV Server - handle ACL method
+caldav-ACL.php:$request->NeedPrivilege('DAV::write-acl');
+caldav-ACL.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-ACL.php:    fwrite($fh,$request->raw_post);
+caldav-ACL.php:$resource = new DAVResource( $request->path );
+caldav-ACL.php:   (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
+caldav-ACL.php:   that an implementation-specific ACL restriction has been violated.
+caldav-ACL.php:   (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
+caldav-ACL.php:   (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
+caldav-ACL.php:   implementation-dependent.  Implementations MUST either report this
+caldav-ACL.php:   (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
+caldav-ACL.php:   However, ACL-compliant servers MUST support at least one ACE granting
+caldav-ACL.php:   (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
+caldav-ACL.php:   non-inherited grant ACEs.
+caldav-ACL.php:   (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
+caldav-ACL.php:   restrictions of the resource include the DAV:grant-only constraint
+caldav-ACL.php:   (DAV:no-invert):  The ACL request MUST NOT include a DAV:invert
+caldav-ACL.php:   the resource includes the DAV:no-invert constraint (defined in
+caldav-ACL.php:   (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
+caldav-ACL.php:   (DAV:not-supported-privilege): The ACEs submitted in the ACL request
+caldav-ACL.php:   (DAV:missing-required-principal): The result of the ACL request MUST
+caldav-ACL.php:   DAV:required-principal XML element in the ACL semantics of that
+caldav-ACL.php:   (DAV:recognized-principal): Every principal URL in the ACL request
+caldav-ACL.php:   (DAV:allowed-principal): The principals specified in the ACEs
+caldav-ACL.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-ACL.php:$aces = $xmltree->GetPath("/DAV::acl/*");
+caldav-ACL.php:$grantor = new DAVResource($request->path);
+caldav-ACL.php:if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
+caldav-ACL.php:if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id');
+caldav-ACL.php:else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id');
+caldav-ACL.php:else $request->PreconditionFailed(403,'not-supported-privilege','ACLs may only be applied to Principals or Collections');
+caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:  $elements = $ace->GetContent();
+caldav-ACL.php:  if ( $principal->GetTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetTag());
+caldav-ACL.php:  $grant_tag = $grant->GetTag();
+caldav-ACL.php:  if ( $grant_tag == 'DAV::deny' )   $request->PreconditionFailed(403,'grant-only');
+caldav-ACL.php:  if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
+caldav-ACL.php:  if ( $grant->GetTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
+caldav-ACL.php:  $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
+caldav-ACL.php:    $privilege_names[] = $priv->GetTag();
+caldav-ACL.php:  $principal_content = $principal->GetContent();
+caldav-ACL.php:  if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
+caldav-ACL.php:  switch( $principal_content->GetTag() ) {
+caldav-ACL.php:      $principal_property = $principal_content->GetContent();
+caldav-ACL.php:      if ( $principal_property[0]->GetTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
+caldav-ACL.php:        $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
+caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+caldav-ACL.php:      $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) );
+caldav-ACL.php:      if ( ! $principal->Exists() || !$principal->IsPrincipal() )
+caldav-ACL.php:        $request->PreconditionFailed(403,'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
+caldav-ACL.php:      $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_id') );
+caldav-ACL.php:      if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
+caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:      if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
+caldav-ACL.php:      $qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+caldav-ACL.php:      $request->PreconditionFailed(403, 'recognized-principal' );
+caldav-ACL.php:$qry->Exec('ACL',__LINE__,__FILE__);
+caldav-ACL.php:$request->DoResponse( 200 );
+caldav-BIND.php:* CalDAV Server - handle BIND method
+caldav-BIND.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-BIND.php:$request->NeedPrivilege('DAV::bind');
+caldav-BIND.php:if ( ! $request->IsCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-into-collection',translate('The BIND Request-URI MUST identify a collection.'));
+caldav-BIND.php:$parent_container = $request->path;
+caldav-BIND.php:if ( ! $parent->Exists() || $parent->IsSchedulingCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::method-not-allowed',translate('The BIND method is not allowed at that location.') );
+caldav-BIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-BIND.php:$segment = $xmltree->GetElements('DAV::segment');
+caldav-BIND.php:$segment = $segment[0]->GetContent();
+caldav-BIND.php:  $request->PreconditionFailed(403, 'DAV::name-allowed',translate('That destination name contains invalid characters.') );
+caldav-BIND.php:$href    = $xmltree->GetElements('DAV::href');
+caldav-BIND.php:$href    = $href[0]->GetContent();
+caldav-BIND.php:if ( $destination->Exists() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
+caldav-BIND.php:if ( !$source->Exists() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::bind-source-exists',translate('The BIND Request MUST identify an existing resource.'));
+caldav-BIND.php:if ( $source->IsPrincipal() || !$source->IsCollection() ) {
+caldav-BIND.php:  $request->PreconditionFailed(403,'DAV::binding-allowed',translate('DAViCal only allows BIND requests for collections at present.'));
+caldav-BIND.php:    ':target_id'    => $source->GetProperty('collection_id'),
+caldav-BIND.php:    ':ticket_id'    => (isset($request->ticket) ? $request->ticket->id() : null),
+caldav-BIND.php:    ':parent_container' => $parent->dav_name(),
+caldav-BIND.php:    ':session_principal' => $session->principal_id,
+caldav-BIND.php:    ':displayname'  => $source->GetProperty('displayname')
+caldav-BIND.php:if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
+caldav-BIND.php:  $request->DoResponse(201);
+caldav-BIND.php:  $request->DoResponse(500,translate('Database Error'));
+caldav-client.php:* removed curl - now using fsockopen
+caldav-client.php:* changed 2009 by Andres Obrero - Switzerland andres@obrero.ch
+caldav-client.php:    $this->user = $user;
+caldav-client.php:    $this->pass = $pass;
+caldav-client.php:    $this->calendar = $calendar;
+caldav-client.php:    $this->headers = array();
+caldav-client.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+caldav-client.php:      $this->server = $matches[2];
+caldav-client.php:      $this->base_url = $matches[5];
+caldav-client.php:        $this->protocol = 'ssl';
+caldav-client.php:        $this->port = 443;
+caldav-client.php:        $this->protocol = 'tcp';
+caldav-client.php:        $this->port = 80;
+caldav-client.php:        $this->port = intval($matches[4]);
+caldav-client.php:  * Adds an If-Match or If-None-Match header
+caldav-client.php:    $this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+caldav-client.php:    $this->headers[] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+caldav-client.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+caldav-client.php:    $this->user_agent = $user_agent;
+caldav-client.php:  * Add a Content-type: header.
+caldav-client.php:    $this->headers[] = "Content-type: $type";
+caldav-client.php:        $this->httpResponse = trim($response);
+caldav-client.php:        $this->httpResponse = trim(substr($response, 0, $pos));
+caldav-client.php:        $this->xmlResponse = trim(substr($response, $pos));
+caldav-client.php:      return $this->httpRequest;
+caldav-client.php:      return $this->httpResponse;
+caldav-client.php:      return $this->xmlRequest;
+caldav-client.php:      return $this->xmlResponse;
+caldav-client.php:    $headers[] = $this->requestMethod." ". $this->base_url . $relative_url . " HTTP/1.1";
+caldav-client.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+caldav-client.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+caldav-client.php:    foreach( $this->headers as $ii => $head ) {
+caldav-client.php:    $headers[] = "Content-Length: " . strlen($this->body);
+caldav-client.php:    $headers[] = "User-Agent: " . $this->user_agent;
+caldav-client.php:    $this->httpRequest = join("\r\n",$headers);
+caldav-client.php:    $this->xmlRequest = $this->body;
+caldav-client.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+caldav-client.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+caldav-client.php:    $this->headers = array();  // reset the headers array for our next request
+caldav-client.php:    $this->ParseResponse($rsp);
+caldav-client.php:    $this->requestMethod = "OPTIONS";
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $headers = $this->DoRequest($relative_url);
+caldav-client.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+caldav-client.php:    $this->body = $xml;
+caldav-client.php:    $this->requestMethod = $request_method;
+caldav-client.php:    $this->SetContentType("text/xml");
+caldav-client.php:    return $this->DoRequest($relative_url);
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $this->requestMethod = "GET";
+caldav-client.php:    return $this->DoRequest( $relative_url );
+caldav-client.php:    $this->body = $icalendar;
+caldav-client.php:    $this->requestMethod = "PUT";
+caldav-client.php:      $this->SetMatch( ($etag != '*'), $etag );
+caldav-client.php:    $this->SetContentType("text/icalendar");
+caldav-client.php:    $headers = $this->DoRequest($relative_url);
+caldav-client.php:    $this->body = "";
+caldav-client.php:    $this->requestMethod = "DELETE";
+caldav-client.php:      $this->SetMatch( true, $etag );
+caldav-client.php:    $this->DoRequest($relative_url);
+caldav-client.php:    return $this->resultcode;
+caldav-client.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+caldav-client.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+caldav-client.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client.php:    <C:calendar-data/>
+caldav-client.php:</C:calendar-query>
+caldav-client.php:    $this->DoXMLRequest( 'REPORT', $xml, $relative_url );
+caldav-client.php:    $xml_parser = xml_parser_create_ns('UTF-8');
+caldav-client.php:    $this->xml_tags = array();
+caldav-client.php:    xml_parse_into_struct( $xml_parser, $this->xmlResponse, $this->xml_tags );
+caldav-client.php:    foreach( $this->xml_tags as $k => $v ) {
+caldav-client.php:        case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
+caldav-client.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:      <C:comp-filter name="VEVENT">
+caldav-client.php:      </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:                <C:time-range start="$start" end="$finish"/>
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:          <C:comp-filter name="VTODO">
+caldav-client.php:                <C:prop-filter name="STATUS">
+caldav-client.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+caldav-client.php:                </C:prop-filter>
+caldav-client.php:                <C:prop-filter name="STATUS">
+caldav-client.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+caldav-client.php:                </C:prop-filter>$time_range
+caldav-client.php:          </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:    <C:comp-filter name="VCALENDAR">
+caldav-client.php:          <C:comp-filter name="VEVENT">
+caldav-client.php:                <C:prop-filter name="UID">
+caldav-client.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+caldav-client.php:                </C:prop-filter>
+caldav-client.php:          </C:comp-filter>
+caldav-client.php:    </C:comp-filter>
+caldav-client.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client.php:    return $this->DoGETRequest( $relative_url . $href );
+caldav-client.php:* $options = $cal->DoOptionsRequest();
+caldav-client.php:*   $cal->SetDepth(1);
+caldav-client.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+caldav-client.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+caldav-client.php:* $options = $cal->DoOptionsRequest();
+caldav-client.php:* <?xml version="1.0" encoding="utf-8" ?>
+caldav-client.php:* //  $cal->SetDepth(1);
+caldav-client.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+caldav-client.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+caldav-client.php:*     print "\n---------------------------------------------\n";
+caldav-client-v2.php:* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
+caldav-client-v2.php:    $this->url = $url;
+caldav-client-v2.php:    $this->displayname = $displayname;
+caldav-client-v2.php:    $this->getctag = $getctag;
+caldav-client-v2.php:    return( '(URL: '.$this->url.'   Ctag: '.$this->getctag.'   Displayname: '.$this->displayname .')'. "\n" );
+caldav-client-v2.php:  * The principal-URL we're using
+caldav-client-v2.php:  * The calendar-URL we're using
+caldav-client-v2.php:  * The calendar-home-set we're using
+caldav-client-v2.php:    $this->user = $user;
+caldav-client-v2.php:    $this->pass = $pass;
+caldav-client-v2.php:    $this->headers = array();
+caldav-client-v2.php:    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+caldav-client-v2.php:      $this->server = $matches[2];
+caldav-client-v2.php:      $this->base_url = $matches[5];
+caldav-client-v2.php:        $this->protocol = 'ssl';
+caldav-client-v2.php:        $this->port = 443;
+caldav-client-v2.php:        $this->protocol = 'tcp';
+caldav-client-v2.php:        $this->port = 80;
+caldav-client-v2.php:        $this->port = intval($matches[4]);
+caldav-client-v2.php:  * Adds an If-Match or If-None-Match header
+caldav-client-v2.php:    $this->headers['match'] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+caldav-client-v2.php:    $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+caldav-client-v2.php:    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+caldav-client-v2.php:    $this->user_agent = $user_agent;
+caldav-client-v2.php:  * Add a Content-type: header.
+caldav-client-v2.php:    $this->headers['content-type'] = "Content-type: $type";
+caldav-client-v2.php:    $this->calendar_url = $url;
+caldav-client-v2.php:      $this->httpResponse = trim($response);
+caldav-client-v2.php:      $this->httpResponse = trim(substr($response, 0, $pos));
+caldav-client-v2.php:      $this->xmlResponse = trim(substr($response, $pos));
+caldav-client-v2.php:      $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
+caldav-client-v2.php:      $parser = xml_parser_create_ns('UTF-8');
+caldav-client-v2.php:      if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
+caldav-client-v2.php:        printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
+caldav-client-v2.php://        echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes );
+caldav-client-v2.php://        echo "\nTags array............................................................\n";  print_r( $this->xmltags );
+caldav-client-v2.php:        printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
+caldav-client-v2.php:      return $this->httpRequest;
+caldav-client-v2.php:      return $this->httpResponseHeaders;
+caldav-client-v2.php:      return $this->httpResponseBody;
+caldav-client-v2.php:      return $this->xmlRequest;
+caldav-client-v2.php:      return $this->xmlResponse;
+caldav-client-v2.php:    if ( !isset($url) ) $url = $this->base_url;
+caldav-client-v2.php:    $this->request_url = $url;
+caldav-client-v2.php:    if ( preg_match( '{[^%?&=+,.-_/a-z0-9]}', $url ) ) {
+caldav-client-v2.php:    $headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
+caldav-client-v2.php:    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+caldav-client-v2.php:    $headers[] = "Host: ".$this->server .":".$this->port;
+caldav-client-v2.php:    if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
+caldav-client-v2.php:    foreach( $this->headers as $ii => $head ) {
+caldav-client-v2.php:    $headers[] = "Content-Length: " . strlen($this->body);
+caldav-client-v2.php:    $headers[] = "User-Agent: " . $this->user_agent;
+caldav-client-v2.php:    $this->httpRequest = join("\r\n",$headers);
+caldav-client-v2.php:    $this->xmlRequest = $this->body;
+caldav-client-v2.php:    $this->httpResponse = '';
+caldav-client-v2.php:    $this->xmlResponse = '';
+caldav-client-v2.php:    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+caldav-client-v2.php:    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+caldav-client-v2.php:    list( $this->httpResponseHeaders, $this->httpResponseBody ) = preg_split( '{\r?\n\r?\n}s', $response, 2 );
+caldav-client-v2.php:    if ( preg_match( '{Transfer-Encoding: chunked}i', $this->httpResponseHeaders ) ) $this->Unchunk();
+caldav-client-v2.php:    $this->headers = array();  // reset the headers array for our next request
+caldav-client-v2.php:    $this->ParseResponse($this->httpResponseBody);
+caldav-client-v2.php:    $chunks = $this->httpResponseBody;
+caldav-client-v2.php:      if ( preg_match('{^((\r\n)?\s*([ 0-9a-fA-F]+)(;[^\n]*)?\r?\n)}', $chunks, $matches ) ) {
+caldav-client-v2.php:          // printf( "---------------------------------\n%s\n---------------------------------\n", substr($chunks,$pos,$bytes) );
+caldav-client-v2.php:    $this->httpResponseBody = $content;
+caldav-client-v2.php:    $this->requestMethod = "OPTIONS";
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $headers = $this->DoRequest($url);
+caldav-client-v2.php:    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+caldav-client-v2.php:    $this->body = $xml;
+caldav-client-v2.php:    $this->requestMethod = $request_method;
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    return $this->DoRequest($url);
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "GET";
+caldav-client-v2.php:    return $this->DoRequest( $url );
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "HEAD";
+caldav-client-v2.php:    return $this->DoRequest( $url );
+caldav-client-v2.php:    $this->body = $icalendar;
+caldav-client-v2.php:    $this->requestMethod = "PUT";
+caldav-client-v2.php:      $this->SetMatch( ($etag != '*'), $etag );
+caldav-client-v2.php:    $this->SetContentType('text/calendar; encoding="utf-8"');
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+caldav-client-v2.php:      printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
+caldav-client-v2.php:      $save_request = $this->httpRequest;
+caldav-client-v2.php:      $save_response_headers = $this->httpResponseHeaders;
+caldav-client-v2.php:      $this->DoHEADRequest( $url );
+caldav-client-v2.php:      if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+caldav-client-v2.php:        printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
+caldav-client-v2.php:      $this->httpRequest = $save_request;
+caldav-client-v2.php:      $this->httpResponseHeaders = $save_response_headers;
+caldav-client-v2.php:    $this->body = "";
+caldav-client-v2.php:    $this->requestMethod = "DELETE";
+caldav-client-v2.php:      $this->SetMatch( true, $etag );
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    return $this->resultcode;
+caldav-client-v2.php:    $this->SetDepth($depth);
+caldav-client-v2.php:      $xml->NSElement($prop,$v);
+caldav-client-v2.php:    $this->body = $xml->Render('propfind',$prop );
+caldav-client-v2.php:    $this->requestMethod = "PROPFIND";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest($url);
+caldav-client-v2.php:    return $this->GetXmlResponse();
+caldav-client-v2.php:      $this->principal_url = $url;
+caldav-client-v2.php:    return $this->principal_url;
+caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+caldav-client-v2.php:  * @param $url array of string The calendar-home-set URLs to set
+caldav-client-v2.php:      $this->calendar_home_set = $urls;
+caldav-client-v2.php:    return $this->calendar_home_set;
+caldav-client-v2.php:  * Get/Set the calendar-home-set URL
+caldav-client-v2.php:      $this->calendar_urls = $urls;
+caldav-client-v2.php:    return $this->calendar_urls;
+caldav-client-v2.php:    foreach( $this->xmltags[$tagname] AS $k => $v ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
+caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
+caldav-client-v2.php://        printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+caldav-client-v2.php:        if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
+caldav-client-v2.php://      printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+caldav-client-v2.php:      if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+caldav-client-v2.php://        printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']);
+caldav-client-v2.php:        return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+caldav-client-v2.php:      $j = $this->xmltags[$tagname][$i];
+caldav-client-v2.php:      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' );
+caldav-client-v2.php:        while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
+caldav-client-v2.php:        if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+caldav-client-v2.php:          return rawurldecode($this->xmlnodes[$j]['value']);
+caldav-client-v2.php:    $level = $this->xmlnodes[$nodenum]['level'];
+caldav-client-v2.php:    while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) {
+caldav-client-v2.php:        if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) {
+caldav-client-v2.php:      elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) {
+caldav-client-v2.php:      elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) {
+caldav-client-v2.php:        $status = $this->xmlnodes[$nodenum]['value'];
+caldav-client-v2.php:        $props[] = $this->xmlnodes[$nodenum];
+caldav-client-v2.php:  * @param string $url The URL to find the principal-URL from
+caldav-client-v2.php:    $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL',
+caldav-client-v2.php:                                  'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
+caldav-client-v2.php:    $principal_url = $this->HrefForProp('DAV::principal');
+caldav-client-v2.php:      foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) {
+caldav-client-v2.php:          $principal_url = $this->HrefValueInside($href);
+caldav-client-v2.php:    return $this->PrincipalURL($principal_url);
+caldav-client-v2.php:  * @param string $url The URL to find the calendar-home-set from
+caldav-client-v2.php:    if ( !isset($this->principal_url) ) {
+caldav-client-v2.php:      $this->FindPrincipal();
+caldav-client-v2.php:      $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0);
+caldav-client-v2.php:    foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) {
+caldav-client-v2.php:      if ( $this->xmlnodes[$v]['type'] != 'open' ) continue;
+caldav-client-v2.php:      while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
+caldav-client-v2.php://        printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
+caldav-client-v2.php:        if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
+caldav-client-v2.php:          $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
+caldav-client-v2.php:      $calendar_home = $this->FindCalendarHome(true);
+caldav-client-v2.php:    return $this->CalendarHomeSet($calendar_home);
+caldav-client-v2.php:    if ( !isset($this->calendar_home_set[0]) ) {
+caldav-client-v2.php:      $this->FindCalendarHome();
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1);
+caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) {
+caldav-client-v2.php:        $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1;
+caldav-client-v2.php:      foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
+caldav-client-v2.php:        $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+caldav-client-v2.php:        $ok_props = $this->GetOKProps($hnode);
+caldav-client-v2.php:              $calendar->getctag = $v['value'];
+caldav-client-v2.php:              $calendar->displayname = $v['value'];
+caldav-client-v2.php:    return $this->CalendarUrls($calendars);
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
+caldav-client-v2.php:    $hnode = $this->xmltags['DAV::href'][0];
+caldav-client-v2.php:    $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+caldav-client-v2.php:    $ok_props = $this->GetOKProps($hnode);
+caldav-client-v2.php:        $calendar->{$name} = $v['value'];
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
+caldav-client-v2.php:    if ( isset($this->xmltags['DAV::getetag']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
+caldav-client-v2.php:        $href = $this->HrefForProp('DAV::getetag', $k);
+caldav-client-v2.php:        if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) $etags[$href] = $this->xmlnodes[$v]['value'];
+caldav-client-v2.php:  * Get a bunch of events for a calendar with a calendar-multiget report
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->body = <<<EOXML
+caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client-v2.php:<prop><getetag/><C:calendar-data/></prop>
+caldav-client-v2.php:</C:calendar-multiget>
+caldav-client-v2.php:    $this->requestMethod = "REPORT";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+caldav-client-v2.php:    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
+caldav-client-v2.php:      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
+caldav-client-v2.php:        $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
+caldav-client-v2.php://        echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]);
+caldav-client-v2.php:        $events[$href] = $this->xmlnodes[$v]['value'];
+caldav-client-v2.php:        $this->DoGETRequest($href);
+caldav-client-v2.php:        $events[$href] = $this->httpResponseBody;
+caldav-client-v2.php:  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+caldav-client-v2.php:  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+caldav-client-v2.php:    if ( isset($url) ) $this->SetCalendar($url);
+caldav-client-v2.php:    $this->body = <<<EOXML
+caldav-client-v2.php:<?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+caldav-client-v2.php:    <C:calendar-data/>
+caldav-client-v2.php:</C:calendar-query>
+caldav-client-v2.php:    $this->requestMethod = "REPORT";
+caldav-client-v2.php:    $this->SetContentType("text/xml");
+caldav-client-v2.php:    $this->DoRequest( $this->calendar_url );
+caldav-client-v2.php:    foreach( $this->xmltags as $k => $v ) {
+caldav-client-v2.php:        case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+caldav-client-v2.php:        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:      <C:comp-filter name="VEVENT">
+caldav-client-v2.php:      </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:                <C:time-range start="$start" end="$finish"/>
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:          <C:comp-filter name="VTODO">
+caldav-client-v2.php:                <C:prop-filter name="STATUS">
+caldav-client-v2.php:                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>
+caldav-client-v2.php:                <C:prop-filter name="STATUS">
+caldav-client-v2.php:                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>$time_range
+caldav-client-v2.php:          </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:    <C:comp-filter name="VCALENDAR">
+caldav-client-v2.php:          <C:comp-filter name="VEVENT">
+caldav-client-v2.php:                <C:prop-filter name="UID">
+caldav-client-v2.php:                        <C:text-match icollation="i;octet">$uid</C:text-match>
+caldav-client-v2.php:                </C:prop-filter>
+caldav-client-v2.php:          </C:comp-filter>
+caldav-client-v2.php:    </C:comp-filter>
+caldav-client-v2.php:    return $this->DoCalendarQuery($filter, $relative_url);
+caldav-client-v2.php:    return $this->DoGETRequest( $href );
+caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+caldav-client-v2.php:*   $cal->SetDepth(1);
+caldav-client-v2.php:*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+caldav-client-v2.php:* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+caldav-client-v2.php:* $options = $cal->DoOptionsRequest();
+caldav-client-v2.php:* <?xml version="1.0" encoding="utf-8" ?>
+caldav-client-v2.php:* //  $cal->SetDepth(1);
+caldav-client-v2.php:* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+caldav-client-v2.php:* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+caldav-client-v2.php:*     print "\n---------------------------------------------\n";
+caldav-DELETE.php:* CalDAV Server - handle DELETE method
+caldav-DELETE.php:$dav_resource = new DAVResource($request->path);
+caldav-DELETE.php:$container = $dav_resource->FetchParentContainer();
+caldav-DELETE.php:$container->NeedPrivilege('DAV::unbind');
+caldav-DELETE.php:$lock_opener = $request->FailIfLocked();
+caldav-DELETE.php:  if ( $qry->Exec('DELETE',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-DELETE.php:    while( $row = $qry->Fetch() ) {
+caldav-DELETE.php:      delete_collection($row->child_id);
+caldav-DELETE.php:  if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params ) ) {
+caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:if ( !$dav_resource->Exists() )$request->DoResponse( 404 );
+caldav-DELETE.php:if ( ! ( $dav_resource->resource_id() > 0 ) ) {
+caldav-DELETE.php:  $request->DoResponse( 403 );
+caldav-DELETE.php:$qry->Begin();
+caldav-DELETE.php:if ( $dav_resource->IsBinding() ) {
+caldav-DELETE.php:  $params = array( ':dav_name' => $dav_resource->dav_name() );
+caldav-DELETE.php:  if ( $qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params )
+caldav-DELETE.php:    && $qry->Commit() ) {
+caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:    $request->DoResponse( 204 );
+caldav-DELETE.php:else if ( $dav_resource->IsCollection() ) {
+caldav-DELETE.php:  if ( delete_collection( $dav_resource->resource_id() ) && $qry->Commit() ) {
+caldav-DELETE.php:    $request->DoResponse( 204 );
+caldav-DELETE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag() ) ) {
+caldav-DELETE.php:    $request->DoResponse( 412, translate("Resource has changed on server - not deleted") );
+caldav-DELETE.php:  $params = array( ':dav_id' => $dav_resource->resource_id() );
+caldav-DELETE.php:  if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE dav_id = :dav_id", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM property WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM locks WHERE dav_name = (SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id)", $params )
+caldav-DELETE.php:    && $qry->QDo("DELETE FROM caldav_data WHERE dav_id = :dav_id", $params )
+caldav-DELETE.php:    && $qry->Commit() ) {
+caldav-DELETE.php:    @dbg_error_log( "DELETE", "DELETE: User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+caldav-DELETE.php:      log_caldav_action( 'DELETE', $dav_resource->GetProperty('uid'), $dav_resource->GetProperty('user_no'), $dav_resource->GetProperty('collection_id'), $request->path );
+caldav-DELETE.php:    $request->DoResponse( 204 );
+caldav-DELETE.php:$request->DoResponse( 500 );
+caldav-DELTICKET.php:* CalDAV Server - handle DELTICKET method in line with defunct proposed RFC
+caldav-DELTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+caldav-DELTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-DELTICKET.php:if ( ! $request->HavePrivilegeTo('DAV::unbind') && $request->ticket->owner() != $session->principal_id ) {
+caldav-DELTICKET.php:  $request->NeedPrivilege('DAV::unbind');
+caldav-DELTICKET.php:if ( ! isset($request->ticket) ) {
+caldav-DELTICKET.php:    $r = new DAVResource($request->path);
+caldav-DELTICKET.php:    if ( ! $r->Exists() ) {
+caldav-DELTICKET.php:      $request->PreconditionFailed(404,'not-found');
+caldav-DELTICKET.php:      $request->PreconditionFailed(412,'ticket-does-not-exist','The specified ticket does not exist');
+caldav-DELTICKET.php:    $request->MalformedRequest('No ticket specified');
+caldav-DELTICKET.php:$qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=:ticket_id', array( ':ticket_id'   => $request->ticket->id() ) );
+caldav-DELTICKET.php:if ( $qry->Exec('DELTICKET', __LINE__, __FILE__) ) {
+caldav-DELTICKET.php:  $request->DoResponse( 204 );
+caldav-DELTICKET.php:$request->DoResponse( 500 );
+caldav-GET.php:* CalDAV Server - handle GET method
+caldav-GET.php:$dav_resource = new DAVResource($request->path);
+caldav-GET.php:$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
+caldav-GET.php:if ( ! $dav_resource->Exists() ) {
+caldav-GET.php:  $request->DoResponse( 404, translate("Resource Not Found.") );
+caldav-GET.php:  // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
+caldav-GET.php:  $confidential->SetType($icalendar->GetType());
+caldav-GET.php:  $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-GET.php:  $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTSTART'), 'DTSTART' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('RRULE'), 'RRULE' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DURATION'), 'DURATION' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('DTEND'), 'DTEND' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('UID'), 'UID' );
+caldav-GET.php:  $confidential->SetProperties( $icalendar->GetProperties('CREATED'), 'CREATED' );
+caldav-GET.php:if ( $dav_resource->IsCollection() ) {
+caldav-GET.php:  if ( ! $dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections) ) {
+caldav-GET.php:    $request->DoResponse( 405, translate("GET requests on collections are only supported for calendars.") );
+caldav-GET.php:  if ( isset($c->get_includes_subcollections) && $c->get_includes_subcollections ) {
+caldav-GET.php:    $params = array( ':path_match' => '^'.$request->path );
+caldav-GET.php:    $params = array( ':collection_id' => $dav_resource->resource_id() );
+caldav-GET.php:  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+caldav-GET.php:  if ( !$qry->Exec("GET",__LINE__,__FILE__) ) {
+caldav-GET.php:    $request->DoResponse( 500, translate("Database Error") );
+caldav-GET.php:  $vcal->VCalendar();
+caldav-GET.php:  $displayname = $dav_resource->GetProperty('displayname');
+caldav-GET.php:    $vcal->AddProperty("X-WR-CALNAME", $displayname);
+caldav-GET.php:  while( $event = $qry->Fetch() ) {
+caldav-GET.php:    $ical = new iCalComponent( $event->caldav_data );
+caldav-GET.php:    $event_zones = $ical->GetComponents('VTIMEZONE',true);
+caldav-GET.php:      $tzid = $tz->GetPValue('TZID');
+caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DTSTART', 'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DUE',     'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      $tzid = $comp->GetPParamValue('DTEND',   'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+caldav-GET.php:      if ( $dav_resource->HavePrivilegeTo('all',false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user
+caldav-GET.php:            || ( $c->allow_get_email_visibility && $comp->IsAttendee($session->email) ) ) {
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:      if ( $event->class == 'PRIVATE' ) continue;
+caldav-GET.php:      if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL' ) {
+caldav-GET.php:       $vcal->AddComponent(obfuscated_event($comp));
+caldav-GET.php:      elseif ( isset($c->hide_alarm) && $c->hide_alarm ) {
+caldav-GET.php:        $comp->ClearComponents('VALARM');
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:        $vcal->AddComponent($comp);
+caldav-GET.php:    if ( isset($timezones[$tzid]) ) $vcal->AddComponent($timezones[$tzid]);
+caldav-GET.php:  $response = $vcal->Render();
+caldav-GET.php:  header( 'Content-Length: '.strlen($response) );
+caldav-GET.php:  header( 'Etag: '.$dav_resource->unique_tag() );
+caldav-GET.php:  $request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $response), 'text/calendar; charset="utf-8"' );
+caldav-GET.php:$resource = $dav_resource->resource();
+caldav-GET.php:$ic = new iCalComponent( $resource->caldav_data );
+caldav-GET.php:if ( $dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $resource->user_no || $session->user_no == $resource->logged_user
+caldav-GET.php:      || ( $c->allow_get_email_visibility && $ic->IsAttendee($session->email) ) ) {
+caldav-GET.php:else if ( $resource->class != 'PRIVATE' ) {
+caldav-GET.php:  if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || ( $resource->class == 'CONFIDENTIAL' && ! $request->HavePrivilegeTo('DAV::write-content') ) ) {
+caldav-GET.php:    $ical = new iCalComponent( $resource->caldav_data );
+caldav-GET.php:    $comps = $ical->GetComponents('VTIMEZONE',false);
+caldav-GET.php:    $ical->SetComponents( array($confidential), $resource->caldav_type );
+caldav-GET.php:    $resource->caldav_data = $ical->Render();
+caldav-GET.php:// else $resource->class == 'PRIVATE' and this person may not see it.
+caldav-GET.php:  $request->DoResponse( 403, translate("Forbidden") );
+caldav-GET.php:header( 'Etag: "'.$resource->dav_etag.'"' );
+caldav-GET.php:header( 'Content-Length: '.strlen($resource->caldav_data) );
+caldav-GET.php:switch( $resource->caldav_type ) {
+caldav-GET.php:    $contenttype = 'text/x-vcard';
+caldav-GET.php:$request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $resource->caldav_data), $contenttype.'; charset="utf-8"' );
+caldav-LOCK.php:if ( ! $request->AllowedTo('write') ) {
+caldav-LOCK.php:  $request->NeedPrivilege( 'write', $request->path );
+caldav-LOCK.php:if ( ! isset($request->xml_tags) ) {
+caldav-LOCK.php:  if ( isset($request->lock_token) ) {
+caldav-LOCK.php:    $request->xml_tags = array();
+caldav-LOCK.php:    $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-LOCK.php:foreach( $request->xml_tags AS $k => $v ) {
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+caldav-LOCK.php:$request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff.
+caldav-LOCK.php:$lock_opener = $request->FailIfLocked();
+caldav-LOCK.php:if ( $request->method == "LOCK" ) {
+caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to lock resource '%s'", $request->path);
+caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+caldav-LOCK.php:        ':dav_name'      => $request->path,
+caldav-LOCK.php:        ':request_depth' => $request->depth,
+caldav-LOCK.php:        ':timeout'       => $request->timeout.' seconds'
+caldav-LOCK.php:    header( "Lock-Token: <opaquelocktoken:$lock_token>" );
+caldav-LOCK.php:  $qry->Exec("LOCK",__LINE__,__FILE__);
+caldav-LOCK.php:  $lock_row = $request->GetLockRow($lock_token);
+caldav-LOCK.php:      new XMLElement( 'locktype',  new XMLElement( $lock_row->type )),
+caldav-LOCK.php:      new XMLElement( 'lockscope', new XMLElement( $lock_row->scope )),
+caldav-LOCK.php:      new XMLElement( 'depth',     $request->GetDepthName() ),
+caldav-LOCK.php:      new XMLElement( 'owner',     new XMLElement( 'href', $lock_row->owner )),
+caldav-LOCK.php:      new XMLElement( 'timeout',   'Second-'.$request->timeout),
+caldav-LOCK.php:elseif (  $request->method == "UNLOCK" ) {
+caldav-LOCK.php:  * @TODO: respond with preconditionfailed(409,'lock-token-matches-request-uri') if
+caldav-LOCK.php:  dbg_error_log( "LOCK", "Attempting to unlock resource '%s'", $request->path);
+caldav-LOCK.php:  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+caldav-LOCK.php:    $qry->Exec("LOCK",__LINE__,__FILE__);
+caldav-LOCK.php:  $request->DoResponse( 204 );
+caldav-LOCK.php:$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+caldav-LOCK.php:$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-MKCOL.php:* CalDAV Server - handle MKCOL and MKCALENDAR method
+caldav-MKCOL.php:* @copyright Catalyst IT Ltd, Morphoss Ltd - http://www.morphoss.com/
+caldav-MKCOL.php:$request->NeedPrivilege('DAV::bind');
+caldav-MKCOL.php:$displayname = $request->path;
+caldav-MKCOL.php:if ( ! preg_match( '#/$#', $request->path ) ) {
+caldav-MKCOL.php:  dbg_error_log( 'MKCOL', 'Add trailling "/" to "%s"', $request->path);
+caldav-MKCOL.php:  $request->path .= '/';
+caldav-MKCOL.php:if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
+caldav-MKCOL.php:if ( $parent->IsSchedulingCollection( 'inbox' ) ) {
+caldav-MKCOL.php:  $request->PreconditionFailed(403, 'urn:ietf:params:xml:ns:caldav:no-mkcol-in-inbox' );
+caldav-MKCOL.php:$request_type = $request->method;
+caldav-MKCOL.php:if ( isset($request->xml_tags) ) {
+caldav-MKCOL.php:  $xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-MKCOL.php:  if ( $xmltree->GetTag() == 'DAV::mkcol' ) $request_type = 'extended-mkcol';
+caldav-MKCOL.php:  if ( $xmltree->GetTag() != 'urn:ietf:params:xml:ns:caldav:mkcalendar' && $request_type != 'extended-mkcol' ) {
+caldav-MKCOL.php:    $request->DoResponse( 406, sprintf('The XML is not a "DAV::mkcol" or "urn:ietf:params:xml:ns:caldav:mkcalendar" document (%s)', $xmltree->GetTag()) );
+caldav-MKCOL.php:  $setprops = $xmltree->GetContent();   // <set>
+caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // <prop>
+caldav-MKCOL.php:  $setprops = $setprops[0]->GetContent();  // the array of properties.
+caldav-MKCOL.php:    $tag = $setting->GetTag();
+caldav-MKCOL.php:    $content = $setting->RenderContent();
+caldav-MKCOL.php:        $is_addressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+caldav-MKCOL.php:        $is_calendar    = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+caldav-MKCOL.php:          $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-MKCOL.php:              new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+caldav-MKCOL.php:          $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+caldav-MKCOL.php:          dbg_error_log( 'MKCOL', 'Displayname is "/" to "%s"', $request->path);
+caldav-MKCOL.php:          $parent_container = $request->path;
+caldav-MKCOL.php:          $request->path .= $content . '/';
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':  /** Ignored, since we will support all component types */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-resource-size':  /** Ignored, since we will support arbitrary size */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:min-date-time':  /** Ignored, since we will support arbitrary time */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-date-time':  /** Ignored, since we will support arbitrary time */
+caldav-MKCOL.php:      case 'urn:ietf:params:xml:ns:caldav:max-instances':  /** Ignored, since we will support arbitrary instances */
+caldav-MKCOL.php:      * The following properties are read-only, so they will cause the request to fail
+caldav-MKCOL.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-MKCOL.php:            new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+caldav-MKCOL.php:            new XMLElement('responsedescription', translate('Property is read-only') )
+caldav-MKCOL.php:      $props[] = new XMLElement($reply->Tag($tag));
+caldav-MKCOL.php:    if ( $request_type == 'extended-mkcol' ) {
+caldav-MKCOL.php:      $request->DoResponse( $failure_code, $reply->Render('mkcol-response', array_merge( $status, $failure ), 'text/xml; charset="utf-8"' ) );
+caldav-MKCOL.php:      array_unshift( $failure, $reply->href( ConstructURL($request->path) ) );
+caldav-MKCOL.php:      $request->DoResponse( 207, $reply->Render('multistatus', new XMLElement( 'response', $failure )), 'text/xml; charset="utf-8"' );
+caldav-MKCOL.php:$qry = new AwlQuery( $sql, array( ':dav_name' => $request->path) );
+caldav-MKCOL.php:if ( ! $qry->Exec('MKCOL',__LINE__,__FILE__) ) {
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error querying database.') );
+caldav-MKCOL.php:if ( $qry->rows() != 0 ) {
+caldav-MKCOL.php:  $request->DoResponse( 405, translate('A collection already exists at that location.') );
+caldav-MKCOL.php:$qry->Begin();
+caldav-MKCOL.php:if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
+caldav-MKCOL.php:              ':user_no'          => $request->user_no,
+caldav-MKCOL.php:              ':dav_name'         => $request->path,
+caldav-MKCOL.php:              ':dav_etag'         => md5($request->user_no. $request->path),
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+caldav-MKCOL.php:  if ( ! $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text )',
+caldav-MKCOL.php:             array( ':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v) ) ) {
+caldav-MKCOL.php:    $request->DoResponse( 500, translate('Error writing calendar properties to database.') );
+caldav-MKCOL.php:if ( !$qry->Commit() ) {
+caldav-MKCOL.php:  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+caldav-MKCOL.php:dbg_error_log( 'MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
+caldav-MKCOL.php:header('Cache-Control: no-cache'); /** RFC4791 mandates this at 5.3.1 */
+caldav-MKCOL.php:$request->DoResponse( 201, '' );
+caldav-MKCOL.php:*   <?xml version="1.0" encoding="utf-8" ?>
+caldav-MKCOL.php:*         <C:calendar-description xml:lang="en">Calendar restricted to events.</C:calendar-description>
+caldav-MKCOL.php:*         <C:supported-calendar-component-set>
+caldav-MKCOL.php:*         </C:supported-calendar-component-set>
+caldav-MKCOL.php:*         <C:calendar-timezone><![CDATA[BEGIN:VCALENDAR
+caldav-MKCOL.php:*   PRODID:-//Example Corp.//CalDAV Client//EN
+caldav-MKCOL.php:*   TZID:US-Eastern
+caldav-MKCOL.php:*   LAST-MODIFIED:19870101T000000Z
+caldav-MKCOL.php:*   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+caldav-MKCOL.php:*   TZOFFSETFROM:-0400
+caldav-MKCOL.php:*   TZOFFSETTO:-0500
+caldav-MKCOL.php:*   TZOFFSETFROM:-0500
+caldav-MKCOL.php:*   TZOFFSETTO:-0400
+caldav-MKCOL.php:*   ]]></C:calendar-timezone>
+caldav-MKTICKET.php:* CalDAV Server - handle MKTICKET method in line with defunct proposed RFC
+caldav-MKTICKET.php:*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+caldav-MKTICKET.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-MKTICKET.php:$request->NeedPrivilege('DAV::bind');
+caldav-MKTICKET.php:$target = new DAVResource( $request->path );
+caldav-MKTICKET.php:if ( ! $target->Exists() ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 404, new XMLElement( 'error', new XMLElement('resource-must-not-be-null'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:if ( ! isset($request->xml_tags) ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-MKTICKET.php:if ( $xmltree->GetTag() != 'http://www.xythos.com/namespaces/StorageServer:ticketinfo' &&
+caldav-MKTICKET.php:     $xmltree->GetTag() != 'DAV::ticketinfo' ) {
+caldav-MKTICKET.php:  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml-for-request'), $reply->GetXmlNsArray() ) );
+caldav-MKTICKET.php:$ticket_timeout = 'Seconds-3600';
+caldav-MKTICKET.php:$ticket_privs_array = array('read-free-busy');
+caldav-MKTICKET.php:foreach( $xmltree->GetContent() AS $k => $v ) {
+caldav-MKTICKET.php:  switch( $v->GetTag() ) {
+caldav-MKTICKET.php:      $ticket_timeout = $v->GetContent();
+caldav-MKTICKET.php:      $ticket_privs_array = $v->GetElements(); // Ensure we always get an array back
+caldav-MKTICKET.php:        $ticket_privileges |= privilege_to_bits( $v1->GetTag() );
+caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read') )           $ticket_privileges |= privilege_to_bits( array('read-free-busy', 'read-current-user-privilege-set') );
+caldav-MKTICKET.php:      if ( $ticket_privileges & privilege_to_bits('read-free-busy') ) $ticket_privileges |= privilege_to_bits( 'schedule-query-freebusy');
+caldav-MKTICKET.php:else if ( preg_match( '{^([a-z]+)-(\d+)$}i', $ticket_timeout, $matches ) ) {
+caldav-MKTICKET.php:  /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */
+caldav-MKTICKET.php:$collection_id = $target->GetProperty('collection_id');
+caldav-MKTICKET.php:$resource_id   = $target->GetProperty('dav_id');
+caldav-MKTICKET.php:      ':owner'       => $session->principal_id,
+caldav-MKTICKET.php:  $result = $qry->Exec('MKTICKET', __LINE__, __FILE__);
+caldav-MKTICKET.php:  $reply->NSElement($privs, $v);
+caldav-MKTICKET.php:      new XMLElement( 'owner', $reply->href( ConstructURL('/'.$session->username.'/') ) ),
+caldav-MKTICKET.php:$prop = new XMLElement( "prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray() );
+caldav-MKTICKET.php:$request->XMLResponse( 200, $prop );
+caldav-MOVE.php:* CalDAV Server - handle MOVE method
+caldav-MOVE.php:$request->NeedPrivilege('DAV::unbind');
+caldav-MOVE.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['move']) && $c->dbg['move'])) ) {
+caldav-MOVE.php:    fwrite($fh,$request->raw_post);
+caldav-MOVE.php:$lock_opener = $request->FailIfLocked();
+caldav-MOVE.php:$dest = new DAVResource($request->destination);
+caldav-MOVE.php:if ( $dest->dav_name() == '/' || $dest->IsPrincipal() ) {
+caldav-MOVE.php:  $dest->NeedPrivilege('DAV::bind');
+caldav-MOVE.php:if ( ! $dest->ContainerExists() ) {
+caldav-MOVE.php:  $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-MOVE.php:if ( ! $request->overwrite && $dest->Exists() ) {
+caldav-MOVE.php:  $request->DoResponse( 412, translate('Not overwriting existing destination resource') );
+caldav-MOVE.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' ) {
+caldav-MOVE.php:  $request->DoResponse( 412 );  /** request to move, but only if there is no source? WTF! */
+caldav-MOVE.php:$src  = new DAVResource($request->path);
+caldav-MOVE.php:if ( ! $src->Exists() ) {
+caldav-MOVE.php:  $request->DoResponse( 412, translate('Source resource does not exist.') );
+caldav-MOVE.php:if ( $src->IsCollection() ) {
+caldav-MOVE.php:  switch( $dest->ContainerType() ) {
+caldav-MOVE.php:    case 'schedule-inbox':
+caldav-MOVE.php:    case 'schedule-outbox':
+caldav-MOVE.php:      $request->DoResponse( 412, translate('Special collections may not contain a calendar or other special collection.') );
+caldav-MOVE.php:  if ( (isset($request->etag_if_match) && $request->etag_if_match != '' )
+caldav-MOVE.php:        || ( isset($request->etag_none_match) && $request->etag_none_match != '') ) {
+caldav-MOVE.php:    * (without the If-None-Match header) on that resource, or if "*" is
+caldav-MOVE.php:    if ( isset($request->etag_if_match) && $request->etag_if_match != $src->unique_tag() ) {
+caldav-MOVE.php:      $error = translate( 'Existing resource does not match "If-Match" header - not accepted.');
+caldav-MOVE.php:    else if ( isset($request->etag_none_match) && $request->etag_none_match != '' && $request->etag_none_match == $src->unique_tag() ) {
+caldav-MOVE.php:      $error = translate( 'Existing resource matches "If-None-Match" header - not accepted.');
+caldav-MOVE.php:    if ( $error != '' ) $request->DoResponse( 412, $error );
+caldav-MOVE.php:$src->NeedPrivilege('DAV::unbind');
+caldav-MOVE.php:$dest->NeedPrivilege('DAV::write-content');
+caldav-MOVE.php:if ( ! $dest->Exists() ) $dest->NeedPrivilege('DAV::bind');
+caldav-MOVE.php:  $qry->Exec('move'); // Just in case
+caldav-MOVE.php:  $request->DoResponse( $response_code );
+caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:$src_name = $src->dav_name();
+caldav-MOVE.php:$dst_name = $dest->dav_name();
+caldav-MOVE.php:$src_collection = $src->GetProperty('collection_id');
+caldav-MOVE.php:$dst_collection = $dest->GetProperty('collection_id');
+caldav-MOVE.php:$src_user_no = $src->GetProperty('user_no');
+caldav-MOVE.php:$dst_user_no = $dest->GetProperty('user_no');
+caldav-MOVE.php:if ( $src->IsCollection()  ) {
+caldav-MOVE.php:  if ( $dest->Exists() ) {
+caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( $dest->Exists() ) {
+caldav-MOVE.php:    if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:    log_caldav_action( 'DELETE', $src->GetProperty('uid'), $src_user_no, $src_collection, $src_name );
+caldav-MOVE.php:    ':sync_type' => ( $dest->Exists() ? 200 : 201 )
+caldav-MOVE.php:  if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:    log_caldav_action( ( $dest->Exists() ? 'UPDATE' : 'INSERT' ), $src->GetProperty('uid'), $dst_user_no, $dst_collection, $dst_name );
+caldav-MOVE.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-MOVE.php:$request->DoResponse( 200 );
+caldav-OPTIONS.php:* CalDAV Server - handle OPTIONS method
+caldav-OPTIONS.php:$resource = new DAVResource($request->path);
+caldav-OPTIONS.php://$resource->NeedPrivilege( 'DAV::read', true );
+caldav-OPTIONS.php:if ( !$resource->Exists() ) {
+caldav-OPTIONS.php:  $request->DoResponse( 404, translate("No collection found at that location.") );
+caldav-OPTIONS.php:$allowed = implode( ', ', array_keys($resource->FetchSupportedMethods()) );
+caldav-OPTIONS.php:$request->DoResponse( 200, "" );
+caldav-POST.php:* CalDAV Server - handle PUT method
+caldav-POST.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-POST.php:include_once('caldav-PUT-functions.php');
+caldav-POST.php:include_once('freebusy-functions.php');
+caldav-POST.php:if ( ! $request->AllowedTo("CALDAV:schedule-send-freebusy")
+caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-invite")
+caldav-POST.php:  && ! $request->AllowedTo("CALDAV:schedule-send-reply") ) {
+caldav-POST.php:  // $request->DoResponse(403);
+caldav-POST.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['post'])) ) {
+caldav-POST.php:    fwrite($fh,$request->raw_post);
+caldav-POST.php:  $fbq_start = $ic->GetPValue('DTSTART');
+caldav-POST.php:  $fbq_end   = $ic->GetPValue('DTEND');
+caldav-POST.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
+caldav-POST.php:  $attendees = $ic->GetProperties('ATTENDEE');
+caldav-POST.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-POST.php:    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-POST.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-POST.php:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email );
+caldav-POST.php:    if ( !$qry->Exec('POST',__LINE__,__FILE__) ) $request->DoResponse( 501, 'Database error');
+caldav-POST.php:    if ( $qry->rows() > 1 ) {
+caldav-POST.php:      if ( !$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params ) )
+caldav-POST.php:        $request->DoResponse( 501, 'Database error');
+caldav-POST.php:      if ( $qry->rows() == 0 ) {
+caldav-POST.php:        /** Sigh... Go back to the original case-insensitive match */
+caldav-POST.php:        $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
+caldav-POST.php:    $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
+caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+caldav-POST.php:    if ( $qry->rows() == 0 ) {
+caldav-POST.php:      $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
+caldav-POST.php:      $reply->CalDAVElement($response, "calendar-data" );
+caldav-POST.php:    if ( ! $attendee_usr = $qry->Fetch() ) $request->DoResponse( 501, 'Database error');
+caldav-POST.php:    if ( (privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0 ) {
+caldav-POST.php:      $reply->CalDAVElement($response, "request-status", "3.8;No authority" );
+caldav-POST.php:      $reply->CalDAVElement($response, "calendar-data" );
+caldav-POST.php:    $attendee_path_match = '^/'.$attendee_usr->username.'/';
+caldav-POST.php:    $fb = get_freebusy( $attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p) );
+caldav-POST.php:    $fb->AddProperty( 'UID',       $ic->GetPValue('UID') );
+caldav-POST.php:    $fb->SetProperties( $ic->GetProperties('ORGANIZER'), 'ORGANIZER');
+caldav-POST.php:    $fb->AddProperty( $attendee );
+caldav-POST.php:    $vcal->VCalendar( array('METHOD' => 'REPLY') );
+caldav-POST.php:    $vcal->AddComponent( $fb );
+caldav-POST.php:    $response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:    $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
+caldav-POST.php:    $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+caldav-POST.php:    $reply->CalDAVElement($response, "calendar-data", $vcal->Render() );
+caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:  $request->XMLResponse( 200, $response );
+caldav-POST.php:  $responses[] = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:caldav' );
+caldav-POST.php:  $reply->CalDAVElement($response, "request-status", "2.0;Success" );  // Cargo-cult setting
+caldav-POST.php:  $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray() );
+caldav-POST.php:  $request->XMLResponse( 200, $response );
+caldav-POST.php:$ical = new iCalComponent( $request->raw_post );
+caldav-POST.php:$method =  $ical->GetPValue('METHOD');
+caldav-POST.php:$resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REQUEST" method with "%s" component.', $method, $first->GetType() );
+caldav-POST.php:    if ( $first->GetType() == 'VFREEBUSY' )
+caldav-POST.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-POST.php:      dbg_error_log('POST', 'Ignoring iTIP "REQUEST" with "%s" component.', $first->GetType() );
+caldav-POST.php:    dbg_error_log('POST', 'Handling iTIP "REPLY" with "%s" component.', $first->GetType() );
+CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+CalDAVPrincipal.php:  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+CalDAVPrincipal.php:  * @var Whether or not we are using an e-mail address based URL.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read permissions on.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which this one has read-write prmissions for.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have read permissions on this one.
+CalDAVPrincipal.php:   * @var caldav-cu-proxy-02: The principals which have write permissions on this one.
+CalDAVPrincipal.php:   * @var The dav_name for this principal - a partial path
+CalDAVPrincipal.php:    $this->exists = null;
+CalDAVPrincipal.php:    $this->url = null;
+CalDAVPrincipal.php:    $this->by_email = false;
+CalDAVPrincipal.php:      dbg_error_log( 'principal', 'Principal: record for %s', $parameters->username );
+CalDAVPrincipal.php:      $this->user_no = $parameters['user_no'];
+CalDAVPrincipal.php:        $this->username = $parameters['username'];
+CalDAVPrincipal.php:        $this->user_no = $parameters['user_no'];
+CalDAVPrincipal.php:        if ( $username = $this->UsernameFromEMail($parameters['email']) ) {
+CalDAVPrincipal.php:          $this->username = $username;
+CalDAVPrincipal.php:        if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
+CalDAVPrincipal.php:          $this->username = $username;
+CalDAVPrincipal.php:      else if ( isset($parameters['principal-property-search']) ) {
+CalDAVPrincipal.php:        $usr = $this->PropertySearch($parameters['principal-property-search']);
+CalDAVPrincipal.php:      $this->exists = false;
+CalDAVPrincipal.php:    $this->exists = true;
+CalDAVPrincipal.php:    $this->InitialiseRecord($usr);
+CalDAVPrincipal.php:      $this->url = $parameters['path'];
+CalDAVPrincipal.php:      $this->dav_name = $parameters['path'];
+CalDAVPrincipal.php:      $this->{$k} = $v;
+CalDAVPrincipal.php:    if ( !isset($this->modified) ) $this->modified = $this->updated;
+CalDAVPrincipal.php:    if ( !isset($this->created) )  $this->created  = $this->joined;
+CalDAVPrincipal.php:    $this->dav_etag = md5($this->username . $this->updated);
+CalDAVPrincipal.php:    $this->_is_group = (isset($usr->type_id) && $usr->type_id == 3);
+CalDAVPrincipal.php:    $this->principal_url = ConstructURL( '/'.$this->username.'/', true );
+CalDAVPrincipal.php:    $this->url = $this->principal_url;
+CalDAVPrincipal.php:    $this->principal_address = $this->principal_url . 'principal.vcf';
+CalDAVPrincipal.php:    $this->user_address_set = array(
+CalDAVPrincipal.php:       'mailto:'.$this->email,
+CalDAVPrincipal.php:       ConstructURL( '/'.$this->username.'/', true ),
+CalDAVPrincipal.php://       ConstructURL( '/~'.$this->username.'/', true ),
+CalDAVPrincipal.php://       ConstructURL( '/__uuids__/'.$this->username.'/', true ),
+CalDAVPrincipal.php:    $this->schedule_inbox_url = sprintf( '%s.in/', $this->url);
+CalDAVPrincipal.php:    $this->schedule_outbox_url = sprintf( '%s.out/', $this->url);
+CalDAVPrincipal.php:    $this->dropbox_url = sprintf( '%s.drop/', $this->url);
+CalDAVPrincipal.php:    $this->notifications_url = sprintf( '%s.notify/', $this->url);
+CalDAVPrincipal.php:    if ( isset ( $c->notifications_server ) ) {
+CalDAVPrincipal.php:      $this->xmpp_uri = 'xmpp:pubsub.'.$c->notifications_server['host'].'?pubsub;node=/davical-'.$this->principal_id;
+CalDAVPrincipal.php:      $this->xmpp_server = $c->notifications_server['host'];
+CalDAVPrincipal.php:    if ( $this->_is_group ) {
+CalDAVPrincipal.php:      $this->group_member_set = array();
+CalDAVPrincipal.php:      $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=member_id) JOIN usr USING(user_no) WHERE group_id = :group_id ORDER BY principal.principal_id ', array( ':group_id' => $this->principal_id) );
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $member = $qry->Fetch() ) {
+CalDAVPrincipal.php:          $this->group_member_set[] = ConstructURL( '/'. $member->username . '/', true);
+CalDAVPrincipal.php:    $this->group_membership = array();
+CalDAVPrincipal.php:    $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=group_id) JOIN usr USING(user_no) WHERE member_id = :member_id UNION SELECT usr.username FROM group_member LEFT JOIN grants ON (to_principal=group_id) JOIN principal ON (principal_id=by_principal) JOIN usr USING(user_no) WHERE member_id = :member_id and by_principal != member_id ORDER BY 1', array( ':member_id' => $this->principal_id ) );
+CalDAVPrincipal.php:    if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:      while( $group = $qry->Fetch() ) {
+CalDAVPrincipal.php:        $this->group_membership[] = ConstructURL( '/'. $group->username . '/', true);
+CalDAVPrincipal.php:    $this->read_proxy_group = null;
+CalDAVPrincipal.php:    $this->write_proxy_group = null;
+CalDAVPrincipal.php:    $this->write_proxy_for = null;
+CalDAVPrincipal.php:    $this->read_proxy_for = null;
+CalDAVPrincipal.php:    dbg_error_log( 'principal', ' User: %s (%d) URL: %s, Home: %s, By Email: %d', $this->username, $this->user_no, $this->url, $this->by_email );
+CalDAVPrincipal.php:    $this->read_proxy_group = array();
+CalDAVPrincipal.php:    $this->write_proxy_group = array();
+CalDAVPrincipal.php:    $this->write_proxy_for = array();
+CalDAVPrincipal.php:    $this->read_proxy_for = array();
+CalDAVPrincipal.php:    if ( !isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy === false ) {
+CalDAVPrincipal.php:      $params = array( ':request_principal' => $this->principal_id, ':scan_depth' => $c->permission_scan_depth );
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+CalDAVPrincipal.php:          if ( (bindec($relationship->pprivs) & $write_priv) != 0 ) {
+CalDAVPrincipal.php:            $this->write_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-write/', true);
+CalDAVPrincipal.php:            $this->read_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-read/', true);
+CalDAVPrincipal.php:      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+CalDAVPrincipal.php:        while( $relationship = $qry->Fetch() ) {
+CalDAVPrincipal.php:          if ( bindec($relationship->pprivs) & $write_priv ) {
+CalDAVPrincipal.php:            $this->write_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php:            $this->read_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-for:    %s', implode(',',$this->read_proxy_for) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-for:   %s', implode(',',$this->write_proxy_for) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Read-proxy-group:  %s', implode(',',$this->read_proxy_group) );
+CalDAVPrincipal.php://      @dbg_error_log( 'principal', 'Write-proxy-group: %s', implode(',',$this->write_proxy_group) );
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->read_proxy_group;
+CalDAVPrincipal.php:    if ( !isset($this->write_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->write_proxy_group;
+CalDAVPrincipal.php:  * @param string read/write - which sort of proxy list is requested.
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_for) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    if ( $type == 'write' ) return $this->write_proxy_for;
+CalDAVPrincipal.php:    return $this->read_proxy_for;
+CalDAVPrincipal.php:  * Accessor for the group membership - the groups this principal is a member of
+CalDAVPrincipal.php:    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+CalDAVPrincipal.php:    return $this->group_membership;
+CalDAVPrincipal.php:  * Accessor for the group member set - the members of this group
+CalDAVPrincipal.php:    if ( ! $this->_is_group ) return null;
+CalDAVPrincipal.php:    return $this->group_member_set;
+CalDAVPrincipal.php:  * @param array $options The request options, controlling whether e-mail paths are allowed.
+CalDAVPrincipal.php:      return $session->username;
+CalDAVPrincipal.php:      $username = $this->UsernameFromEMail($username);
+CalDAVPrincipal.php:  * Work out the username, based on the given e-mail
+CalDAVPrincipal.php:    @dbg_error_log( 'principal', 'Retrieving username from e-mail address "%s" ', $email );
+CalDAVPrincipal.php:    if ( $qry->Exec('principal') && $user = $qry->Fetch() ) {
+CalDAVPrincipal.php:      $username = $user->username;
+CalDAVPrincipal.php:      $this->by_email = true;
+CalDAVPrincipal.php:    return $this->exists;
+CalDAVPrincipal.php:    return $this->_is_group;
+CalDAVPrincipal.php:    return (isset($this->username)?$this->username:'username not set');
+CalDAVPrincipal.php:    if ( !isset($this->dav_name) ) {
+CalDAVPrincipal.php:      if ( !isset($this->username) ) $this->dav_name = '';
+CalDAVPrincipal.php:      else $this->dav_name = '/'.$this->username.'/';
+CalDAVPrincipal.php:    return $this->dav_name;
+CalDAVPrincipal.php:    if ( !isset($this->calendar_home_set) ) {
+CalDAVPrincipal.php:      $this->calendar_home_set = array();
+CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_calendar AND user_no = :user_no', array( ':user_no' => $this->user_no));
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+CalDAVPrincipal.php:          while( $calendar = $qry->Fetch() ) {
+CalDAVPrincipal.php:            $this->calendar_home_set[] = ConstructURL($calendar->parent_container, true);
+CalDAVPrincipal.php:          $this->calendar_home_set[] = $this->principal_url;
+CalDAVPrincipal.php:    return $this->calendar_home_set;
+CalDAVPrincipal.php:    if ( !isset($this->addressbook_home_set) ) {
+CalDAVPrincipal.php:     $this->addressbook_home_set = array();
+CalDAVPrincipal.php:/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_addressbook AND user_no = :user_no', array( ':user_no' => $this->user_no));
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        if ( $qry->rows() > 0 ) {
+CalDAVPrincipal.php:          while( $addressbook = $qry->Fetch() ) {
+CalDAVPrincipal.php:            $this->addressbook_home_set[] = ConstructURL($addressbook->parent_container, true);
+CalDAVPrincipal.php:          $this->addressbook_home_set[] = $this->principal_url;
+CalDAVPrincipal.php:    return $this->addressbook_home_set;
+CalDAVPrincipal.php:    if ( !isset($this->calendar_free_busy_set) ) {
+CalDAVPrincipal.php:      * calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV
+CalDAVPrincipal.php:      $this->calendar_free_busy_set = array();
+CalDAVPrincipal.php:                        array( ':user_no' => $this->user_no) );
+CalDAVPrincipal.php:      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+CalDAVPrincipal.php:        while( $calendar = $qry->Fetch() ) {
+CalDAVPrincipal.php:          $this->calendar_free_busy_set[] = ConstructURL($calendar->dav_name, true);
+CalDAVPrincipal.php:    return $this->calendar_free_busy_set;
+CalDAVPrincipal.php:    if ( !isset($this->privileges) ) $this->privileges = 0;
+CalDAVPrincipal.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+CalDAVPrincipal.php:    return $this->privileges;
+CalDAVPrincipal.php:                            'collection_id' => (isset($this->principal_id) ? $this->principal_id : 0),
+CalDAVPrincipal.php:                            'type'     => 'principal' . (substr($this->dav_name(), 0, 12) == '/principals/'?'_link':''),
+CalDAVPrincipal.php:                            'user_no'  => (isset($this->user_no)  ? $this->user_no : 0),
+CalDAVPrincipal.php:                            'username' => $this->username(),
+CalDAVPrincipal.php:                            'dav_name' => $this->dav_name,
+CalDAVPrincipal.php:                            'email'    => (isset($this->email)    ? $this->email : ''),
+CalDAVPrincipal.php:                            'created'  => (isset($this->created)  ? $this->created : date('Ymd\THis')),
+CalDAVPrincipal.php:                            'updated'  => (isset($this->updated)  ? $this->updated : date('Ymd\THis'))
+CalDAVPrincipal.php:    $collection->dav_etag = (isset($this->dav_etag) ? $this->dav_etag : md5($collection->username . $collection->updated));
+CalDAVPrincipal.php:    $collection->dav_displayname =  (isset($this->dav_displayname) ? $this->dav_displayname : (isset($this->fullname) ? $this->fullname : $collection->username));
+CalDAVPrincipal.php:        $prop->NewElement('getcontenttype', 'httpd/unix-directory' );
+CalDAVPrincipal.php:        $prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
+CalDAVPrincipal.php:        $prop->NewElement('displayname', $this->fullname );
+CalDAVPrincipal.php:      case 'DAV::principal-URL':
+CalDAVPrincipal.php:        $prop->NewElement('principal-URL', $reply->href($this->principal_url) );
+CalDAVPrincipal.php:        $prop->NewElement('getlastmodified', ISODateToHTTPDate($this->modified) );
+CalDAVPrincipal.php:        $prop->NewElement('creationdate', DateToISODate($this->created) );
+CalDAVPrincipal.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+CalDAVPrincipal.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+CalDAVPrincipal.php:        $prop->NewElement('getcontentlanguage', $locale );
+CalDAVPrincipal.php:      case 'DAV::group-member-set':
+CalDAVPrincipal.php:        if ( ! $this->_is_group ) return false;
+CalDAVPrincipal.php:        $prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
+CalDAVPrincipal.php:      case 'DAV::group-membership':
+CalDAVPrincipal.php:        $prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:dropbox-home-URL':
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-server':
+CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
+CalDAVPrincipal.php:      case 'http://calendarserver.org/ns/:xmpp-uri':
+CalDAVPrincipal.php:        if ( ! isset( $this->xmpp_uri ) ) return false;
+CalDAVPrincipal.php:        $reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:carddav:addressbook-home-set':
+CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->addressbook_home_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+CalDAVPrincipal.php:        $reply->NSElement($prop, $tag, $reply->href( $this->calendar_home_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+CalDAVPrincipal.php:        $reply->CalDAVElement( $prop, 'calendar-free-busy-set', $reply->href( $this->calendar_free_busy_set() ) );
+CalDAVPrincipal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+CalDAVPrincipal.php:        $reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
+CalDAVPrincipal.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url ) );
+CalDAVPrincipal.php:      case 'DAV::principal-collection-set':
+CalDAVPrincipal.php:        $reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
+CalDAVPrincipal.php:      case 'DAV::alternate-URI-set':
+CalDAVPrincipal.php:        $prop->NewElement( $reply->Tag($tag));
+CalDAVPrincipal.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+CalDAVPrincipal.php:        $denied[] = $reply->Tag($tag);
+CalDAVPrincipal.php:    dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
+CalDAVPrincipal.php:      if ( ! $this->PrincipalProperty( $tag, $prop, $reply, $denied ) ) {
+CalDAVPrincipal.php:        dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
+CalDAVPrincipal.php:        $not_found[] = $reply->Tag($tag);
+CalDAVPrincipal.php:    $href = $reply->href($this->url );
+CalDAVPrincipal.php:        $noprop->NewElement( $v );
+CalDAVPrincipal.php:        $noprop->NewElement( $v );
+caldav-PROPFIND.php:* CalDAV Server - handle PROPFIND method
+caldav-PROPFIND.php://$request->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read-current-user-privilege-set') );
+caldav-PROPFIND.php:if ( !isset($request->xml_tags) ) {
+caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body contains no XML data!") );
+caldav-PROPFIND.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-PROPFIND.php:  $request->DoResponse( 403, translate("Request body is not valid XML data!") );
+caldav-PROPFIND.php:$allprop    = $xmltree->GetPath('/DAV::propfind/*');
+caldav-PROPFIND.php:  switch ( $propwrap->GetTag() ) {
+caldav-PROPFIND.php:      $subprop = $propwrap->GetElements();
+caldav-PROPFIND.php:        $property_list[] = $v->GetTag();
+caldav-PROPFIND.php: * Add the calendar-proxy-read/write pseudocollections
+caldav-PROPFIND.php:  if ($parent_path != $request->principal->dav_name()) {
+caldav-PROPFIND.php:    dbg_error_log( 'PROPFIND', 'Not returning proxy response since "%s" != "%s"', $parent_path, $request->principal->dav_name() );
+caldav-PROPFIND.php:    $proxy_group = $request->principal->ReadProxyGroup();
+caldav-PROPFIND.php:    $proxy_group = $request->principal->WriteProxyGroup();
+caldav-PROPFIND.php:  $collection->parent_container = $parent_path;
+caldav-PROPFIND.php:  $collection->dav_name = $parent_path.'calendar-proxy-'.$which.'/';
+caldav-PROPFIND.php:  $collection->is_calendar    = 'f';
+caldav-PROPFIND.php:  $collection->is_addressbook = 'f';
+caldav-PROPFIND.php:  $collection->is_principal   = 't';
+caldav-PROPFIND.php:  $collection->is_proxy       = 't';
+caldav-PROPFIND.php:  $collection->proxy_type     = $which;
+caldav-PROPFIND.php:  $collection->type           = 'proxy';
+caldav-PROPFIND.php:  $collection->dav_displayname = $collection->dav_name;
+caldav-PROPFIND.php:  $collection->collection_id = 0;
+caldav-PROPFIND.php:  $collection->user_no = $session->user_no;
+caldav-PROPFIND.php:  $collection->username = $session->username;
+caldav-PROPFIND.php:  $collection->email = $session->email;
+caldav-PROPFIND.php:  $collection->created = date('Ymd\THis');
+caldav-PROPFIND.php:  $collection->dav_etag = md5($c->system_name . $collection->dav_name . implode($proxy_group) );
+caldav-PROPFIND.php:  $collection->proxy_for = $proxy_group;
+caldav-PROPFIND.php:  $collection->resourcetypes  = sprintf('<DAV::collection/><http://calendarserver.org/ns/:calendar-proxy-%s/>', $which);
+caldav-PROPFIND.php:  $collection->in_freebusy_set = 'f';
+caldav-PROPFIND.php:  $collection->schedule_transp = 'transp';
+caldav-PROPFIND.php:  $collection->timezone        = null;
+caldav-PROPFIND.php:  $collection->description     = '';
+caldav-PROPFIND.php:  $resource->FetchPrincipal();
+caldav-PROPFIND.php:  return $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:  $bound_from = $collection->bound_from();
+caldav-PROPFIND.php:  $bound_to   = $collection->dav_name();
+caldav-PROPFIND.php:  if ( !isset($parent_path) ) $parent_path = $collection->dav_name();
+caldav-PROPFIND.php:                                                              $depth, $collection->dav_name(), $bound_from, $bound_to );
+caldav-PROPFIND.php:  if ( ! $collection->IsCalendar() &&  ! $collection->IsAddressbook() ) {
+caldav-PROPFIND.php:    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+caldav-PROPFIND.php:      if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:        while( $binding = $qry->Fetch() ) {
+caldav-PROPFIND.php:          $resource = new DAVResource($binding->dav_name);
+caldav-PROPFIND.php:          //if ( $resource->HavePrivilegeTo('DAV::read', false) ) {
+caldav-PROPFIND.php:            $resource->set_bind_location( str_replace($bound_from,$bound_to,$binding->dav_name));
+caldav-PROPFIND.php:            $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:              $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource, $binding->dav_name ) );
+caldav-PROPFIND.php:    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:      while( $subcollection = $qry->Fetch() ) {
+caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$subcollection->dav_name));
+caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:          $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource,
+caldav-PROPFIND.php:                                                   str_replace($resource->parent_path(), $parent_path, $resource->dav_name() ) ) );
+caldav-PROPFIND.php:    if ( $collection->IsPrincipal() ) {
+caldav-PROPFIND.php:      // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
+caldav-PROPFIND.php:      dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $bound_from );
+caldav-PROPFIND.php:  //if ( $collection->HavePrivilegeTo('DAV::read', false) ) {
+caldav-PROPFIND.php:    if ( $collection->IsCalendar() ) {
+caldav-PROPFIND.php:      if ( ! $collection->HavePrivilegeTo('all', false) ) {
+caldav-PROPFIND.php:      if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+caldav-PROPFIND.php:        $time_limit_clause = " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+caldav-PROPFIND.php:    if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY caldav_data.dav_id";
+caldav-PROPFIND.php:    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-PROPFIND.php:      while( $item = $qry->Fetch() ) {
+caldav-PROPFIND.php:        $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->dav_name));
+caldav-PROPFIND.php:        $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+caldav-PROPFIND.php:if ( $request->IsProxyRequest() ) {
+caldav-PROPFIND.php:  $response = add_proxy_response($request->proxy_type, $request->principal->dav_name() );
+caldav-PROPFIND.php:  $resource = new DAVResource($request->path);
+caldav-PROPFIND.php:  if ( ! $resource->Exists() ) {
+caldav-PROPFIND.php:    $request->PreconditionFailed( 404, 'must-exist', translate('That resource is not present on this server.') );
+caldav-PROPFIND.php:  //$resource->NeedPrivilege('DAV::read');
+caldav-PROPFIND.php:  if ( $resource->IsCollection() ) {
+caldav-PROPFIND.php:    dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name() );
+caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:    if ( $request->depth > 0 ) {
+caldav-PROPFIND.php:      $responses = array_merge($responses, get_collection_contents( $request->depth - 1, $resource ) );
+caldav-PROPFIND.php:  //elseif ( $request->HavePrivilegeTo('DAV::read',false) ) 
+caldav-PROPFIND.php:    $responses[] = $resource->RenderAsXML($property_list, $reply);
+caldav-PROPFIND.php:$xmldoc = $reply->Render('multistatus', $responses);
+caldav-PROPFIND.php:$request->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:* CalDAV Server - handle PROPPATCH method
+caldav-PROPPATCH.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-PROPPATCH.php:$dav_resource = new DAVResource($request->path);
+caldav-PROPPATCH.php:if ( ! ($dav_resource->HavePrivilegeTo('DAV::write-properties') || $dav_resource->IsBinding() ) ) {
+caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+caldav-PROPPATCH.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-PROPPATCH.php:// echo $xmltree->Render();
+caldav-PROPPATCH.php:if ( $xmltree->GetTag() != "DAV::propertyupdate" ) {
+caldav-PROPPATCH.php:  $request->DoResponse( 403 );
+caldav-PROPPATCH.php:$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
+caldav-PROPPATCH.php:$rmprops  = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
+caldav-PROPPATCH.php:* the special-case stuff as needed and falling through to a default which
+caldav-PROPPATCH.php:$qry->Begin();
+caldav-PROPPATCH.php:$setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+caldav-PROPPATCH.php:      * Can't set displayname on resources - only collections or principals
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() || $dav_resource->IsPrincipal() ) {
+caldav-PROPPATCH.php:        if ( $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        else if ( $dav_resource->IsPrincipal() ) {
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':user_no' => $request->user_no) );
+caldav-PROPPATCH.php:          $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:      $setcollection  = count($setting->GetPath('DAV::resourcetype/DAV::collection'));
+caldav-PROPPATCH.php:      $setaddressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal()
+caldav-PROPPATCH.php:                            && ! $dav_resource->IsBinding() && ! ($setaddressbook && $setcalendar) ) {
+caldav-PROPPATCH.php:        $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET is_calendar = :is_calendar::boolean, is_addressbook = :is_addressbook::boolean,
+caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':resourcetypes' => $resourcetypes,
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-calendar-transp':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && ( $dav_resource->IsCalendar() || $setcalendar ) && !$dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $transparency = $setting->GetPath('urn:ietf:params:xml:ns:caldav:schedule-calendar-transp/*');
+caldav-PROPPATCH.php:        $transparency = preg_replace( '{^.*:}', '', $transparency[0]->GetTag());
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET schedule_transp = :transparency WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                    array( ':dav_name' => $dav_resource->dav_name(), ':transparency' => $transparency ) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                                new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                                translate("The CalDAV:schedule-calendar-transp property may only be set on calendars.") )
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:          new XMLElement( 'responsedescription', translate("The calendar-free-busy-set is superseded by the schedule-transp property of a calendar collection.") )
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone');
+caldav-PROPPATCH.php:        $tzstring = $tzcomponent[0]->GetContent();
+caldav-PROPPATCH.php:        $timezones = $calendar->component->GetComponents('VTIMEZONE');
+caldav-PROPPATCH.php:        $tzid = $tz->GetPValue('TZID');
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name',
+caldav-PROPPATCH.php:                                       array( ':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+caldav-PROPPATCH.php:      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                               new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                               translate("Property is read-only") )
+caldav-PROPPATCH.php:      $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text)',
+caldav-PROPPATCH.php:            array( ':dav_name' => $dav_resource->dav_name(), ':user_no' => $request->user_no, ':tag' => $tag, ':value' => $content) );
+caldav-PROPPATCH.php:  $tag = $setting->GetTag();
+caldav-PROPPATCH.php:  $content = $setting->RenderContent();
+caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+caldav-PROPPATCH.php:      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+caldav-PROPPATCH.php:        $qry->QDo('UPDATE collection SET timezone = NULL WHERE dav_name = :dav_name', array( ':dav_name' => $dav_resource->dav_name()) );
+caldav-PROPPATCH.php:        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+caldav-PROPPATCH.php:                              translate("calendar-timezone property is only valid for a calendar.") )
+caldav-PROPPATCH.php:    * The following properties are read-only, so they will cause the request to fail
+caldav-PROPPATCH.php:    case 'DAV::principal-collection-set':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:CALENDAR-USER-ADDRESS-SET':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+caldav-PROPPATCH.php:    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+caldav-PROPPATCH.php:      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+caldav-PROPPATCH.php:          new XMLElement('responsedescription', translate("Property is read-only") )
+caldav-PROPPATCH.php:      $qry->QDo('DELETE FROM property WHERE dav_name=:dav_name AND property_name=:property_name',
+caldav-PROPPATCH.php:                  array( ':dav_name' => $dav_resource->dav_name(), ':property_name' => $tag) );
+caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+caldav-PROPPATCH.php:  $qry->Rollback();
+caldav-PROPPATCH.php:  $request->DoResponse( 207, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:if ( $qry->Commit() ) {
+caldav-PROPPATCH.php:  $url = ConstructURL($request->path);
+caldav-PROPPATCH.php:  $request->DoResponse( 200, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+caldav-PROPPATCH.php:$request->DoResponse( 500 );
+caldav-PUT-default.php:* CalDAV Server - handle PUT method on unknown (arbitrary) content-types
+caldav-PUT-default.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT-default.php:    fwrite($fh,$request->raw_post);
+caldav-PUT-default.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT-default.php:$dest = new DAVResource($request->path);
+caldav-PUT-default.php:$container = $dest->FetchParentContainer();
+caldav-PUT-default.php:if ( $container->IsCalendar() ) {
+caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:caldav:supported-calendar-data',
+caldav-PUT-default.php:                  translate('Incorrect content type for calendar: ') . $request->content_type );
+caldav-PUT-default.php:else if ( $container->IsAddressbook() ) {
+caldav-PUT-default.php:  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:carddav:supported-address-data',
+caldav-PUT-default.php:                  translate('Incorrect content type for addressbook: ') . $request->content_type );
+caldav-PUT-default.php:if ( ! $dest->Exists() ) {
+caldav-PUT-default.php:  if ( $container->IsPrincipal() ) {
+caldav-PUT-default.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+caldav-PUT-default.php:  if ( ! $container->Exists() ) {
+caldav-PUT-default.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-PUT-default.php:  $container->NeedPrivilege('DAV::bind');
+caldav-PUT-default.php:  if ( $dest->IsCollection() ) {
+caldav-PUT-default.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+caldav-PUT-default.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+caldav-PUT-default.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+caldav-PUT-default.php:  $dest->NeedPrivilege('DAV::write-content');
+caldav-PUT-default.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+caldav-PUT-default.php:  $request->DoResponse(412);
+caldav-PUT-default.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+caldav-PUT-default.php:  $request->DoResponse(412);
+caldav-PUT-default.php:$collection_id = $container->GetProperty('collection_id');
+caldav-PUT-default.php:$qry->Begin();
+caldav-PUT-default.php:$etag = md5($request->raw_post);
+caldav-PUT-default.php:    ':user_no' => $dest->GetProperty('user_no'),
+caldav-PUT-default.php:    ':dav_name' => $dest->bound_from(),
+caldav-PUT-default.php:    ':dav_data' => $request->raw_post,
+caldav-PUT-default.php:    ':session_user' => $session->user_no
+caldav-PUT-default.php:if ( $dest->Exists() ) {
+caldav-PUT-default.php:$qry->QDo( $sql, $params );
+caldav-PUT-default.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+caldav-PUT-default.php:if ( !$qry->Exec('move') ) rollback(500);
+caldav-PUT-default.php:$request->DoResponse( $response_code );
+caldav-PUT-functions.php:* CalDAV Server - handle PUT method
+caldav-PUT-functions.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-PUT-functions.php:$tz_regex = ':^(Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Brazil|Canada|Chile|Etc|Europe|Indian|Mexico|Mideast|Pacific|US)/[a-z_]+$:i';
+caldav-PUT-functions.php:  $qry->Rollback();
+caldav-PUT-functions.php:    if ( isset($bad_events) && isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+caldav-PUT-functions.php:      $request->DoResponse( $error_no, $message );
+caldav-PUT-functions.php:  $c->messages[] = sprintf(translate('Status: %d, Message: %s, User: %d, Path: %s'), $error_no, $message, $user_no, $path);
+caldav-PUT-functions.php:  if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) {
+caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) {
+caldav-PUT-functions.php:    if ( !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+caldav-PUT-functions.php:      if ( $qry->rows() == 0 ) {
+caldav-PUT-functions.php:        $request->DoResponse( 405 ); // Method not allowed
+caldav-PUT-functions.php:    if ( $qry->rows() == 0 ) {
+caldav-PUT-functions.php:      $qry->QDo( $sql, $params );
+caldav-PUT-functions.php:      $collection = $qry->Fetch();
+caldav-PUT-functions.php:      $params = array( ':is_public' => ($public?'t':'f'), ':collection_id' => $collection->collection_id );
+caldav-PUT-functions.php:      if ( ! $qry->QDo($sql,$params) ) {
+caldav-PUT-functions.php:  if( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+caldav-PUT-functions.php:    $collection = $qry->Fetch();
+caldav-PUT-functions.php:    if ($collection->public_events_only == 't') {
+caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+caldav-PUT-functions.php:  $attendees = $ic->GetProperties('ATTENDEE');
+caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-PUT-functions.php:    $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+caldav-PUT-functions.php:    if ( $attendee->GetParameterValue ( 'PARTSTAT' ) != 'NEEDS-ACTION' || preg_match ( '/^[35]\.[3-9]/',  $attendee->GetParameterValue ( 'SCHEDULE-STATUS' ) ) ) {
+caldav-PUT-functions.php:      $attendee->SetParameterValue ('SCHEDULE-STATUS','3.7;Invalid Calendar User');
+caldav-PUT-functions.php:    $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+caldav-PUT-functions.php:    $priv =  $ar->HavePrivilegeTo('schedule-deliver-invite' );
+caldav-PUT-functions.php:    if ( ! $ar->HavePrivilegeTo('schedule-deliver-invite' ) ){
+caldav-PUT-functions.php:      $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+caldav-PUT-functions.php:      $reply->NSElement( $privnodes[1], 'schedule-deliver-invite' );
+caldav-PUT-functions.php:      $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+caldav-PUT-functions.php:      $xmldoc = $reply->Render('error',$xml);
+caldav-PUT-functions.php:      $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"');
+caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+caldav-PUT-functions.php:    $ncal->VCalendar ();
+caldav-PUT-functions.php:    $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+caldav-PUT-functions.php:    $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:    $content = $ncal->Render();
+caldav-PUT-functions.php:    $cid = $ar->GetProperty('collection_id');
+caldav-PUT-functions.php:    dbg_error_log('DELIVER', 'to user: %s, to path: %s, collection: %s, from user: %s, caldata %s', $attendee_principal->user_no, $deliver_path, $cid, $request->user_no, $content );
+caldav-PUT-functions.php:    write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:      $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:    $attendee->SetParameterValue ('SCHEDULE-STATUS','1.2;Scheduling message has been delivered');
+caldav-PUT-functions.php:  $ncal->VCalendar ();
+caldav-PUT-functions.php:  $ncal->AddProperty ( 'METHOD', 'REQUEST' );
+caldav-PUT-functions.php:  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:  $content = $ncal->Render();
+caldav-PUT-functions.php: 	$deliver_path = preg_replace ( '/^.*caldav.php/','', $request->principal->schedule_inbox_url );
+caldav-PUT-functions.php:  write_resource( $request->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:    $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:  header('Schedule-Tag: "'.$etag . '"' );
+caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+caldav-PUT-functions.php:  $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $etag = md5 ( $request->raw_post );
+caldav-PUT-functions.php:	$organizer = $ic->GetProperties('ORGANIZER');
+caldav-PUT-functions.php:  $attendees = array_merge($organizer,$ic->GetProperties('ATTENDEE'));
+caldav-PUT-functions.php:  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( "POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property" );
+caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:	  $deliver_path = preg_replace ( '/^.*caldav.php/','', $attendee_principal->schedule_inbox_url );
+caldav-PUT-functions.php:	  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
+caldav-PUT-functions.php:    if ( $attendee_email == $request->principal->email ) {
+caldav-PUT-functions.php:	  if ( ! $ar->HavePrivilegeTo('schedule-deliver-reply' ) ){
+caldav-PUT-functions.php:	     $privnodes = array( $reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement( 'privilege' ) );
+caldav-PUT-functions.php:	     $reply->NSElement( $privnodes[1], 'schedule-deliver-reply' );
+caldav-PUT-functions.php:	     $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+caldav-PUT-functions.php:	     $xmldoc = $reply->Render('error',$xml);
+caldav-PUT-functions.php:			 $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+caldav-PUT-functions.php:	  $ncal->VCalendar ();
+caldav-PUT-functions.php:	  $ncal->AddProperty ( 'METHOD', 'REPLY' );
+caldav-PUT-functions.php:	  $ncal->AddComponent ( array_merge ( $ical->GetComponents('VEVENT',false) , array ($ic) ));
+caldav-PUT-functions.php:	  $content = $ncal->Render();
+caldav-PUT-functions.php:	  write_resource( $attendee_principal->user_no, $deliver_path . $etag . '.ics' ,
+caldav-PUT-functions.php:	    $content , $ar->GetProperty('collection_id'), $request->user_no,
+caldav-PUT-functions.php:  $request->DoResponse( 201, 'Created' );
+caldav-PUT-functions.php:* @return float The result of the scheduling request, per caldav-sched #3.5.4
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'create_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'update_scheduling_requests called with non-object parameter (%s)', gettype($resource) );
+caldav-PUT-functions.php:  $attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/ATTENDEE');
+caldav-PUT-functions.php:	$wr_attendees = $resource->GetPropertiesByPath('/VCALENDAR/*/X-WR-ATTENDEE');
+caldav-PUT-functions.php:    dbg_error_log( 'POST', 'Non-compliant iCal request.  Using X-WR-ATTENDEE property' );
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', 'Event has no attendees - no scheduling required.', count($attendees) );
+caldav-PUT-functions.php:    $attendee->SetParameterValue( 'SCHEDULE-STATUS', write_scheduling_request( $resource, $attendee->Value() ) );
+caldav-PUT-functions.php:  if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put'])) ) {
+caldav-PUT-functions.php:    $fh = fopen('/tmp/PUT-2.txt','w');
+caldav-PUT-functions.php:  $timezones = $calendar->GetComponents('VTIMEZONE',true);
+caldav-PUT-functions.php:  $components = $calendar->GetComponents('VTIMEZONE',false);
+caldav-PUT-functions.php:  $displayname = $calendar->GetPValue('X-WR-CALNAME');
+caldav-PUT-functions.php:    if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:    $tz_ids[$tz->GetPValue('TZID')] = $k;
+caldav-PUT-functions.php:    $uid = $comp->GetPValue('UID');
+caldav-PUT-functions.php:    $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
+caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $comp->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:  if ( ! $qry->Exec('PUT',__LINE__,__FILE__) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:  if ( ! $qry->rows() == 1 ) {
+caldav-PUT-functions.php:  $collection = $qry->Fetch();
+caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) $qry->Begin();
+caldav-PUT-functions.php:  $base_params = array( ':collection_id' => $collection->collection_id );
+caldav-PUT-functions.php:    if ( !$qry->QDo('DELETE FROM calendar_item WHERE collection_id = :collection_id', $base_params)
+caldav-PUT-functions.php:      || !$qry->QDo('DELETE FROM caldav_data WHERE collection_id = :collection_id', $base_params) )
+caldav-PUT-functions.php:      rollback_on_error( $caldav_context, $user_no, $collection->collection_id );
+caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Begin();
+caldav-PUT-functions.php:    $vcal->VCalendar();
+caldav-PUT-functions.php:    $vcal->SetComponents($resource);
+caldav-PUT-functions.php:    $icalendar = $vcal->Render();
+caldav-PUT-functions.php:    $dav_data_params[':caldav_type'] = $first->GetType();
+caldav-PUT-functions.php:    $dav_data_params[':session_user'] = $session->user_no;
+caldav-PUT-functions.php:    if ( !$qry->QDo($dav_data_insert,$dav_data_params) ) rollback_on_error( $caldav_context, $user_no, $path );
+caldav-PUT-functions.php:    $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $dav_data_params[':dav_name']));
+caldav-PUT-functions.php:    if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+caldav-PUT-functions.php:      $dav_id = $row->dav_id;
+caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DTSTART');
+caldav-PUT-functions.php:    if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+caldav-PUT-functions.php:      $dtstart = $first->GetPValue('DUE');
+caldav-PUT-functions.php:    $dtend = $first->GetPValue('DTEND');
+caldav-PUT-functions.php:      dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:      if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+caldav-PUT-functions.php:        $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:      elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-PUT-functions.php:        * non-inclusive end is the end of the calendar date specified by the
+caldav-PUT-functions.php:        * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+caldav-PUT-functions.php:        $value_type = $first->GetPParamValue('DTSTART','VALUE');
+caldav-PUT-functions.php:    $last_modified = $first->GetPValue('LAST-MODIFIED');
+caldav-PUT-functions.php:    $dtstamp = $first->GetPValue('DTSTAMP');
+caldav-PUT-functions.php:    $class = ($collection->public_events_only == 't' ? 'PUBLIC' : $first->GetPValue('CLASS') );
+caldav-PUT-functions.php:    $tzid = $first->GetPParamValue('DTSTART','TZID');
+caldav-PUT-functions.php:    if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:        $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+caldav-PUT-functions.php:          $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+caldav-PUT-functions.php:      if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+caldav-PUT-functions.php:        $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+caldav-PUT-functions.php:        $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+caldav-PUT-functions.php:    $calitem_params[':uid'] = $first->GetPValue('UID');
+caldav-PUT-functions.php:    $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+caldav-PUT-functions.php:    $calitem_params[':location'] = $first->GetPValue('LOCATION');
+caldav-PUT-functions.php:    $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+caldav-PUT-functions.php:    $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+caldav-PUT-functions.php:    $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+caldav-PUT-functions.php:    $calitem_params[':url'] = $first->GetPValue('URL');
+caldav-PUT-functions.php:    $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+caldav-PUT-functions.php:    $calitem_params[':due'] = $first->GetPValue('DUE');
+caldav-PUT-functions.php:    $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+caldav-PUT-functions.php:    $calitem_params[':status'] = $first->GetPValue('STATUS');
+caldav-PUT-functions.php:    $created = $first->GetPValue('CREATED');
+caldav-PUT-functions.php:    if ( !$qry->QDo($sql,$calitem_params) ) rollback_on_error( $caldav_context, $user_no, $path);
+caldav-PUT-functions.php:    if ( isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import ) $qry->Commit();
+caldav-PUT-functions.php:  if ( !(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) ) {
+caldav-PUT-functions.php:    if ( ! $qry->Commit() ) rollback_on_error( $caldav_context, $user_no, $path);
+caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $alarms = $ical->GetComponents('VALARM');
+caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_alarm ( dav_id, action, trigger, summary, description, component, next_trigger )
+caldav-PUT-functions.php:  $qry->Prepare();
+caldav-PUT-functions.php:    $trigger = array_merge($v->GetProperties('TRIGGER'));
+caldav-PUT-functions.php:    $trigger_type = $trigger->GetParameterValue('VALUE');
+caldav-PUT-functions.php:      switch ( $trigger->GetParameterValue('RELATED') ) {
+caldav-PUT-functions.php:        case 'DTEND':  $related = $ical->GetPValue('DTEND'); break;
+caldav-PUT-functions.php:        case 'DUE':    $related = $ical->GetPValue('DUE');   break;
+caldav-PUT-functions.php:        default:       $related = $ical->GetPValue('DTSTART');
+caldav-PUT-functions.php:      $duration = $trigger->Value();
+caldav-PUT-functions.php:      $minus = (substr($duration,0,1) == '-');
+caldav-PUT-functions.php:      $related_trigger = trim(preg_replace( '#[PT-]#', ' ', $duration ));
+caldav-PUT-functions.php:        $related_trigger = preg_replace( '{(\d+[WDHMS])}', '-$1 ', $related_trigger );
+caldav-PUT-functions.php:    $qry->Bind(':action', $v->GetPValue('ACTION'));
+caldav-PUT-functions.php:    $qry->Bind(':trigger', $trigger->Render());
+caldav-PUT-functions.php:    $qry->Bind(':summary', $v->GetPValue('SUMMARY'));
+caldav-PUT-functions.php:    $qry->Bind(':description', $v->GetPValue('DESCRIPTION'));
+caldav-PUT-functions.php:    $qry->Bind(':component', $v->Render());
+caldav-PUT-functions.php:    $qry->Bind(':related', $related );
+caldav-PUT-functions.php:    $qry->Bind(':related_trigger', $related_trigger );
+caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:  $attendees = $ical->GetProperties('ATTENDEE');
+caldav-PUT-functions.php:  $qry->SetSql('INSERT INTO calendar_attendee ( dav_id, status, partstat, cn, attendee, role, rsvp, property )
+caldav-PUT-functions.php:  $qry->Prepare();
+caldav-PUT-functions.php:    $attendee = $v->Value();
+caldav-PUT-functions.php:      dbg_error_log( 'LOG', 'Duplicate: "%s"', $v->Render() );
+caldav-PUT-functions.php:    $qry->Bind(':attendee', $attendee );
+caldav-PUT-functions.php:    $qry->Bind(':status',   $v->GetParameterValue('STATUS') );
+caldav-PUT-functions.php:    $qry->Bind(':partstat', $v->GetParameterValue('PARTSTAT') );
+caldav-PUT-functions.php:    $qry->Bind(':cn',       $v->GetParameterValue('CN') );
+caldav-PUT-functions.php:    $qry->Bind(':role',     $v->GetParameterValue('ROLE') );
+caldav-PUT-functions.php:    $qry->Bind(':rsvp',     $v->GetParameterValue('RSVP') );
+caldav-PUT-functions.php:    $qry->Bind(':property', $v->Render() );
+caldav-PUT-functions.php:    $qry->Exec('PUT',__LINE__,__FILE__);
+caldav-PUT-functions.php:    $processed[$attendee] = $v->Render();
+caldav-PUT-functions.php:  $resources = $ic->GetComponents('VTIMEZONE',false); // Not matching VTIMEZONE
+caldav-PUT-functions.php:    /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
+caldav-PUT-functions.php:    $resource_type = $first->GetType();
+caldav-PUT-functions.php:  $qry->Begin();
+caldav-PUT-functions.php:  if ( !$qry->QDo($sql,$params) ) {
+caldav-PUT-functions.php:  $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
+caldav-PUT-functions.php:  if ( $qry->rows() == 1 && $row = $qry->Fetch() ) {
+caldav-PUT-functions.php:    $dav_id = $row->dav_id;
+caldav-PUT-functions.php:  $dtstart = $first->GetPValue('DTSTART');
+caldav-PUT-functions.php:  if ( (!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '' ) {
+caldav-PUT-functions.php:    $dtstart = $first->GetPValue('DUE');
+caldav-PUT-functions.php:  $dtend = $first->GetPValue('DTEND');
+caldav-PUT-functions.php:    dbg_error_log( 'PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:    if ( $first->GetPValue('DURATION') != '' AND $dtstart != '' ) {
+caldav-PUT-functions.php:      $duration = preg_replace( '#[PT]#', ' ', $first->GetPValue('DURATION') );
+caldav-PUT-functions.php:    elseif ( $first->GetType() == 'VEVENT' ) {
+caldav-PUT-functions.php:      * non-inclusive end is the end of the calendar date specified by the
+caldav-PUT-functions.php:      * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
+caldav-PUT-functions.php:      $value_type = $first->GetPParamValue('DTSTART','VALUE');
+caldav-PUT-functions.php:  $last_modified = $first->GetPValue('LAST-MODIFIED');
+caldav-PUT-functions.php:  $dtstamp = $first->GetPValue('DTSTAMP');
+caldav-PUT-functions.php:  $class = $first->GetPValue('CLASS');
+caldav-PUT-functions.php:  $tzid = $first->GetPParamValue('DTSTART','TZID');
+caldav-PUT-functions.php:  if ( !isset($tzid) || $tzid == '' ) $tzid = $first->GetPParamValue('DUE','TZID');
+caldav-PUT-functions.php:  $timezones = $ic->GetComponents('VTIMEZONE');
+caldav-PUT-functions.php:    if ( $tz->GetPValue('TZID') != $tzid ) {
+caldav-PUT-functions.php:      dbg_error_log( 'ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid );
+caldav-PUT-functions.php:      $tzid = $tz->GetPValue('TZID');
+caldav-PUT-functions.php:    $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
+caldav-PUT-functions.php:        $qry->QDo('SET TIMEZONE TO \''.$tz_locn."'" );
+caldav-PUT-functions.php:    if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 0 ) {
+caldav-PUT-functions.php:      $params[':tzspec'] = (isset($tz) ? $tz->Render() : null );
+caldav-PUT-functions.php:      $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params );
+caldav-PUT-functions.php:  $created = $first->GetPValue('CREATED');
+caldav-PUT-functions.php:  $calitem_params[':uid'] = $first->GetPValue('UID');
+caldav-PUT-functions.php:  $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
+caldav-PUT-functions.php:  $calitem_params[':location'] = $first->GetPValue('LOCATION');
+caldav-PUT-functions.php:  $calitem_params[':transp'] = $first->GetPValue('TRANSP');
+caldav-PUT-functions.php:  $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
+caldav-PUT-functions.php:  $calitem_params[':rrule'] = $first->GetPValue('RRULE');
+caldav-PUT-functions.php:  $calitem_params[':url'] = $first->GetPValue('URL');
+caldav-PUT-functions.php:  $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
+caldav-PUT-functions.php:  $calitem_params[':due'] = $first->GetPValue('DUE');
+caldav-PUT-functions.php:  $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
+caldav-PUT-functions.php:  $calitem_params[':status'] = $first->GetPValue('STATUS');
+caldav-PUT-functions.php:    log_caldav_action( $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+caldav-PUT-functions.php:            $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path );
+caldav-PUT-functions.php:  if ( !$qry->Exec('PUT',__LINE__,__FILE__) ) {
+caldav-PUT-functions.php:  $qry->QDo("SELECT write_sync_change( $collection_id, $sync_change, :dav_name)", array(':dav_name' => $path ) );
+caldav-PUT-functions.php:  $qry->Commit();
+caldav-PUT-functions.php:  if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() == 1 ) {
+caldav-PUT-functions.php:    $collection = $qry->Fetch();
+caldav-PUT-functions.php:    $user_no = $collection->user_no;
+caldav-PUT-functions.php:    return write_resource( $user_no, $path, $caldav_data, $collection->collection_id, $user_no, $etag, $ic, $put_action_type, false, $write_action_log );
+caldav-PUT.php:* CalDAV Server - handle PUT method
+caldav-PUT.php:$dav_resource = new DAVResource($request->path);
+caldav-PUT.php:if ( ! $dav_resource->HavePrivilegeTo('DAV::write-content') ) {
+caldav-PUT.php:  $request->DoResponse(403);
+caldav-PUT.php:if ( ! $dav_resource->Exists() && ! $dav_resource->HavePrivilegeTo('DAV::bind') ) {
+caldav-PUT.php:  $request->DoResponse(403);
+caldav-PUT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT.php:    fwrite($fh,$request->raw_post);
+caldav-PUT.php:include_once('caldav-PUT-functions.php');
+caldav-PUT.php:controlRequestContainer( $dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
+caldav-PUT.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT.php:if ( $dav_resource->IsCollection()  ) {
+caldav-PUT.php:  if ( $dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true ) {
+caldav-PUT.php:    $request->DoResponse( 405 ); // Method not allowed
+caldav-PUT.php:  * as an import. The code is in caldav-PUT-functions.php
+caldav-PUT.php:  import_collection($request->raw_post,$request->user_no,$request->path,true, $appending);
+caldav-PUT.php:  $request->DoResponse( 200 );
+caldav-PUT.php:$etag = md5($request->raw_post);
+caldav-PUT.php:$ic = new iCalComponent( $request->raw_post );
+caldav-PUT.php:if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
+caldav-PUT.php:  $request->PreconditionFailed(412,'if-match');
+caldav-PUT.php:if ( $dav_resource->Exists() ) {
+caldav-PUT.php:  if ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $dav_resource->unique_tag() ) {
+caldav-PUT.php:    $request->PreconditionFailed(412,'if-match', translate( 'Existing resource does not match "If-Match" header - not accepted.'));
+caldav-PUT.php:  else if ( isset($request->etag_none_match) && $request->etag_none_match != ''
+caldav-PUT.php:               && ($request->etag_none_match == $dav_resource->unique_tag() || $request->etag_none_match == '*') ) {
+caldav-PUT.php:    * (without the If-None-Match header) on that resource, or if "*" is
+caldav-PUT.php:    $request->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
+caldav-PUT.php:$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
+caldav-PUT.php:write_resource( $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), $request->raw_post, $dav_resource->GetProperty('collection_id'),
+caldav-PUT.php:                                $session->user_no, $etag, $ic, $put_action_type, true, true );
+caldav-PUT.php:$request->DoResponse( ($dav_resource->Exists() ? 204 : 201) );
+caldav-PUT-vcard.php:* CalDAV Server - handle PUT method on VCARD content-types
+caldav-PUT-vcard.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+caldav-PUT-vcard.php:    fwrite($fh,$request->raw_post);
+caldav-PUT-vcard.php:$lock_opener = $request->FailIfLocked();
+caldav-PUT-vcard.php:$dest = new DAVResource($request->path);
+caldav-PUT-vcard.php:$container = $dest->FetchParentContainer();
+caldav-PUT-vcard.php:if ( ! $dest->Exists() ) {
+caldav-PUT-vcard.php:  if ( $container->IsPrincipal() ) {
+caldav-PUT-vcard.php:    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+caldav-PUT-vcard.php:  if ( ! $container->Exists() ) {
+caldav-PUT-vcard.php:    $request->DoResponse( 409, translate('Destination collection does not exist') );
+caldav-PUT-vcard.php:  $container->NeedPrivilege('DAV::bind');
+caldav-PUT-vcard.php:  if ( $dest->IsCollection() ) {
+caldav-PUT-vcard.php:    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+caldav-PUT-vcard.php:      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+caldav-PUT-vcard.php:    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+caldav-PUT-vcard.php:  $dest->NeedPrivilege('DAV::write-content');
+caldav-PUT-vcard.php:if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+caldav-PUT-vcard.php:  $request->DoResponse(412);
+caldav-PUT-vcard.php:if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+caldav-PUT-vcard.php:  $request->DoResponse(412);
+caldav-PUT-vcard.php:$collection_id = $container->GetProperty('collection_id');
+caldav-PUT-vcard.php:$qry->Begin();
+caldav-PUT-vcard.php:$etag = md5($request->raw_post);
+caldav-PUT-vcard.php:    ':user_no' => $dest->GetProperty('user_no'),
+caldav-PUT-vcard.php:    ':dav_name' => $dest->bound_from(),
+caldav-PUT-vcard.php:    ':dav_data' => $request->raw_post,
+caldav-PUT-vcard.php:    ':session_user' => $session->user_no
+caldav-PUT-vcard.php:if ( $dest->Exists() ) {
+caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+caldav-PUT-vcard.php:  $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']) );
+caldav-PUT-vcard.php:  $qry->QDo( $sql, $params );
+caldav-PUT-vcard.php:  $qry->QDo("SELECT currval('dav_id_seq') AS dav_id" );
+caldav-PUT-vcard.php:$row = $qry->Fetch();
+caldav-PUT-vcard.php:$vcard = new vCard( $request->raw_post );
+caldav-PUT-vcard.php:$vcard->Write( $row->dav_id, $dest->Exists() );
+caldav-PUT-vcard.php:$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+caldav-PUT-vcard.php:if ( !$qry->QDo('COMMIT') ) rollback(500);
+caldav-PUT-vcard.php:$request->DoResponse( $response_code );
+caldav-REPORT-calquery.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+caldav-REPORT-calquery.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+caldav-REPORT-calquery.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+caldav-REPORT-calquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-query');
+caldav-REPORT-calquery.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-calquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/'.$proptype.'/*');
+caldav-REPORT-calquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-calquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-calquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-calquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-calquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-calquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-calquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-calquery.php: * There can only be *one* FILTER element, and it must contain *one* COMP-FILTER
+caldav-REPORT-calquery.php: * element.  In every case I can see this contained COMP-FILTER element will be a
+caldav-REPORT-calquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/urn:ietf:params:xml:ns:caldav:filter/*');
+caldav-REPORT-calquery.php:  if ( $qry_filters->GetTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $qry_filters->GetAttribute("name") == "VCALENDAR" )
+caldav-REPORT-calquery.php:    $qry_filters = $qry_filters->GetContent();  // Everything is inside a VCALENDAR AFAICS
+caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("name") );
+caldav-REPORT-calquery.php:  dbg_error_log("calquery","Applying filter for item '%s'", $item->dav_name );
+caldav-REPORT-calquery.php:  $ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
+caldav-REPORT-calquery.php:  return $ical->TestFilter($filters);
+caldav-REPORT-calquery.php:    $tag = $v->GetTag();
+caldav-REPORT-calquery.php:    dbg_error_log("calquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
+caldav-REPORT-calquery.php:        $not_defined = "not-"; // then fall through to IS-DEFINED case
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:is-defined':
+caldav-REPORT-calquery.php:          dbg_error_log("calquery", "Could not handle 'is-%sdefined' on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
+caldav-REPORT-calquery.php:            case 'completed':  /** @todo when it can be handled in the SQL - see around line 200 below */
+caldav-REPORT-calquery.php:              if ( ! $target_collection->IsSchedulingCollection() ) {
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:time-range':
+caldav-REPORT-calquery.php:        $start_column = ($components[sizeof($components)-1] == 'VTODO' ? "due" : 'dtend');     // The column we compare against the START attribute
+caldav-REPORT-calquery.php:        $start = $v->GetAttribute("start");
+caldav-REPORT-calquery.php:        $finish = $v->GetAttribute("end");
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:text-match':
+caldav-REPORT-calquery.php:        $search = $v->GetContent();
+caldav-REPORT-calquery.php:        $negate = $v->GetAttribute("negate-condition");
+caldav-REPORT-calquery.php:        $collation = $v->GetAttribute("collation");
+caldav-REPORT-calquery.php:          case 'i;ascii-casemap':
+caldav-REPORT-calquery.php:        dbg_error_log("calquery", " text-match: (%s IS NULL OR %s%s %s '%s') ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:comp-filter':
+caldav-REPORT-calquery.php:        $comp_filter_name = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:prop-filter':
+caldav-REPORT-calquery.php:        $propertyname = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:          case 'PERCENT-COMPLETE':
+caldav-REPORT-calquery.php:            dbg_error_log("calquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:      case 'urn:ietf:params:xml:ns:caldav:param-filter':
+caldav-REPORT-calquery.php:        return false; // Can't handle PARAM-FILTER conditions in the SQL
+caldav-REPORT-calquery.php:        $parameter = $v->GetAttribute("name");
+caldav-REPORT-calquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-calquery.php:$target_collection = new DAVResource($request->path);
+caldav-REPORT-calquery.php:$bound_from = $target_collection->bound_from();
+caldav-REPORT-calquery.php:if ( !$target_collection->Exists() ) {
+caldav-REPORT-calquery.php:  $request->DoResponse( 404 );
+caldav-REPORT-calquery.php:if ( ! ($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection()) ) {
+caldav-REPORT-calquery.php:  $request->DoResponse( 403, translate('The calendar-query report must be run against a calendar or a scheduling collection') );
+caldav-REPORT-calquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+caldav-REPORT-calquery.php:if ( $target_collection->Privileges() != privilege_to_bits('DAV::all') ) {
+caldav-REPORT-calquery.php:if ( isset($c->hide_TODO) && $c->hide_TODO && ! $target_collection->HavePrivilegeTo('DAV::write-content') ) {
+caldav-REPORT-calquery.php:if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+caldav-REPORT-calquery.php:  $where .= " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+caldav-REPORT-calquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+caldav-REPORT-calquery.php:if ( $qry->Exec("calquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-calquery.php:  while( $calendar_object = $qry->Fetch() ) {
+caldav-REPORT-calquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+caldav-REPORT-calquery.php:        $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+caldav-REPORT-calquery.php:        $vResource = new vComponent($calendar_object->caldav_data);
+caldav-REPORT-calquery.php:        $calendar_object->caldav_data = $expanded->Render();
+caldav-REPORT-calquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-calquery.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-cardquery.php:  $expansion = $address_data_xml->GetElements();
+caldav-REPORT-cardquery.php:    $address_data_properties[strtoupper($v->GetAttribute('name'))] = true;
+caldav-REPORT-cardquery.php:$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-query');
+caldav-REPORT-cardquery.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-cardquery.php:    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/'.$proptype.'/*');
+caldav-REPORT-cardquery.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-cardquery.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-cardquery.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+caldav-REPORT-cardquery.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-cardquery.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-cardquery.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-cardquery.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+caldav-REPORT-cardquery.php:$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/urn:ietf:params:xml:ns:carddav:filter/*');
+caldav-REPORT-cardquery.php:  dbg_error_log("cardquery","Applying filter for item '%s'", $item->dav_name );
+caldav-REPORT-cardquery.php:  $vcard = new vComponent( $item->caldav_data );
+caldav-REPORT-cardquery.php:  return $vcard->TestFilter($filters);
+caldav-REPORT-cardquery.php:    $tag = $v->GetTag();
+caldav-REPORT-cardquery.php:    dbg_error_log("cardquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:text-match':
+caldav-REPORT-cardquery.php:        $search = $v->GetContent();
+caldav-REPORT-cardquery.php:        $negate = $v->GetAttribute("negate-condition");
+caldav-REPORT-cardquery.php:        $collation = $v->GetAttribute("collation");
+caldav-REPORT-cardquery.php:          case 'i;ascii-casemap':
+caldav-REPORT-cardquery.php:          case 'i;unicode-casemap':
+caldav-REPORT-cardquery.php:        dbg_error_log("cardquery", " text-match: (%s%s %s '%s') ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:prop-filter':
+caldav-REPORT-cardquery.php:        $propertyname = $v->GetAttribute("name");
+caldav-REPORT-cardquery.php:            dbg_error_log("cardquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-cardquery.php:      case 'urn:ietf:params:xml:ns:carddav:param-filter':
+caldav-REPORT-cardquery.php:        return false; /** Figure out how to handle PARAM-FILTER conditions in the SQL */
+caldav-REPORT-cardquery.php:        $parameter = $v->GetAttribute("name");
+caldav-REPORT-cardquery.php:        $subfilter = $v->GetContent();
+caldav-REPORT-cardquery.php:$target_collection = new DAVResource($request->path);
+caldav-REPORT-cardquery.php:$bound_from = $target_collection->bound_from();
+caldav-REPORT-cardquery.php:if ( !$target_collection->Exists() ) {
+caldav-REPORT-cardquery.php:  $request->DoResponse( 404 );
+caldav-REPORT-cardquery.php:if ( ! ($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection()) ) {
+caldav-REPORT-cardquery.php:  $request->DoResponse( 403, translate('The addressbook-query report must be run against an addressbook collection') );
+caldav-REPORT-cardquery.php:$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+caldav-REPORT-cardquery.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+caldav-REPORT-cardquery.php:if ( $qry->Exec("cardquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-cardquery.php:  while( $address_object = $qry->Fetch() ) {
+caldav-REPORT-cardquery.php:      if ( $bound_from != $target_collection->dav_name() ) {
+caldav-REPORT-cardquery.php:        $address_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $address_object->dav_name);
+caldav-REPORT-cardquery.php:        $vcard = new VCard($address_object->caldav_data);
+caldav-REPORT-cardquery.php:        $vcard->MaskProperties($address_data_properties);
+caldav-REPORT-cardquery.php:        $address_object->caldav_data = $vcard->Render();
+caldav-REPORT-cardquery.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-cardquery.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-expand-property.php:  $propstat_set = $multistatus_response->GetElements('propstat');
+caldav-REPORT-expand-property.php:    $status = $v->GetElements('status');
+caldav-REPORT-expand-property.php:    if ( preg_match( '{^HTTP/\S+\s+200}', $status[0]->GetContent() ) ) {
+caldav-REPORT-expand-property.php:    $props = $propstat_200->GetElements('prop');
+caldav-REPORT-expand-property.php:      $properties = array_merge($properties,$p->GetElements());
+caldav-REPORT-expand-property.php://      dbg_error_log('REPORT',' get_href_containers: Checking property "%s" for hrefs.', $property->GetNSTag() );
+caldav-REPORT-expand-property.php:      $hrefs = $property->GetElements('href');
+caldav-REPORT-expand-property.php:      $pname = $property->GetAttribute('name');
+caldav-REPORT-expand-property.php:      $pns = $property->GetAttribute('namespace');
+caldav-REPORT-expand-property.php:      $subtrees[$pname] = $property->GetElements();
+caldav-REPORT-expand-property.php:    $part_response = $resource->RenderAsXML( $props, $reply );
+caldav-REPORT-expand-property.php:            $hrefs = $property->GetElements();
+caldav-REPORT-expand-property.php:            $pname = $property->GetTag();
+caldav-REPORT-expand-property.php:            $pns = $property->GetAttribute('xmlns');
+caldav-REPORT-expand-property.php:              $content = $v->GetContent();
+caldav-REPORT-expand-property.php:            $property->SetContent( expand_properties($paths, $subtrees[$pname], $reply, false) );
+caldav-REPORT-expand-property.php:$property_tree = $xmltree->GetPath('/DAV::expand-property/DAV::property');
+caldav-REPORT-expand-property.php:$multistatus = new XMLElement( "multistatus", expand_properties( $request->path, $property_tree, $reply), $reply->GetXmlNsArray() );
+caldav-REPORT-expand-property.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-freebusy.php: * Handle the FREE-BUSY-QUERY variant of REPORT
+caldav-REPORT-freebusy.php:include_once("freebusy-functions.php");
+caldav-REPORT-freebusy.php:$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
+caldav-REPORT-freebusy.php:$fbq_start = $fbq_content[0]->GetAttribute('start');
+caldav-REPORT-freebusy.php:$fbq_end   = $fbq_content[0]->GetAttribute('end');
+caldav-REPORT-freebusy.php:  $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+caldav-REPORT-freebusy.php:$freebusy = get_freebusy( '^'.$request->path.$request->DepthRegexTail(), $range_start, $range_end );
+caldav-REPORT-freebusy.php:$result->VCalendar();
+caldav-REPORT-freebusy.php:$result->AddComponent($freebusy);
+caldav-REPORT-freebusy.php:$request->DoResponse( 200, $result->Render(), 'text/calendar' );
+caldav-REPORT-multiget.php:* Handle the calendar-multiget REPORT request.
+caldav-REPORT-multiget.php:  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+caldav-REPORT-multiget.php:    $expand_range_start = $expansion[0]->GetAttribute('start');
+caldav-REPORT-multiget.php:    $expand_range_end = $expansion[0]->GetAttribute('end');
+caldav-REPORT-multiget.php:$proptype = $qry_content[0]->GetTag();
+caldav-REPORT-multiget.php:    $qry_props = $xmltree->GetPath('/*/'.$proptype.'/*');
+caldav-REPORT-multiget.php:    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+caldav-REPORT-multiget.php:      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+caldav-REPORT-multiget.php:      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-multiget.php:    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+caldav-REPORT-multiget.php:      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+caldav-REPORT-multiget.php:        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+caldav-REPORT-multiget.php:        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+caldav-REPORT-multiget.php:$collection = new DAVResource($request->path);
+caldav-REPORT-multiget.php:$bound_from = $collection->bound_from();
+caldav-REPORT-multiget.php:$mg_hrefs = $xmltree->GetPath('/*/DAV::href');
+caldav-REPORT-multiget.php:   * anything up to the matching request->path (which will include any http...) and then
+caldav-REPORT-multiget.php:  $href = $bound_from . preg_replace( "{^.*\E$request->path\Q}", '', rawurldecode($v->GetContent()) );
+caldav-REPORT-multiget.php:$where = " WHERE caldav_data.collection_id = " . $collection->resource_id();
+caldav-REPORT-multiget.php:  if ( $collection->Privileges() != privilege_to_bits('DAV::all') ) {
+caldav-REPORT-multiget.php:  if ( isset($c->hide_TODO) && $c->hide_TODO && ! $collection->Privileges() == privilege_to_bits('all') ) {
+caldav-REPORT-multiget.php:if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $where .= " ORDER BY caldav_data.dav_id";
+caldav-REPORT-multiget.php:if ( $qry->Exec('REPORT',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-multiget.php:  while( $dav_object = $qry->Fetch() ) {
+caldav-REPORT-multiget.php:    if ( $bound_from != $collection->dav_name() ) {
+caldav-REPORT-multiget.php:      $dav_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $dav_object->dav_name);
+caldav-REPORT-multiget.php:      $vResource = new vComponent($dav_object->caldav_data);
+caldav-REPORT-multiget.php:      $dav_object->caldav_data = $expanded->Render();
+caldav-REPORT-multiget.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-multiget.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT.php:* CalDAV Server - handle REPORT method
+caldav-REPORT.php:require_once('RRule-v2.php');
+caldav-REPORT.php:if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['report']) && $c->dbg['report'])) ) {
+caldav-REPORT.php:    fwrite($fh,$request->raw_post);
+caldav-REPORT.php:if ( !isset($request->xml_tags) ) {
+caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body contains no XML data!") );
+caldav-REPORT.php:$xmltree = BuildXMLTree( $request->xml_tags, $position);
+caldav-REPORT.php:  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+caldav-REPORT.php:$target = new DAVResource($request->path);
+caldav-REPORT.php:if ( $xmltree->GetTag() != 'DAV::principal-property-search'
+caldav-REPORT.php:                && $xmltree->GetTag() != 'DAV::principal-property-search-set' ) {
+caldav-REPORT.php:  //$target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy'), true ); // They may have either
+caldav-REPORT.php:$reportnum = -1;
+caldav-REPORT.php:if ( $xmltree->GetTag() == 'urn:ietf:params:xml:ns:caldav:free-busy-query' ) {
+caldav-REPORT.php:  include("caldav-REPORT-freebusy.php");
+caldav-REPORT.php:switch( $xmltree->GetTag() ) {
+caldav-REPORT.php:  case 'DAV::principal-property-search':
+caldav-REPORT.php:    include("caldav-REPORT-principal.php");
+caldav-REPORT.php:  case 'DAV::principal-search-property-set':
+caldav-REPORT.php:    include("caldav-REPORT-pps-set.php");
+caldav-REPORT.php:  case 'DAV::sync-collection':
+caldav-REPORT.php:    include("caldav-REPORT-sync-collection.php");
+caldav-REPORT.php:  case 'DAV::expand-property':
+caldav-REPORT.php:    include("caldav-REPORT-expand-property.php");
+caldav-REPORT.php:  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+caldav-REPORT.php:  $caldav_data = $item->caldav_data;
+caldav-REPORT.php:  $displayname = $item->summary;
+caldav-REPORT.php:  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+caldav-REPORT.php:    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+caldav-REPORT.php:      if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+caldav-REPORT.php:        $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-REPORT.php:        $confidential->SetType($first->GetType());
+caldav-REPORT.php:        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-REPORT.php:        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+caldav-REPORT.php:        $ical->SetComponents(array($confidential),$confidential->GetType());
+caldav-REPORT.php:        $caldav_data = $ical->Render();
+caldav-REPORT.php:  $url = ConstructURL($item->dav_name);
+caldav-REPORT.php:        $prop->NewElement($k, $contentlength );
+caldav-REPORT.php:      case 'calendar-data':
+caldav-REPORT.php:        $reply->CalDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:        $prop->NewElement($k, "text/calendar" );
+caldav-REPORT.php:      case 'current-user-principal':
+caldav-REPORT.php:        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+caldav-REPORT.php:        $prop->NewElement($k, $displayname );
+caldav-REPORT.php:        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+caldav-REPORT.php:        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+caldav-REPORT.php:      case '"current-user-privilege-set"':
+caldav-REPORT.php:        $prop->NewElement($k, privileges($request->permissions) );
+caldav-REPORT.php:      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:  dbg_error_log("REPORT","Building XML Response for item '%s'", $item->dav_name );
+caldav-REPORT.php:  $caldav_data = $item->caldav_data;
+caldav-REPORT.php:  $displayname = preg_replace( '{^.*/}', '', $item->dav_name );
+caldav-REPORT.php:  switch( $item->caldav_type ) {
+caldav-REPORT.php:      $displayname = $item->summary;
+caldav-REPORT.php:      $displayname = $item->fn;
+caldav-REPORT.php:      $contenttype = 'text/x-vcard';
+caldav-REPORT.php:  if ( isset($properties['calendar-data']) || isset($properties['displayname']) ) {
+caldav-REPORT.php:    if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
+caldav-REPORT.php:      if ( $type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
+caldav-REPORT.php:        $resources = $ical->GetComponents('VTIMEZONE',false);
+caldav-REPORT.php:        $confidential->SetType($first->GetType());
+caldav-REPORT.php:        $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+caldav-REPORT.php:        $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTSTART'), 'DTSTART' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('RRULE'), 'RRULE' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DURATION'), 'DURATION' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('DTEND'), 'DTEND' );
+caldav-REPORT.php:        $confidential->SetProperties( $first->GetProperties('UID'), 'UID' );
+caldav-REPORT.php:        $ical->SetComponents(array($confidential),$confidential->GetType());
+caldav-REPORT.php:        $caldav_data = $ical->Render();
+caldav-REPORT.php:  $url = ConstructURL($item->dav_name);
+caldav-REPORT.php:        $prop->NewElement($k, $contentlength );
+caldav-REPORT.php:      case 'calendar-data':
+caldav-REPORT.php:        if ( $type == 'calendar' ) $reply->CalDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:      case 'address-data':
+caldav-REPORT.php:        if ( $type == 'vcard' ) $reply->CardDAVElement($prop, $k, $caldav_data );
+caldav-REPORT.php:        $prop->NewElement($k, $contenttype );
+caldav-REPORT.php:      case 'current-user-principal':
+caldav-REPORT.php:        $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
+caldav-REPORT.php:        $prop->NewElement($k, $displayname );
+caldav-REPORT.php:        $prop->NewElement($k); // Just an empty resourcetype for a non-collection.
+caldav-REPORT.php:        $prop->NewElement($k, '"'.$item->dav_etag.'"' );
+caldav-REPORT.php:      case '"current-user-privilege-set"':
+caldav-REPORT.php:        $prop->NewElement($k, privileges($request->permissions) );
+caldav-REPORT.php:      case 'SOME-DENIED-PROPERTY':  /** indicating the style for future expansion */
+caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:      $noprop->NewElement( strtolower($v) );
+caldav-REPORT.php:if ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-query" ) {
+caldav-REPORT.php:  $calquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:caldav:calendar-query/*");
+caldav-REPORT.php:  include("caldav-REPORT-calquery.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:caldav:calendar-multiget" ) {
+caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-multiget');
+caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-multiget" ) {
+caldav-REPORT.php:  $qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-multiget');
+caldav-REPORT.php:  include("caldav-REPORT-multiget.php");
+caldav-REPORT.php:elseif ( $xmltree->GetTag() == "urn:ietf:params:xml:ns:carddav:addressbook-query" ) {
+caldav-REPORT.php:  $cardquery = $xmltree->GetPath("/urn:ietf:params:xml:ns:carddav:addressbook-query/*");
+caldav-REPORT.php:  include("caldav-REPORT-cardquery.php");
+caldav-REPORT.php:  $request->PreconditionFailed( 403, 'DAV::supported-report', sprintf( '"%s" is not a supported report type') );
+caldav-REPORT-pps-set.php:* DAViCal CalDAV Server - handle principal-search-property-set report (RFC3744)
+caldav-REPORT-pps-set.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-REPORT-pps-set.php:  $xmldoc->NSElement($prop, $property );
+caldav-REPORT-pps-set.php:  return new XMLElement( 'principal-search-property', $prop );
+caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-home-set',
+caldav-REPORT-pps-set.php:  'urn:ietf:params:xml:ns:caldav:calendar-user-address-set'
+caldav-REPORT-pps-set.php:$report = new XMLElement( 'principal-search-property-set', $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-pps-set.php:$request->XMLResponse( 207, $report );
+caldav-REPORT-principal.php:$searches = $xmltree->GetPath('/DAV::principal-property-search/DAV::property-search');
+caldav-REPORT-principal.php:$CS_search_test = $xmltree->GetAttribute('test');
+caldav-REPORT-principal.php:  $qry_props = $search->GetPath('/DAV::property-search/DAV::prop/*');  // There may be many
+caldav-REPORT-principal.php:  $match     = $search->GetPath('/DAV::property-search/DAV::match');   // There may only be one
+caldav-REPORT-principal.php:  $match = $match[0]->GetContent();
+caldav-REPORT-principal.php:    switch( $v1->GetTag() ) {
+caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+caldav-REPORT-principal.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+caldav-REPORT-principal.php:        * @todo We should handle a lot more properties here.  principal-URL seems a likely one to be used.
+caldav-REPORT-principal.php:        dbg_error_log("principal", "Unhandled tag '%s' to match '%s'\n", $v1->GetTag(), $match );
+caldav-REPORT-principal.php:$get_props = $xmltree->GetPath('/DAV::principal-property-search/DAV::prop/*');
+caldav-REPORT-principal.php:  $properties[] = $v1->GetTag();
+caldav-REPORT-principal.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+caldav-REPORT-principal.php:  while( $row = $qry->Fetch() ) {
+caldav-REPORT-principal.php:    $responses[] = $principal->RenderAsXML( $properties, $reply );
+caldav-REPORT-principal.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-principal.php:$request->XMLResponse( 207, $multistatus );
+caldav-REPORT-sync-collection.php:* CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
+caldav-REPORT-sync-collection.php:* @copyright Morphoss Ltd - http://www.morphoss.com/
+caldav-REPORT-sync-collection.php:$sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
+caldav-REPORT-sync-collection.php:$sync_token = $sync_tokens[0]->GetContent();
+caldav-REPORT-sync-collection.php:dbg_error_log( 'sync', " sync-token: %s", $sync_token );
+caldav-REPORT-sync-collection.php:$props = $xmltree->GetElements('DAV::prop');
+caldav-REPORT-sync-collection.php:$props = $v->GetContent();
+caldav-REPORT-sync-collection.php:  $proplist[] = $v->GetTag();
+caldav-REPORT-sync-collection.php:$collection = new DAVResource( $request->path );
+caldav-REPORT-sync-collection.php:$params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
+caldav-REPORT-sync-collection.php:if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
+caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:$row = $qry->Fetch();
+caldav-REPORT-sync-collection.php:if ( !isset($row->new_sync_token) ) {
+caldav-REPORT-sync-collection.php:  if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
+caldav-REPORT-sync-collection.php:    $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:  $row = $qry->Fetch();
+caldav-REPORT-sync-collection.php:$new_token = $row->new_sync_token;
+caldav-REPORT-sync-collection.php:if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
+caldav-REPORT-sync-collection.php:  while( $object = $qry->Fetch() ) {
+caldav-REPORT-sync-collection.php:    if ( $object->dav_name == $last_dav_name ) {
+caldav-REPORT-sync-collection.php:      if ( $object->sync_status == 404 ) {
+caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+caldav-REPORT-sync-collection.php:      else if ( $object->sync_status == 201 && $first_status == 404 ) {
+caldav-REPORT-sync-collection.php:          new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:          new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:        $responses[] = new XMLElement( 'sync-response', $resultset );
+caldav-REPORT-sync-collection.php:        new XMLElement( 'href', ConstructURL($object->dav_name) ),
+caldav-REPORT-sync-collection.php:        new XMLElement( 'status', display_status($object->sync_status) )
+caldav-REPORT-sync-collection.php:      if ( $object->sync_status != 404 ) {
+caldav-REPORT-sync-collection.php:        $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) );
+caldav-REPORT-sync-collection.php:      if ( isset($c->use_old_sync_response_tag) && $c->use_old_sync_response_tag ) $response_tag = 'sync-response';
+caldav-REPORT-sync-collection.php:      $first_status = $object->sync_status;
+caldav-REPORT-sync-collection.php:      $last_dav_name  = $object->dav_name;
+caldav-REPORT-sync-collection.php:  $responses[] = new XMLElement( 'sync-token', $new_token );
+caldav-REPORT-sync-collection.php:  $request->DoResponse( 500, translate("Database error") );
+caldav-REPORT-sync-collection.php:$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+caldav-REPORT-sync-collection.php:$request->XMLResponse( 207, $multistatus );
+CalDAVRequest.php:*  - Ascertaining the paths
+CalDAVRequest.php:*  - Ascertaining the current user's permission to those paths.
+CalDAVRequest.php:*  - Utility functions which we can use to decide whether this
+CalDAVRequest.php:  * The 'current_user_principal_xml' the DAV:current-user-principal answer. An
+CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+CalDAVRequest.php:  *  calendar, schedule-inbox, schedule-outbox
+CalDAVRequest.php:    $this->supported_privileges = array(
+CalDAVRequest.php:          'write-content' => translate('Write content'),
+CalDAVRequest.php:          'write-properties' => translate('Write properties')
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:read-free-busy' => translate('Read the free/busy information for a calendar collection'),
+CalDAVRequest.php:        'read-acl' => translate('Read ACLs for a resource or collection'),
+CalDAVRequest.php:        'read-current-user-privilege-set' => translate('Read the details of the current user\'s access control to this resource.'),
+CalDAVRequest.php:        'write-acl' => translate('Write ACLs for a resource or collection'),
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-deliver' => array(
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite'=> translate('Deliver scheduling invitations from an organiser to this scheduling inbox'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply' => translate('Deliver scheduling replies from an attendee to this scheduling inbox'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy' => translate('Allow free/busy enquiries targeted at the owner of this scheduling inbox')
+CalDAVRequest.php:        'urn:ietf:params:xml:ns:caldav:schedule-send' => array(
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-invite' => translate('Send scheduling invitations as an organiser from the owner of this scheduling outbox.'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-reply' => translate('Send scheduling replies as an attendee from the owner of this scheduling outbox.'),
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy' => translate('Send free/busy enquiries')
+CalDAVRequest.php:    $this->options = $options;
+CalDAVRequest.php:    if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false;
+CalDAVRequest.php:    $this->principal = (object) array( 'username' => $session->username, 'user_no' => $session->user_no );
+CalDAVRequest.php:    $this->raw_post = file_get_contents ( 'php://input');
+CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['request']) && $c->dbg['request']) ) {
+CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s: %s", $k, $v );
+CalDAVRequest.php:      $lines = preg_split( '#[\r\n]+#', $this->raw_post);
+CalDAVRequest.php:        dbg_error_log( "LOG request", "-->%s", $v );
+CalDAVRequest.php:    $this->method = $_SERVER['REQUEST_METHOD'];
+CalDAVRequest.php:      $this->content_type = (isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : null);
+CalDAVRequest.php:      if ( preg_match( '{^(\S+/\S+)\s*(;.*)?$}', $this->content_type, $matches ) ) {
+CalDAVRequest.php:        $this->content_type = $matches[1];
+CalDAVRequest.php:      if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' ) {
+CalDAVRequest.php:        if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
+CalDAVRequest.php:          dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
+CalDAVRequest.php:                                                 $request->method, $this->content_type );
+CalDAVRequest.php:          $this->content_type = 'text/xml';
+CalDAVRequest.php:    $this->user_agent = ((isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "Probably Mulberry"));
+CalDAVRequest.php:      $this->depth = $_SERVER['HTTP_DEPTH'];
+CalDAVRequest.php:      * is not present then a reasonable request-type-dependent default should be
+CalDAVRequest.php:      switch( $this->method ) {
+CalDAVRequest.php:          $this->depth = 'infinity';
+CalDAVRequest.php:          $this->depth = 0;
+CalDAVRequest.php:    if ( $this->depth == 'infinity' ) $this->depth = DEPTH_INFINITY;
+CalDAVRequest.php:    $this->depth = intval($this->depth);
+CalDAVRequest.php:    if ( isset($_SERVER['HTTP_DESTINATION']) ) $this->destination = $_SERVER['HTTP_DESTINATION'];
+CalDAVRequest.php:    $this->overwrite = ( isset($_SERVER['HTTP_OVERWRITE']) && ($_SERVER['HTTP_OVERWRITE'] == 'F') ? false : true ); // RFC4918, 9.8.4 says default True.
+CalDAVRequest.php:    * LOCK things use an "If" header to hold the lock in some cases, and "Lock-token" in others
+CalDAVRequest.php:    if ( isset($_SERVER['HTTP_IF']) ) $this->if_clause = $_SERVER['HTTP_IF'];
+CalDAVRequest.php:      $this->lock_token = $matches[1];
+CalDAVRequest.php:      $this->ticket = new DAVTicket($_GET['ticket']);
+CalDAVRequest.php:      $this->ticket = new DAVTicket($_SERVER['HTTP_TICKET']);
+CalDAVRequest.php:          $this->timeout = (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100);
+CalDAVRequest.php:        elseif ( strtolower(substr($v,0,7)) == 'second-' ) {
+CalDAVRequest.php:          $this->timeout = min( intval(substr($v,7)), (isset($c->maximum_lock_timeout) ? $c->maximum_lock_timeout : 86400 * 100) );
+CalDAVRequest.php:      if ( ! isset($this->timeout) || $this->timeout == 0 ) $this->timeout = (isset($c->default_lock_timeout) ? $c->default_lock_timeout : 900);
+CalDAVRequest.php:    $this->path = (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : "/");
+CalDAVRequest.php:    $this->path = rawurldecode($this->path);
+CalDAVRequest.php:    if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) {
+CalDAVRequest.php:      $this->path = $matches[1]. '/';
+CalDAVRequest.php:    // dbg_error_log( "caldav", "Sanitising path '%s'", $this->path );
+CalDAVRequest.php:    if ( preg_match( $bad_chars_regex, $this->path ) ) {
+CalDAVRequest.php:      $this->DoResponse( 400, translate("The calendar path contains illegal characters.") );
+CalDAVRequest.php:    if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path);
+CalDAVRequest.php:    $this->user_no = $session->user_no;
+CalDAVRequest.php:    $this->username = $session->username;
+CalDAVRequest.php:    if ( $session->user_no > 0 ) {
+CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('href', ConstructURL('/'.$session->username.'/') );
+CalDAVRequest.php:      $this->current_user_principal_url = new XMLElement('unauthenticated' );
+CalDAVRequest.php:    * we SHOULD return a Content-location header with the correction...
+CalDAVRequest.php:    *  - The exact request.
+CalDAVRequest.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+CalDAVRequest.php:    *  - The request URL truncated to the last '/'
+CalDAVRequest.php:    $params = array( ':exact_name' => $this->path );
+CalDAVRequest.php:    if ( !preg_match( '#/$#', $this->path ) ) {
+CalDAVRequest.php:      $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $this->path);
+CalDAVRequest.php:      $params[':trailing_slash_name'] = $this->path."/";
+CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+CalDAVRequest.php:      if ( $row->dav_name == $this->path."/" ) {
+CalDAVRequest.php:        $this->path = $row->dav_name;
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:      $this->collection_id = $row->collection_id;
+CalDAVRequest.php:      $this->collection_path = $row->dav_name;
+CalDAVRequest.php:      $this->collection_type = ($row->is_calendar == 't' ? 'calendar' : 'collection');
+CalDAVRequest.php:      $this->collection = $row;
+CalDAVRequest.php:      if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $matches ) ) {
+CalDAVRequest.php:        $this->collection_type = 'schedule-'. $matches[3]. 'box';
+CalDAVRequest.php:      $this->collection->type = $this->collection_type;
+CalDAVRequest.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->path, $matches ) ) {
+CalDAVRequest.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+CalDAVRequest.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+CalDAVRequest.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+CalDAVRequest.php:        $this->collection_id = $row->collection_id;
+CalDAVRequest.php:        $this->collection_path = $matches[1];
+CalDAVRequest.php:        $this->collection = $row;
+CalDAVRequest.php:        $this->collection->type = $this->collection_type;
+CalDAVRequest.php:    else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) {
+CalDAVRequest.php:      $this->collection_type = 'proxy';
+CalDAVRequest.php:      $this->_is_proxy_request = true;
+CalDAVRequest.php:      $this->proxy_type = $matches[3];
+CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+CalDAVRequest.php:        $this->path .= '/';
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a (proxy) collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:    else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_type = 'email';
+CalDAVRequest.php:      $this->collection_path = $this->path;
+CalDAVRequest.php:      $this->_is_principal = true;
+CalDAVRequest.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_path = $matches[1].'/';  // Enforce trailling '/'
+CalDAVRequest.php:      $this->collection_type = 'principal';
+CalDAVRequest.php:      $this->_is_principal = true;
+CalDAVRequest.php:      if ( $this->collection_path == $this->path."/" ) {
+CalDAVRequest.php:        $this->path .= '/';
+CalDAVRequest.php:        dbg_error_log( "caldav", "Path is actually a collection - sending Content-Location header." );
+CalDAVRequest.php:        header( "Content-Location: ".ConstructURL($this->path) );
+CalDAVRequest.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:        $this->depth = 0;
+CalDAVRequest.php:    else if ( $this->path == '/' ) {
+CalDAVRequest.php:      $this->collection_id = -1;
+CalDAVRequest.php:      $this->collection_path = '/';
+CalDAVRequest.php:      $this->collection_type = 'root';
+CalDAVRequest.php:    if ( $this->collection_path == $this->path ) $this->_is_collection = true;
+CalDAVRequest.php:    dbg_error_log( "caldav", " Collection '%s' is %d, type %s", $this->collection_path, $this->collection_id, $this->collection_type );
+CalDAVRequest.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->path, "options" => $this->options ) );
+CalDAVRequest.php:    if ( isset($this->principal->user_no) ) $this->user_no  = $this->principal->user_no;
+CalDAVRequest.php:    if ( isset($this->principal->username)) $this->username = $this->principal->username;
+CalDAVRequest.php:    if ( isset($this->principal->by_email) && $this->principal->by_email) $this->by_email = true;
+CalDAVRequest.php:    if ( isset($this->principal->principal_id)) $this->principal_id = $this->principal->principal_id;
+CalDAVRequest.php:    if ( $this->collection_type == 'principal' || $this->collection_type == 'email' || $this->collection_type == 'proxy' ) {
+CalDAVRequest.php:      $this->collection = $this->principal->AsCollection();
+CalDAVRequest.php:      if( $this->collection_type == 'proxy' ) {
+CalDAVRequest.php:        $this->collection = $this->principal->AsCollection();
+CalDAVRequest.php:        $this->collection->is_proxy = 't';
+CalDAVRequest.php:        $this->collection->type = 'proxy';
+CalDAVRequest.php:        $this->collection->proxy_type = $this->proxy_type;
+CalDAVRequest.php:        $this->collection->dav_displayname = sprintf('Proxy %s for %s', $this->proxy_type, $this->principal->username() );
+CalDAVRequest.php:    elseif( $this->collection_type == 'root' ) {
+CalDAVRequest.php:      $this->collection = (object) array(
+CalDAVRequest.php:                            'dav_etag' => md5($c->system_name),
+CalDAVRequest.php:                            'dav_displayname' => $c->system_name,
+CalDAVRequest.php:    $this->setPermissions();
+CalDAVRequest.php:    $this->supported_methods = array(
+CalDAVRequest.php:    if ( $this->IsCollection() ) {
+CalDAVRequest.php:      switch ( $this->collection_type ) {
+CalDAVRequest.php:          $this->supported_methods = array(
+CalDAVRequest.php:        case 'schedule-inbox':
+CalDAVRequest.php:        case 'schedule-outbox':
+CalDAVRequest.php:          $this->supported_methods = array_merge(
+CalDAVRequest.php:            $this->supported_methods,
+CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+CalDAVRequest.php:          $this->supported_methods['GET'] = '';
+CalDAVRequest.php:          $this->supported_methods['PUT'] = '';
+CalDAVRequest.php:          $this->supported_methods['HEAD'] = '';
+CalDAVRequest.php:          $this->supported_methods['MKCOL'] = '';
+CalDAVRequest.php:          $this->supported_methods['MKCALENDAR'] = '';
+CalDAVRequest.php:          $this->supported_methods['PROPPATCH'] = '';
+CalDAVRequest.php:          $this->supported_methods['BIND'] = '';
+CalDAVRequest.php:      $this->supported_methods = array_merge(
+CalDAVRequest.php:        $this->supported_methods,
+CalDAVRequest.php:    /**** comentado por motivos de incmpatibilidade do sync-collection
+CalDAVRequest.php:    $this->supported_reports = array(
+CalDAVRequest.php:      'DAV::principal-property-search' => '',
+CalDAVRequest.php:      'DAV::expand-property' => '',
+CalDAVRequest.php:      'DAV::sync-collection' => ''
+CalDAVRequest.php:    $this->supported_reports = array(
+CalDAVRequest.php:      'DAV::principal-property-search' => '',
+CalDAVRequest.php:      'DAV::expand-property' => ''
+CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_calendar ) {
+CalDAVRequest.php:      $this->supported_reports = array_merge(
+CalDAVRequest.php:        $this->supported_reports,
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+CalDAVRequest.php:    if ( isset($this->collection) && $this->collection->is_addressbook ) {
+CalDAVRequest.php:      $this->supported_reports = array_merge(
+CalDAVRequest.php:        $this->supported_reports,
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+CalDAVRequest.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+CalDAVRequest.php:    if ( isset($this->content_type) && preg_match( '#(application|text)/xml#', $this->content_type ) ) {
+CalDAVRequest.php:      $xml_parser = xml_parser_create_ns('UTF-8');
+CalDAVRequest.php:      $this->xml_tags = array();
+CalDAVRequest.php:      $rc = xml_parse_into_struct( $xml_parser, $this->raw_post, $this->xml_tags );
+CalDAVRequest.php:        $this->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml'), array( 'xmlns' => 'DAV:') ) );
+CalDAVRequest.php:      if ( count($this->xml_tags) ) {
+CalDAVRequest.php:        $this->xml_tags = null;
+CalDAVRequest.php:        dbg_error_log( "ERROR", "Incoming request sent content-type XML with no XML request body." );
+CalDAVRequest.php:    * Look out for If-None-Match or If-Match headers
+CalDAVRequest.php:      $this->etag_none_match = str_replace('"','',$_SERVER["HTTP_IF_NONE_MATCH"]);
+CalDAVRequest.php:      if ( $this->etag_none_match == '' ) unset($this->etag_none_match);
+CalDAVRequest.php:      $this->etag_if_match = str_replace('"','',$_SERVER["HTTP_IF_MATCH"]);
+CalDAVRequest.php:      if ( $this->etag_if_match == '' ) unset($this->etag_if_match);
+CalDAVRequest.php:    $this->user_no = $session->user_no;
+CalDAVRequest.php:    $this->username = $session->username;
+CalDAVRequest.php:    $this->principal_id = $session->principal_id;
+CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+CalDAVRequest.php:    $path_split = explode('/', $this->path );
+CalDAVRequest.php:    $this->username = $path_split[1];
+CalDAVRequest.php:    if ( $this->username == 'principals' ) $this->username = $path_split[3];
+CalDAVRequest.php:    if ( isset($this->options['allow_by_email']) && preg_match( '#/(\S+@\S+[.]\S+)/?$#', $this->path, $matches) ) {
+CalDAVRequest.php:      $this->by_email = $matches[1];
+CalDAVRequest.php:                          array(':email' => $this->by_email ) );
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $user = $qry->Fetch() ) {
+CalDAVRequest.php:        $this->user_no = $user->user_no;
+CalDAVRequest.php:        $this->username = $user->username;
+CalDAVRequest.php:        $this->principal_id = $user->principal_id;
+CalDAVRequest.php:    elseif( $user = getUserByName($this->username,'caldav',__LINE__,__FILE__)) {
+CalDAVRequest.php:      $this->principal = $user;
+CalDAVRequest.php:      $this->user_no = $user->user_no;
+CalDAVRequest.php:      $this->principal_id = $user->principal_id;
+CalDAVRequest.php:    if ( $this->path == '/' || $this->path == '' ) {
+CalDAVRequest.php:      $this->privileges = privilege_to_bits( array('read','read-free-busy','read-acl'));
+CalDAVRequest.php:    else if ( $session->AllowedTo("Admin") || $session->user_no == $this->user_no ) {
+CalDAVRequest.php:      $this->privileges = privilege_to_bits('all');
+CalDAVRequest.php:      dbg_error_log( "caldav", "Full permissions for %s", ( $session->user_no == $this->user_no ? "user accessing their own hierarchy" : "a systems administrator") );
+CalDAVRequest.php:      $this->privileges = 0;
+CalDAVRequest.php:      if ( $this->IsPublic() ) {
+CalDAVRequest.php:        $this->privileges = privilege_to_bits(array('read','read-free-busy'));
+CalDAVRequest.php:      else if ( isset($c->public_freebusy_url) && $c->public_freebusy_url ) {
+CalDAVRequest.php:        $this->privileges = privilege_to_bits('read-free-busy');
+CalDAVRequest.php:      $params = array( ':session_principal_id' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+CalDAVRequest.php:      if ( isset($this->by_email) && $this->by_email ) {
+CalDAVRequest.php:        $params[':request_principal_id'] = $this->principal_id;
+CalDAVRequest.php:        $params[':request_path'] = $this->path;
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) && $permission_result = $qry->Fetch() )
+CalDAVRequest.php:        $this->privileges |= bindec($permission_result->perm);
+CalDAVRequest.php:      dbg_error_log( 'caldav', 'Restricted permissions for user accessing someone elses hierarchy: %s', decbin($this->privileges) );
+CalDAVRequest.php:      if ( isset($this->ticket) && $this->ticket->MatchesPath($this->path) ) {
+CalDAVRequest.php:        $this->privileges |= $this->ticket->privileges();
+CalDAVRequest.php:        dbg_error_log( 'caldav', 'Applying permissions for ticket "%s" now: %s', $this->ticket->id(), decbin($this->privileges) );
+CalDAVRequest.php:    $this->permissions = array();
+CalDAVRequest.php:    $privs = bits_to_privilege($this->privileges);
+CalDAVRequest.php:      $this->permissions[$v] = $type;
+CalDAVRequest.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+CalDAVRequest.php:    if ( !isset($this->_locks_found) ) {
+CalDAVRequest.php:      $this->_locks_found = array();
+CalDAVRequest.php:      $qry->Exec('caldav',__LINE__,__FILE__);
+CalDAVRequest.php:      $qry = new AwlQuery($sql, array( ':dav_name' => $this->path, ':pattern_end_match' => ($this->IsInfiniteDepth() ? '' : '$') ) );
+CalDAVRequest.php:      if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+CalDAVRequest.php:        while( $lock_row = $qry->Fetch() ) {
+CalDAVRequest.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+CalDAVRequest.php:        $this->DoResponse(500,translate("Database Error"));
+CalDAVRequest.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+CalDAVRequest.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->path ) {
+CalDAVRequest.php:    if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) {
+CalDAVRequest.php:    if ( isset($this->path) ) return $this->path;
+CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return 'infinity';
+CalDAVRequest.php:    return $this->depth;
+CalDAVRequest.php:    if ( $this->IsInfiniteDepth() ) return '';
+CalDAVRequest.php:    if ( $this->depth == 0 ) return '$';
+CalDAVRequest.php:    if ( isset($this->_locks_found) && isset($this->_locks_found[$lock_token]) ) {
+CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+CalDAVRequest.php:    if ( $qry->Exec('caldav',__LINE__,__FILE__) ) {
+CalDAVRequest.php:      $lock_row = $qry->Fetch();
+CalDAVRequest.php:      $this->_locks_found = array( $lock_token => $lock_row );
+CalDAVRequest.php:      return $this->_locks_found[$lock_token];
+CalDAVRequest.php:      $this->DoResponse( 500, translate("Database Error") );
+CalDAVRequest.php:    if ( isset($this->lock_token) && $this->lock_token == $lock_token ) {
+CalDAVRequest.php:    if ( isset($this->if_clause) ) {
+CalDAVRequest.php:      dbg_error_log( "caldav", "Checking lock token '%s' against '%s'", $lock_token, $this->if_clause );
+CalDAVRequest.php:      $tokens = preg_split( '/[<>]/', $this->if_clause );
+CalDAVRequest.php:      @dbg_error_log( "caldav", "Invalid lock token '%s' - not in Lock-token (%s) or If headers (%s) ", $lock_token, $this->lock_token, $this->if_clause );
+CalDAVRequest.php:    if ( !isset($this->_locks_found) && false === $this->IsLocked() ) return false;
+CalDAVRequest.php:    if ( isset($this->_locks_found[$lock_token]) ) return $this->_locks_found[$lock_token];
+CalDAVRequest.php:    if ( $existing_lock = $this->IsLocked() ) { // NOTE Assignment in if() is expected here.
+CalDAVRequest.php:      dbg_error_log( "caldav", "There is a lock on '%s'", $this->path);
+CalDAVRequest.php:      if ( ! $this->ValidateLockToken($existing_lock) ) {
+CalDAVRequest.php:        $lock_row = $this->GetLockRow($existing_lock);
+CalDAVRequest.php:        * Already locked - deny it
+CalDAVRequest.php:            new XMLElement( 'href',   $lock_row->dav_name ),
+CalDAVRequest.php:        if ( $lock_row->dav_name != $this->path ) {
+CalDAVRequest.php:              new XMLElement( 'href',   $this->path ),
+CalDAVRequest.php:        $xmldoc = $response->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+CalDAVRequest.php:        $this->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:  * Coerces the Content-type of the request into something valid/appropriate
+CalDAVRequest.php:    if ( isset($this->content_type) ) {
+CalDAVRequest.php:      $type = explode( '/', $this->content_type, 2);
+CalDAVRequest.php:    /** Null (or peculiar) content-type supplied so we have to try and work it out... */
+CalDAVRequest.php:    $first_word = trim(substr( $this->raw_post, 0, 30));
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/xml"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/xml';
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/calendar"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/calendar';
+CalDAVRequest.php:        dbg_error_log( 'LOG WARNING', 'Application sent content-type of "%s" instead of "text/vcard"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)') );
+CalDAVRequest.php:        $this->content_type = 'text/vcard';
+CalDAVRequest.php:        dbg_error_log( 'LOG NOTICE', 'Unusual content-type of "%s" and first word of content is "%s"',
+CalDAVRequest.php:                                        (isset($this->content_type)?$this->content_type:'(null)'), $first_word );
+CalDAVRequest.php:    if ( !isset($this->_is_collection) ) {
+CalDAVRequest.php:      $this->_is_collection = preg_match( '#/$#', $this->path );
+CalDAVRequest.php:    return $this->_is_collection;
+CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+CalDAVRequest.php:    return $this->collection->is_calendar == 't';
+CalDAVRequest.php:    if ( !$this->IsCollection() || !isset($this->collection) ) return false;
+CalDAVRequest.php:    return $this->collection->is_addressbook == 't';
+CalDAVRequest.php:    if ( !isset($this->_is_principal) ) {
+CalDAVRequest.php:      $this->_is_principal = preg_match( '#^/[^/]+/$#', $this->path );
+CalDAVRequest.php:    return $this->_is_principal;
+CalDAVRequest.php:    if ( !isset($this->_is_proxy_request) ) {
+CalDAVRequest.php:      $this->_is_proxy_request = preg_match( '#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->path );
+CalDAVRequest.php:    return $this->_is_proxy_request;
+CalDAVRequest.php:    return ($this->depth == DEPTH_INFINITY);
+CalDAVRequest.php:    return $this->collection_id;
+CalDAVRequest.php:    if ( $privs === null ) $privs = $this->supported_privileges;
+CalDAVRequest.php:      $reply->NSElement($privilege,$k);
+CalDAVRequest.php:        dbg_error_log( 'caldav', '"%s" is a container of sub-privileges.', $k );
+CalDAVRequest.php:        $privset = array_merge($privset, $this->BuildSupportedPrivileges($reply,$v));
+CalDAVRequest.php:      $privileges[] = new XMLElement('supported-privilege',$privset);
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:  * | REPORT     | DAV:read or CALDAV:read-free-busy (on all referenced |
+CalDAVRequest.php:  * +------------+------------------------------------------------------+
+CalDAVRequest.php:    dbg_error_log('caldav', 'Checking whether "%s" is allowed to "%s"', $session->username, $activity);
+CalDAVRequest.php:    if ( isset($this->permissions['all']) ) return true;
+CalDAVRequest.php:      case "CALDAV:schedule-send-freebusy":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:      case "CALDAV:schedule-send-invite":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:      case "CALDAV:schedule-send-reply":
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:        return isset($this->permissions['read']) || isset($this->permissions['urn:ietf:params:xml:ns:caldav:read-free-busy']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['unbind']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-properties']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['write-content']);
+CalDAVRequest.php:        return isset($this->permissions['write']) || isset($this->permissions['bind']);
+CalDAVRequest.php:        if ( !isset($this->permissions['write']) || !isset($this->permissions['bind']) ) return false;
+CalDAVRequest.php:        if ( $this->is_principal ) return false;
+CalDAVRequest.php:        if ( $this->path == '/' ) return false;
+CalDAVRequest.php://             decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+CalDAVRequest.php:        return (($this->privileges & $test_bits) > 0 );
+CalDAVRequest.php:    return $this->privileges;
+CalDAVRequest.php://              decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+CalDAVRequest.php:    return ($this->privileges & $test_bits) > 0;
+CalDAVRequest.php:  * Sometimes it's a perfectly formed request, but we just don't do that :-(
+CalDAVRequest.php:        dbg_error_log("ERROR", " %s: Support for $v:$k properties is not implemented yet", $this->method );
+CalDAVRequest.php:        $badprops->NewElement(strtolower($k),false,array("xmlns" => strtolower($v)));
+CalDAVRequest.php:      $this->XMLResponse( 422, $error );
+CalDAVRequest.php:  * Send a need-privileges error response.  This function will only return
+CalDAVRequest.php:      if ( $this->HavePrivilegeTo($privileges) ) return;
+CalDAVRequest.php:      $href = $this->path;
+CalDAVRequest.php:    $privnodes = array( $reply->href(ConstructURL($href)), new XMLElement( 'privilege' ) );
+CalDAVRequest.php:    $reply->NSElement( $privnodes[1], $privileges[0] );
+CalDAVRequest.php:    $xml = new XMLElement( 'need-privileges', new XMLElement( 'resource', $privnodes) );
+CalDAVRequest.php:    $xmldoc = $reply->Render('error',$xml);
+CalDAVRequest.php:    $this->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:    $xmldoc = sprintf('<?xml version="1.0" encoding="utf-8" ?>
+CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:    $this->DoResponse( 400, $text );
+CalDAVRequest.php:    $xmldoc = $xmltree->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+CalDAVRequest.php:    $this->DoResponse( $status, $xmldoc, 'text/xml; charset="utf-8"' );
+CalDAVRequest.php:  * Utility function we call when we have a simple status-based response to
+CalDAVRequest.php:  function DoResponse( $status, $message="", $content_type="text/plain; charset=\"utf-8\"" ) {
+CalDAVRequest.php:    @header( sprintf("X-DAViCal-Version: DAViCal/%d.%d.%d; DB/%d.%d.%d", $c->code_major, $c->code_minor, $c->code_patch, $c->schema_major, $c->schema_minor, $c->schema_patch) );
+CalDAVRequest.php:    @header( "Content-type: ".$content_type );
+CalDAVRequest.php:    if ( (isset($c->dbg['ALL']) && $c->dbg['ALL']) || (isset($c->dbg['response']) && $c->dbg['response']) || $status > 399 ) {
+CalDAVRequest.php:        dbg_error_log( "LOG headers", "-->%s", $v );
+CalDAVRequest.php:        dbg_error_log( "LOG response", "-->%s", $v );
+CalDAVRequest.php:    header( "Content-Length: ".strlen($message) );
+CalDAVRequest.php:    if ( isset($c->dbg['caldav']) && $c->dbg['caldav'] ) {
+CalDAVRequest.php:      dbg_error_log("caldav", "Status: %d, Message: %s, User: %d, Path: %s", $status, $message, $session->user_no, $this->path);
+CalDAVRequest.php:    if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+CalDAVRequest.php:      $script_time = microtime(true) - $c->script_start_time;
+CalDAVRequest.php:                         $this->method, $status, $script_time, $c->total_query_time, $this->path);
+check_UTF8.php: * http://iki.fi/hsivonen/php-utf8/
+check_UTF8.php: * Version 1.0, 2003-05-30
+check_UTF8.php: * Takes an UTF-8 string and returns an array of ints representing the
+check_UTF8.php: * Returns false if the input string isn't a valid UTF-8 octet sequence.
+check_UTF8.php:      // When mState is zero we expect either a US-ASCII character or a
+check_UTF8.php:      // multi-octet sequence.
+check_UTF8.php:        // US-ASCII, pass straight through.
+check_UTF8.php:         * (b) outside the Unicode range of 0-0x10FFFF.
+check_UTF8.php:        /* Current octet is neither in the US-ASCII range nor a legal first
+check_UTF8.php:         * octet of a multi-octet sequence.
+check_UTF8.php:      // When mState is non-zero, we expect a continuation of the multi-octet
+check_UTF8.php:        $shift = ($mState - 1) * 6;
+check_UTF8.php:        if (0 == --$mState) {
+check_UTF8.php:          /* End of the multi-octet sequence. mUcs4 now contains the final
+check_UTF8.php:          // From Unicode 3.1, non-shortest form is illegal
+check_UTF8.php:         * Incomplete multi-octet sequence.
+check_UTF8.php: * a UTF-8 string. Astral planes are supported ie. the ints in the
+check_UTF8.php:      // nop -- zap the BOM
+davical_configuration_missing.php:include("page-header.php");
+davical_configuration_missing.php:<p>There is no configuration file present in <b>/etc/davical/$_SERVER[SERVER_NAME]-conf.php</b> so
+davical_configuration_missing.php:<p>Well, you're seeing this! At least you have DAViCal <i>installed</i> :-) You also have Apache and PHP working
+davical_configuration_missing.php:   or later so keep downloading new versions and trying again.  Or make some guesses.  Or bug Andrew :-)</p>
+davical_configuration_missing.php://  \$c->domain_name  = 'davical.example.com';
+davical_configuration_missing.php://  \$c->sysabbr     = 'davical';
+davical_configuration_missing.php://  \$c->system_name = 'DAViCal CalDAV Server';
+davical_configuration_missing.php:  \$c->admin_email  = 'admin@example.com';
+davical_configuration_missing.php:  \$c->pg_connect[] = 'dbname=davical user=davical_app';
+davical_configuration_missing.php:configuring someone for the admin e-mail is a really good idea.</p>
+davical_configuration_missing.php:include("page-footer.php");
+DAViCalSession.php:// the records related to the logged-on user...  (the where clause gets added).
+DAViCalSession.php:    $this->Session($sid);
+DAViCalSession.php:    if ( !isset($u->principal_id) ) {
+DAViCalSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+DAViCalSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+DAViCalSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+DAViCalSession.php:        $u = $qry->Fetch();
+DAViCalSession.php:    $this->GetRoles();
+DAViCalSession.php:    if ( function_exists('awl_set_locale') && isset($this->locale) && $this->locale != '' ) {
+DAViCalSession.php:      awl_set_locale($this->locale);
+DAViCalSession.php:    $this->roles = array();
+DAViCalSession.php:    $sql = 'SELECT role_name FROM roles JOIN role_member ON roles.role_no=role_member.role_no WHERE user_no = '.$this->user_no;
+DAViCalSession.php:    if ( $qry->Exec('DAViCalSession') && $qry->rows() > 0 ) {
+DAViCalSession.php:      while( $role = $qry->Fetch() ) {
+DAViCalSession.php:        $this->roles[$role->role_name] = 1;
+DAViCalSession.php:    if ( (isset($c->restrict_admin_domain) && $c->restrict_admin_domain != $current_domain)
+DAViCalSession.php:      || (isset($c->restrict_admin_port) && $c->restrict_admin_port != $_SERVER['SERVER_PORT'] ) ) {
+DAViCalSession.php:    if ( isset($c->restrict_admin_roles) && $roles == '' ) $roles = $c->restrict_admin_roles;
+DAViCalSession.php:    if ( $this->logged_in && $roles == '' ) return;
+DAViCalSession.php:    if ( ! $this->logged_in ) {
+DAViCalSession.php:      $c->messages[] = i18n('You must log in to use this system.');
+DAViCalSession.php:      include_once('page-header.php');
+DAViCalSession.php:        if ( $this->login_failed ) {
+DAViCalSession.php:          $c->messages[] = i18n('Invalid user name or password.');
+DAViCalSession.php:                  .' '.translate($c->system_name).' '
+DAViCalSession.php:        echo '<p>'.translate('If you would like to request access, please e-mail').' '.$c->admin_email."</p>\n";
+DAViCalSession.php:        echo $this->RenderLoginPanel();
+DAViCalSession.php:        if ( $this->AllowedTo($v) ) return;
+DAViCalSession.php:      $c->messages[] = i18n('You are not authorised to use this function.');
+DAViCalSession.php:      include_once('page-header.php');
+DAViCalSession.php:    include('page-footer.php');
+DAViCalSession.php:$session->_CheckLogin();
+DAVResource.php:  * @var True if this resource is a principal-URL
+DAVResource.php:    $this->exists        = null;
+DAVResource.php:    $this->bound_from    = null;
+DAVResource.php:    $this->dav_name      = null;
+DAVResource.php:    $this->unique_tag    = null;
+DAVResource.php:    $this->resource      = null;
+DAVResource.php:    $this->collection    = null;
+DAVResource.php:    $this->principal     = null;
+DAVResource.php:    $this->parent        = null;
+DAVResource.php:    $this->resourcetypes = null;
+DAVResource.php:    $this->contenttype   = null;
+DAVResource.php:    $this->privileges    = null;
+DAVResource.php:    $this->dead_properties   = null;
+DAVResource.php:    $this->supported_methods = null;
+DAVResource.php:    $this->supported_reports = null;
+DAVResource.php:    $this->_is_collection    = false;
+DAVResource.php:    $this->_is_principal     = false;
+DAVResource.php:    $this->_is_calendar      = false;
+DAVResource.php:    $this->_is_binding       = false;
+DAVResource.php:    $this->_is_addressbook   = false;
+DAVResource.php:    $this->_is_proxy_request = false;
+DAVResource.php:      $this->FromRow($parameters);
+DAVResource.php:        $this->FromPath($parameters['path']);
+DAVResource.php:      $this->FromPath($parameters);
+DAVResource.php:    $this->exists = true;
+DAVResource.php:    $this->dav_name = $row->dav_name;
+DAVResource.php:    $this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
+DAVResource.php:    $this->_is_collection = preg_match( '{/$}', $this->dav_name );
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+DAVResource.php:      $this->collection = (object) array();
+DAVResource.php:      $this->resource_id = $row->collection_id;
+DAVResource.php:      $this->_is_principal = preg_match( '{^/[^/]+/$}', $this->dav_name );
+DAVResource.php:      if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
+DAVResource.php:        $this->collection->dav_name = $matches[1].'/';
+DAVResource.php:        $this->collection->type = 'principal_link';
+DAVResource.php:        $this->_is_principal = true;
+DAVResource.php:      $this->resource = (object) array();
+DAVResource.php:      if ( isset($row->dav_id) ) $this->resource_id = $row->dav_id;
+DAVResource.php:    dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->dav_name, ($this->_is_collection?'':' not') );
+DAVResource.php:      if ( $this->_is_collection )
+DAVResource.php:        $this->collection->{$k} = $v;
+DAVResource.php:        $this->resource->{$k} = $v;
+DAVResource.php:          $this->{$k} = $v;
+DAVResource.php:          $this->unique_tag = '"'.$v.'"';
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      if ( !isset( $this->collection->type ) || $this->collection->type == 'collection' ) {
+DAVResource.php:        if ( $this->_is_principal )
+DAVResource.php:          $this->collection->type = 'principal';
+DAVResource.php:        else if ( $row->is_calendar == 't' ) {
+DAVResource.php:          $this->collection->type = 'calendar';
+DAVResource.php:        else if ( $row->is_addressbook == 't' ) {
+DAVResource.php:          $this->collection->type = 'addressbook';
+DAVResource.php:        else if ( isset($row->is_proxy) && $row->is_proxy == 't' ) {
+DAVResource.php:          $this->collection->type = 'proxy';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:        else if ( $this->dav_name == '/' )
+DAVResource.php:          $this->collection->type = 'root';
+DAVResource.php:          $this->collection->type = 'collection';
+DAVResource.php:      $this->_is_calendar     = ($this->collection->is_calendar == 't');
+DAVResource.php:      $this->_is_addressbook  = ($this->collection->is_addressbook == 't');
+DAVResource.php:      $this->_is_proxy_request= ($this->collection->type == 'proxy');
+DAVResource.php:      if ( $this->_is_principal && !isset($this->resourcetypes) ) {
+DAVResource.php:        $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+DAVResource.php:      else if ( $this->_is_proxy_request ) {
+DAVResource.php:        $this->resourcetypes  = $this->collection->resourcetypes;
+DAVResource.php:      if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+DAVResource.php:      $this->resourcetypes = '';
+DAVResource.php:      if ( isset($this->resource->caldav_data) ) {
+DAVResource.php:        if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) $this->contenttype = 'text/calendar';
+DAVResource.php:        $this->resource->displayname = $this->resource->summary;
+DAVResource.php:    $this->dav_name = DeconstructURL($inpath);
+DAVResource.php:    $this->FetchCollection();
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      if ( $this->_is_principal || $this->collection->type == 'principal' )   $this->FetchPrincipal();
+DAVResource.php:      $this->FetchResource();
+DAVResource.php:               $this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
+DAVResource.php:    *    - also discussed at more length in 5.2
+DAVResource.php:    *  - The exact request.
+DAVResource.php:    *  - If the exact request, doesn't end in '/', then the request URL with a '/' appended
+DAVResource.php:    *  - The request URL truncated to the last '/'
+DAVResource.php:    dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->dav_name );
+DAVResource.php:    $this->collection = (object) array(
+DAVResource.php:      'collection_id' => -1,
+DAVResource.php:    $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:    if ( !preg_match( '#/$#', $this->dav_name ) ) {
+DAVResource.php:      $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+DAVResource.php:      $params[':plus_slash']  = $this->dav_name.'/';
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:      $this->collection = $row;
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      if ( $row->is_calendar == 't' )
+DAVResource.php:        $this->collection->type = 'calendar';
+DAVResource.php:      else if ( $row->is_addressbook == 't' )
+DAVResource.php:        $this->collection->type = 'addressbook';
+DAVResource.php:      else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:        $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:        $this->collection->type = 'collection';
+DAVResource.php:    else if ( preg_match( '{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->dav_name, $matches ) ) {
+DAVResource.php:      // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it
+DAVResource.php:      $this->collection_type = 'schedule-'. $matches[4]. 'box';
+DAVResource.php:      $params[':resourcetypes'] = sprintf('<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
+DAVResource.php:      $qry->Exec('DAVResource');
+DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:        $this->collection = $row;
+DAVResource.php:        $this->collection->exists = true;
+DAVResource.php:        $this->collection->type = $this->collection_type;
+DAVResource.php:    else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
+DAVResource.php:      $this->collection->type = 'proxy';
+DAVResource.php:      $this->_is_proxy_request = true;
+DAVResource.php:      $this->proxy_type = $matches[3];
+DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+DAVResource.php:      $this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      $this->collection->parent_container = $matches[1] . '/';
+DAVResource.php:    else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches)
+DAVResource.php:           || preg_match( '#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
+DAVResource.php:      $this->_is_principal = true;
+DAVResource.php:      $this->FetchPrincipal();
+DAVResource.php:    else if ( $this->dav_name == '/' ) {
+DAVResource.php:      $this->collection->dav_name = '/';
+DAVResource.php:      $this->collection->type = 'root';
+DAVResource.php:      $this->collection->exists = true;
+DAVResource.php:      $this->collection->displayname = $c->system_name;
+DAVResource.php:      $this->collection->default_privileges = (1 | 16 | 32);
+DAVResource.php:      $this->collection->parent_container = '/';
+DAVResource.php:      $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+DAVResource.php:      if ( !preg_match( '#/$#', $this->dav_name ) ) {
+DAVResource.php:        $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
+DAVResource.php:        $params[':plus_slash']  = $this->dav_name.'/';
+DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
+DAVResource.php:        $this->collection = $row;
+DAVResource.php:        $this->collection->exists = true;
+DAVResource.php:        $this->_is_binding = true;
+DAVResource.php:        $this->collection->parent_set = $row->parent_container;
+DAVResource.php:        $this->collection->parent_container = $row->bind_parent_container;
+DAVResource.php:        $this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
+DAVResource.php:        $this->collection->bound_from = $row->dav_name;
+DAVResource.php:        $this->collection->dav_name = $row->bound_to;
+DAVResource.php:        if ( isset($row->access_ticket_id) ) {
+DAVResource.php:          if ( !isset($this->tickets) ) $this->tickets = array();
+DAVResource.php:          $this->tickets[] = new DAVTicket($row->access_ticket_id);
+DAVResource.php:        if ( $row->is_calendar == 't' )
+DAVResource.php:          $this->collection->type = 'calendar';
+DAVResource.php:        else if ( $row->is_addressbook == 't' )
+DAVResource.php:          $this->collection->type = 'addressbook';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->collection->type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:          $this->collection->type = 'collection';
+DAVResource.php:        dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
+DAVResource.php:        $this->collection->exists = false;
+DAVResource.php:        $this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
+DAVResource.php:    @dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
+DAVResource.php:    $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
+DAVResource.php:    if ( $this->_is_collection ) {
+DAVResource.php:      $this->dav_name = $this->collection->dav_name;
+DAVResource.php:      $this->resource_id = $this->collection->collection_id;
+DAVResource.php:      $this->_is_calendar    = ($this->collection->type == 'calendar');
+DAVResource.php:      $this->_is_addressbook = ($this->collection->type == 'addressbook');
+DAVResource.php:      $this->contenttype = 'httpd/unix-directory';
+DAVResource.php:      if ( !isset($this->exists) && isset($this->collection->exists) ) {
+DAVResource.php:        $this->exists = $this->collection->exists;
+DAVResource.php:      if ( $this->exists ) {
+DAVResource.php:        if ( isset($this->collection->dav_etag) ) $this->unique_tag = '"'.$this->collection->dav_etag.'"';
+DAVResource.php:        if ( isset($this->collection->created) )  $this->created = $this->collection->created;
+DAVResource.php:        if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
+DAVResource.php:        if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
+DAVResource.php:        if ( !isset($this->parent) ){ 
+DAVResource.php:            $this->FetchParentContainer();
+DAVResource.php:        $this->user_no = $this->parent->GetProperty('user_no');
+DAVResource.php:      if ( isset($this->collection->resourcetypes) )
+DAVResource.php:        $this->resourcetypes = $this->collection->resourcetypes;
+DAVResource.php:        $this->resourcetypes = '<DAV::collection/>';
+DAVResource.php:        if ( $this->_is_principal )   $this->resourcetypes .= '<DAV::principal/>';
+DAVResource.php:        if ( $this->_is_addressbook ) $this->resourcetypes .= '<urn:ietf:params:xml:ns:carddav:addressbook/>';
+DAVResource.php:        if ( $this->_is_calendar )    $this->resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+DAVResource.php:    if ( isset($this->principal) ) return;
+DAVResource.php:    $this->principal = new CalDAVPrincipal( array( "path" => $this->bound_from() ) );
+DAVResource.php:    if ( $this->_is_principal && $this->principal->Exists() ) {
+DAVResource.php:      $this->exists = true;
+DAVResource.php:      $this->unique_tag = $this->principal->dav_etag;
+DAVResource.php:      $this->created = $this->principal->created;
+DAVResource.php:      $this->modified = $this->principal->modified;
+DAVResource.php:      $this->resourcetypes = '<DAV::collection/><DAV::principal/>';
+DAVResource.php:      $this->resource_id = $this->principal->principal_id;
+DAVResource.php:      $this->collection = $this->principal->AsCollection();
+DAVResource.php:      $this->user_no = $this->principal->user_no;
+DAVResource.php:    elseif ( $this->_is_principal ) {
+DAVResource.php:      $this->exists = false;
+DAVResource.php:      $this->collection->dav_name = $this->dav_name;
+DAVResource.php:      $this->collection->type = 'principal';
+DAVResource.php:    if ( isset($this->exists) ) return;   // True or false, we've got what we can already
+DAVResource.php:    if ( $this->_is_collection ) return;   // We have all we're going to read
+DAVResource.php:    $params = array( ':dav_name' => $this->bound_from() );
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+DAVResource.php:      $this->exists = true;
+DAVResource.php:      $this->resource = $qry->Fetch();
+DAVResource.php:      $this->unique_tag = $this->resource->dav_etag;
+DAVResource.php:      $this->created = $this->resource->created;
+DAVResource.php:      $this->modified = $this->resource->modified;
+DAVResource.php:      $this->resource_id = $this->resource->dav_id;
+DAVResource.php:      if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
+DAVResource.php:        $this->contenttype = 'text/calendar';
+DAVResource.php:      $this->resourcetypes = '';
+DAVResource.php:      $this->exists = false;
+DAVResource.php:    if ( isset($this->dead_properties) ) return;
+DAVResource.php:    $this->dead_properties = array();
+DAVResource.php:    $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
+DAVResource.php:    if ( $qry->Exec('DAVResource') ) {
+DAVResource.php:      while ( $property = $qry->Fetch() ) {
+DAVResource.php:        $this->dead_properties[$property->property_name] = $property->property_value;
+DAVResource.php:    if ( $this->dav_name == '/' ) return null;
+DAVResource.php:    if ( !isset($this->parent) ) {
+DAVResource.php:      if ( $this->_is_collection ) {
+DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+DAVResource.php:        $this->parent = new DAVResource( $this->parent_path() );
+DAVResource.php:        dbg_error_log( 'DAVResource', 'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
+DAVResource.php:        $this->parent = new DAVResource($this->collection->dav_name);
+DAVResource.php:    return $this->parent;
+DAVResource.php:    if ( $this->dav_name == '/' || $this->dav_name == '' ) {
+DAVResource.php:      $this->privileges = (1 | 16 | 32); // read + read-acl + read-current-user-privilege-set
+DAVResource.php:    if ( $session->AllowedTo('Admin') ) {
+DAVResource.php:      $this->privileges = privilege_to_bits('all');
+DAVResource.php:    if ( $this->IsPrincipal() ) {
+DAVResource.php:      if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:      $this->privileges = $this->principal->Privileges();
+DAVResource.php:      dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing "%s"', $this->privileges, $this->principal->username() );
+DAVResource.php:    if ( ! isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:    $this->privileges = 0;
+DAVResource.php:    if ( !isset($this->collection->path_privs) ) {
+DAVResource.php:      if ( !isset($this->parent) ) $this->FetchParentContainer();
+DAVResource.php:      $this->collection->path_privs = $this->parent->Privileges();
+DAVResource.php:      $this->collection->user_no = $this->parent->GetProperty('user_no');
+DAVResource.php:      $this->collection->principal_id = $this->parent->GetProperty('principal_id');
+DAVResource.php:    $this->privileges = $this->collection->path_privs;
+DAVResource.php:    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+DAVResource.php:                       decbin($this->privileges), $session->username, $this->dav_name() );
+DAVResource.php:    if ( isset($request->ticket) && $request->ticket->MatchesPath($this->bound_from()) ) {
+DAVResource.php:      $this->privileges |= $request->ticket->privileges();
+DAVResource.php:      dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
+DAVResource.php:    if ( isset($this->tickets) ) {
+DAVResource.php:      if ( !isset($this->resource_id) ) $this->FetchResource();
+DAVResource.php:      foreach( $this->tickets AS $k => $ticket ) {
+DAVResource.php:        if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
+DAVResource.php:          $this->privileges |= $ticket->privileges();
+DAVResource.php:          dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
+DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:    return $this->privileges;
+DAVResource.php:    if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:        $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
+DAVResource.php:      return ($this->privileges & $test_bits) > 0;
+DAVResource.php:      return ($this->privileges & $test_bits) == $test_bits;
+DAVResource.php:    if ( $this->HavePrivilegeTo($privilege, $any) ) return;
+DAVResource.php:    $request->NeedPrivilege( $privilege, $this->dav_name );
+DAVResource.php:      if ( !isset($this->privileges) ) $this->FetchPrivileges();
+DAVResource.php:      $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type : null ) );
+DAVResource.php:    if ( isset($this->supported_methods) ) return $this->supported_methods;
+DAVResource.php:    $this->supported_methods = array(
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:/*      if ( $this->IsPrincipal() ) {
+DAVResource.php:        $this->supported_methods['MKCALENDAR'] = '';
+DAVResource.php:        $this->supported_methods['MKCOL'] = '';
+DAVResource.php:      switch ( $this->collection->type ) {
+DAVResource.php:          $this->supported_methods = array(
+DAVResource.php:        case 'schedule-outbox':
+DAVResource.php:          $this->supported_methods = array_merge(
+DAVResource.php:            $this->supported_methods,
+DAVResource.php:        case 'schedule-inbox':
+DAVResource.php:          $this->supported_methods['GET'] = '';
+DAVResource.php:          $this->supported_methods['PUT'] = '';
+DAVResource.php:          $this->supported_methods['HEAD'] = '';
+DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+DAVResource.php:          $this->supported_methods['MKTICKET'] = '';
+DAVResource.php:          $this->supported_methods['DELTICKET'] = '';
+DAVResource.php:          $this->supported_methods['GET'] = '';
+DAVResource.php:          $this->supported_methods['PUT'] = '';
+DAVResource.php:          $this->supported_methods['HEAD'] = '';
+DAVResource.php:          $this->supported_methods['MKCOL'] = '';
+DAVResource.php:          $this->supported_methods['MKCALENDAR'] = '';
+DAVResource.php:          $this->supported_methods['PROPPATCH'] = '';
+DAVResource.php:      $this->supported_methods = array_merge(
+DAVResource.php:        $this->supported_methods,
+DAVResource.php:    return $this->supported_methods;
+DAVResource.php:    if ( !isset($this->supported_methods) ) $this->FetchSupportedMethods();
+DAVResource.php:    foreach( $this->supported_methods AS $k => $v ) {
+DAVResource.php:      $methods[] = new XMLElement( 'supported-method', null, array('name' => $k) );
+DAVResource.php:    if ( isset($this->supported_reports) ) return $this->supported_reports;
+DAVResource.php:   /************* comentado por incompatibilidade do sync-collection 
+DAVResource.php:    $this->supported_reports = array(
+DAVResource.php:      'DAV::principal-property-search' => '',
+DAVResource.php:      'DAV::principal-search-property-set' => '',
+DAVResource.php:      'DAV::expand-property' => '',
+DAVResource.php:      'DAV::sync-collection' => ''
+DAVResource.php:    $this->supported_reports = array(
+DAVResource.php:      'DAV::principal-property-search' => '',
+DAVResource.php:      'DAV::principal-search-property-set' => '',
+DAVResource.php:      'DAV::expand-property' => ''
+DAVResource.php:    if ( !isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:    if ( $this->collection->is_calendar ) {
+DAVResource.php:      $this->supported_reports = array_merge(
+DAVResource.php:        $this->supported_reports,
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-query' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:calendar-multiget' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:caldav:free-busy-query' => ''
+DAVResource.php:    if ( $this->collection->is_addressbook ) {
+DAVResource.php:      $this->supported_reports = array_merge(
+DAVResource.php:        $this->supported_reports,
+DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-query' => '',
+DAVResource.php:          'urn:ietf:params:xml:ns:carddav:addressbook-multiget' => ''
+DAVResource.php:    return $this->supported_reports;
+DAVResource.php:    if ( !isset($this->supported_reports) ) $this->FetchSupportedReports();
+DAVResource.php:    foreach( $this->supported_reports AS $k => $v ) {
+DAVResource.php:      $reply->NSElement($report, $k );
+DAVResource.php:      $reports[] = new XMLElement('supported-report', $report );
+DAVResource.php:    if ( isset($this->access_tickets) ) return;
+DAVResource.php:    $this->access_tickets = array();
+DAVResource.php:        EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds,
+DAVResource.php:    $params = array(':collection_id' => $this->collection->collection_id, ':scan_depth' => $c->permission_scan_depth);
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:      if ( !isset($this->exists) ) $this->FetchResource();
+DAVResource.php:      $params[':dav_id'] = $this->resource->dav_id;
+DAVResource.php:    if ( isset($this->exists) && !$this->exists ) return;
+DAVResource.php:    if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
+DAVResource.php:      while( $ticket = $qry->Fetch() ) {
+DAVResource.php:        $this->access_tickets[] = $ticket;
+DAVResource.php:  * If the current user does not have DAV::read-acl privilege on this resource they
+DAVResource.php:    if ( !isset($this->access_tickets) ) $this->FetchTickets();
+DAVResource.php:    $show_all = $this->HavePrivilegeTo('DAV::read-acl');
+DAVResource.php:    foreach( $this->access_tickets AS $meh => $trow ) {
+DAVResource.php:      if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) ) continue;
+DAVResource.php:      dbg_error_log( 'DAVResource', ':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
+DAVResource.php:      $ticket = new XMLElement( $reply->Tag( 'ticketinfo', 'http://www.xythos.com/namespaces/StorageServer', 'TKT' ) );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf( 'Seconds-%d', $trow->seconds) : 'infinity') );
+DAVResource.php:      $reply->NSElement($ticket, 'http://www.xythos.com/namespaces/StorageServer:visits', 'infinity' );
+DAVResource.php:      foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
+DAVResource.php:        $privs[] = $reply->NewXMLElement($v);
+DAVResource.php:      $reply->NSElement($ticket, 'DAV::privilege', $privs );
+DAVResource.php:  * that.  At present we only maintain permissions on a per-collection basis though.
+DAVResource.php:    if ( !isset($this->_locks_found) ) {
+DAVResource.php:      $this->_locks_found = array();
+DAVResource.php:      $qry = new AwlQuery($sql, array( ':this_path' => $this->dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
+DAVResource.php:      if ( $qry->Exec('DAVResource',__LINE__,__FILE__) ) {
+DAVResource.php:        while( $lock_row = $qry->Fetch() ) {
+DAVResource.php:          $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
+DAVResource.php:        $this->DoResponse(500,i18n("Database Error"));
+DAVResource.php:    foreach( $this->_locks_found AS $lock_token => $lock_row ) {
+DAVResource.php:      if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
+DAVResource.php:    return $this->_is_collection;
+DAVResource.php:    return $this->_is_collection && $this->_is_principal;
+DAVResource.php:    return $this->_is_collection && $this->_is_calendar;
+DAVResource.php:    if ( $this->_is_collection && preg_match( '{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
+DAVResource.php:    return $this->_is_collection && $this->_is_addressbook;
+DAVResource.php:    return $this->_is_binding;
+DAVResource.php:    if ( ! isset($this->exists) ) {
+DAVResource.php:      if ( $this->IsPrincipal() ) {
+DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:        $this->exists = $this->principal->Exists();
+DAVResource.php:      else if ( ! $this->IsCollection() ) {
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php://    dbg_error_log('DAVResource',' Checking whether "%s" exists.  It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
+DAVResource.php:    return $this->exists;
+DAVResource.php:    if ( $this->collection->dav_name != $this->dav_name ) {
+DAVResource.php:      return $this->collection->exists;
+DAVResource.php:    $parent = $this->FetchParentContainer();
+DAVResource.php:    return $parent->Exists();
+DAVResource.php:    if ( isset($this->dav_name) ) return $this->dav_name;
+DAVResource.php:    if ( isset($this->bound_from) ) return $this->bound_from;
+DAVResource.php:    return $this->dav_name();
+DAVResource.php:    if ( !isset($this->bound_from) && isset($this->dav_name) ) {
+DAVResource.php:      $this->bound_from = $this->dav_name;
+DAVResource.php:    $this->dav_name = $new_dav_name;
+DAVResource.php:    return $this->dav_name;
+DAVResource.php:    if ( $this->IsCollection() ) {
+DAVResource.php:      if ( !isset($this->collection) ) $this->FetchCollection();
+DAVResource.php:      if ( !isset($this->collection->parent_container) ) {
+DAVResource.php:        $this->collection->parent_container = preg_replace( '{[^/]+/$}', '', $this->bound_from());
+DAVResource.php:      return $this->collection->parent_container;
+DAVResource.php:    return preg_replace( '{[^/]+$}', '', $this->bound_from());
+DAVResource.php:  * Returns the principal-URL for this resource
+DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    if ( $this->principal->Exists() ) {
+DAVResource.php:      return $this->principal->principal_url;
+DAVResource.php:    if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    return $this->resource;
+DAVResource.php:    if ( isset($this->unique_tag) ) return $this->unique_tag;
+DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
+DAVResource.php:    return $this->unique_tag;
+DAVResource.php:  * Returns the definitive resource_id for this resource - usually a dav_id
+DAVResource.php:    if ( isset($this->resource_id) ) return $this->resource_id;
+DAVResource.php:    if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:    if ( $this->exists !== true || !isset($this->resource_id) ) $this->resource_id = null;
+DAVResource.php:    return $this->resource_id;
+DAVResource.php:    return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' );
+DAVResource.php:    if ( $this->IsPrincipal() ) return 'root';
+DAVResource.php:    if ( !$this->IsCollection() ) return $this->collection->type;
+DAVResource.php:    if ( ! isset($this->collection->parent_container) ) return null;
+DAVResource.php:    if ( isset($this->parent_container_type) ) return $this->parent_container_type;
+DAVResource.php:    if ( preg_match('#/[^/]+/#', $this->collection->parent_container) ) {
+DAVResource.php:      $this->parent_container_type = 'principal';
+DAVResource.php:                                array( ':parent_name' => $this->collection->parent_container ) );
+DAVResource.php:      if ( $qry->Exec('DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
+DAVResource.php:        if ( $parent->is_calendar == 't' )
+DAVResource.php:          $this->parent_container_type = 'calendar';
+DAVResource.php:        else if ( $parent->is_addressbook == 't' )
+DAVResource.php:          $this->parent_container_type = 'addressbook';
+DAVResource.php:        else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
+DAVResource.php:          $this->parent_container_type = 'schedule-'. $matches[3]. 'box';
+DAVResource.php:          $this->parent_container_type = 'collection';
+DAVResource.php:        $this->parent_container_type = null;
+DAVResource.php:    return $this->parent_container_type;
+DAVResource.php:  * BuildACE - construct an XMLElement subtree for a DAV::ace
+DAVResource.php:    $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type : 'resource'));
+DAVResource.php:        $xmldoc->NSElement($privilege,$k);
+DAVResource.php:        $privilege->NewElement($k);
+DAVResource.php:    if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:    $default_privs = $this->principal->default_privileges;
+DAVResource.php:    if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
+DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, pow(2,25) - 1, new XMLElement('property', new XMLElement('owner')) );
+DAVResource.php:                                array( ':collection_id' => $this->collection->collection_id, ':principal_id' => $this->principal->principal_id ) );
+DAVResource.php:    if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
+DAVResource.php:      while( $grant = $qry->Fetch() ) {
+DAVResource.php:        if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
+DAVResource.php:        if ( $by_collection &&  !isset($grant->by_collection) ) break;
+DAVResource.php:        $acl[] = $this->BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
+DAVResource.php:    $acl[] = $this->BuildACE($xmldoc, $default_privs, new XMLElement('authenticated') );
+DAVResource.php:  * Return general server-related properties, in plain form
+DAVResource.php:        return $this->collection->collection_id;
+DAVResource.php:        if ( isset($this->resourcetypes) ) {
+DAVResource.php:          $this->resourcetypes = preg_replace('{^\s*<(.*)/>\s*$}', '$1', $this->resourcetypes);
+DAVResource.php:          $type_list = preg_split('{(/>\s*<|\n)}', $this->resourcetypes);
+DAVResource.php:        if ( isset($this->{$name}) ) return $this->{$name};
+DAVResource.php:        if ( $this->_is_principal ) {
+DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php:        else if ( $this->_is_collection ) {
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:          if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
+DAVResource.php:          if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:          if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
+DAVResource.php:          if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
+DAVResource.php://        dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
+DAVResource.php:    if ( isset($this->dead_properties) ) $this->FetchDeadProperties();
+DAVResource.php:    $allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()),
+DAVResource.php:        'DAV::owner', 'DAV::principal-URL', 'DAV::current-user-principal',
+DAVResource.php:        'urn:ietf:params:xml:ns:carddav:max-resource-size', 'urn:ietf:params:xml:ns:carddav:supported-address-data',
+DAVResource.php:        'urn:ietf:params:xml:ns:carddav:addressbook-description', 'urn:ietf:params:xml:ns:carddav:addressbook-home-set'
+DAVResource.php:  * Return general server-related properties for this URL
+DAVResource.php://    dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
+DAVResource.php:        $property_list = $this->DAV_AllProperties();
+DAVResource.php:          $this->ResourceProperty($v, $prop, $reply, $discarded);
+DAVResource.php:        $prop->NewElement('href', ConstructURL($this->dav_name) );
+DAVResource.php:      case 'DAV::resource-id':
+DAVResource.php:        if ( $this->resource_id > 0 )
+DAVResource.php:          $reply->DAVElement( $prop, 'resource-id', $reply->href(ConstructURL('/.resources/'.$this->resource_id) ) );
+DAVResource.php:      case 'DAV::parent-set':
+DAVResource.php:        $qry = new AwlQuery($sql, array( ':bound_from' => $this->bound_from() ) );
+DAVResource.php:        if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+DAVResource.php:          while( $row = $qry->Fetch() ) {
+DAVResource.php:            $parents[$row->parent_container] = true;
+DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->bound_from())] = true;
+DAVResource.php:        $parents[preg_replace( '{(?<=/)[^/]+/?$}','',$this->dav_name())] = true;
+DAVResource.php:        $parent_set = $reply->DAVElement( $prop, 'parent-set' );
+DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+DAVResource.php:            $reply->DAVElement($parent_set, 'parent', array(
+DAVResource.php:        if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        $prop->NewElement('getcontenttype', $this->contenttype );
+DAVResource.php:        $resourcetypes = $prop->NewElement('resourcetype' );
+DAVResource.php:        $type_list = $this->GetProperty('resourcetype');
+DAVResource.php:          $reply->NSElement( $resourcetypes, $v );
+DAVResource.php:        if ( $this->_is_binding ) {
+DAVResource.php:          $reply->NSElement( $resourcetypes, 'http://xmlns.davical.org/davical:webdav-binding' );
+DAVResource.php:        $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
+DAVResource.php:        $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        if ( isset($this->resource) ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
+DAVResource.php:        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+DAVResource.php:        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+DAVResource.php:        $reply->NSElement($prop, $tag, $locale );
+DAVResource.php:      case 'DAV::acl-restrictions':
+DAVResource.php:        $reply->NSElement($prop, $tag, array( new XMLElement('grant-only'), new XMLElement('no-invert') ) );
+DAVResource.php:      case 'DAV::inherited-acl-set':
+DAVResource.php:        if ( ! $this->_is_collection ) {
+DAVResource.php:          $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
+DAVResource.php:        $reply->NSElement($prop, $tag, $inherited_acls );
+DAVResource.php:        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+DAVResource.php:        $owner_url = ( isset($this->_is_binding) && $this->_is_binding ? $this->collection->bind_owner_url : $this->principal_url() );
+DAVResource.php:        $reply->DAVElement( $prop, 'owner', $reply->href( $owner_url ) );
+DAVResource.php:      case 'DAV::alternate-URI-set':
+DAVResource.php:        $reply->NSElement($prop, $tag );
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->unique_tag() );
+DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
+DAVResource.php:      case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
+DAVResource.php:        $reply->CalendarserverElement($prop, 'calendar-proxy-'.$proxy_type.'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
+DAVResource.php:      case 'DAV::current-user-privilege-set':
+DAVResource.php:        //if ( $this->HavePrivilegeTo('DAV::read-current-user-privilege-set') ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, $this->BuildPrivileges() );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
+DAVResource.php:        if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, 'text/calendar' );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        if ( $this->IsCalendar() )
+DAVResource.php:        else if ( $this->IsSchedulingCollection() )
+DAVResource.php:          $components[] = $reply->NewXMLElement( 'comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
+DAVResource.php:        $reply->CalDAVElement($prop, 'supported-calendar-component-set', $components );
+DAVResource.php:      case 'DAV::supported-method-set':
+DAVResource.php:        $prop->NewElement('supported-method-set', $this->BuildSupportedMethods() );
+DAVResource.php:      case 'DAV::supported-report-set':
+DAVResource.php:        $prop->NewElement('supported-report-set', $this->BuildSupportedReports( $reply ) );
+DAVResource.php:        $prop->NewElement('supportedlock',
+DAVResource.php:      case 'DAV::supported-privilege-set':
+DAVResource.php:        $prop->NewElement('supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
+DAVResource.php:      case 'DAV::current-user-principal':
+DAVResource.php:        $prop->NewElement('current-user-principal', $reply->href( $request->principal->principal_url ) );
+DAVResource.php:      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+DAVResource.php:        $denied[] = $reply->Tag($tag);
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+DAVResource.php:        if ( ! $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->collection->tz_spec) || $this->collection->tz_spec == '' ) return false;
+DAVResource.php:        $cal->VCalendar();
+DAVResource.php:        $cal->AddComponent( new iCalComponent($this->collection->tz_spec) );
+DAVResource.php:        $reply->NSElement($prop, $tag, $cal->Render() );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:address-data':
+DAVResource.php:      case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+DAVResource.php:        if ( $this->_is_collection ) return false;
+DAVResource.php:        if ( !isset($this->resource) ) $this->FetchResource();
+DAVResource.php:        $reply->NSElement($prop, $tag, $this->resource->caldav_data );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
+DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+DAVResource.php:        $reply->NSElement($prop, $tag, 65500 );
+DAVResource.php:      case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
+DAVResource.php:        if ( ! $this->_is_collection || !$this->_is_addressbook ) return false;
+DAVResource.php:        $address_data = $reply->NewXMLElement( 'address-data', false,
+DAVResource.php:                      array( 'content-type' => 'text/vcard', 'version' => '3.0'), 'urn:ietf:params:xml:ns:carddav');
+DAVResource.php:        $reply->NSElement($prop, $tag, $address_data );
+DAVResource.php:        if ( $this->HavePrivilegeTo('DAV::read-acl') ) {
+DAVResource.php:          $reply->NSElement($prop, $tag, $this->GetACL( $reply ) );
+DAVResource.php:        $reply->NSElement($prop,'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply) );
+DAVResource.php:        $property_value = $this->GetProperty(preg_replace('{^.*:}', '', $tag));
+DAVResource.php:          $reply->NSElement($prop, $tag, $property_value );
+DAVResource.php:          if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
+DAVResource.php:          if ( isset($this->dead_properties[$tag]) ) {
+DAVResource.php:            $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
+DAVResource.php://            dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
+DAVResource.php:    dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
+DAVResource.php:        $tag = $tag->GetTag();
+DAVResource.php:      $found = $this->ResourceProperty($tag, $prop, $reply, $denied );
+DAVResource.php:        if ( !isset($this->principal) ) $this->FetchPrincipal();
+DAVResource.php:        $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
+DAVResource.php://        dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
+DAVResource.php:        $not_found[] = $reply->Tag($tag);
+DAVResource.php:        $reply->NSElement($noprop, $v);
+DAVResource.php:        $noprop->NewElement($v);
+DAVResource.php:    dbg_error_log('DAVResource',':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
+DAVResource.php:    if ( !$this->Exists() ) return null;
+DAVResource.php:    $elements = $this->GetPropStat( $properties, $reply );
+DAVResource.php:      $dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
+DAVResource.php:      $dav_name = $this->dav_name;
+DAVResource.php:    array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
+DAVTicket.php:    $this->dav_name             = null;
+DAVTicket.php:    $this->target_collection_id = null;
+DAVTicket.php:    $this->target_resource_id   = null;
+DAVTicket.php:    $this->expiry               = null;
+DAVTicket.php:    $this->expired              = true;
+DAVTicket.php:    $this->dav_owner_id         = null;
+DAVTicket.php:    $this->ticket_id            = $ticket_id;
+DAVTicket.php:    $this->privileges           = 0;
+DAVTicket.php:    $this->grantor_collection_privileges = 0;
+DAVTicket.php:        array(':ticket_id' => $ticket_id, ':scan_depth' => $c->permission_scan_depth)
+DAVTicket.php:    if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $t = $qry->Fetch() ) {
+DAVTicket.php:      if ( ! $t->expired ) {
+DAVTicket.php:          $this->{$k} = $v;
+DAVTicket.php:        $this->expired = false;
+DAVTicket.php:        $this->privileges = bindec($this->privileges);
+DAVTicket.php:        $this->grantor_collection_privileges = bindec($this->grantor_collection_privileges);
+DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found a current ticket for "%s"', implode(', ',bits_to_privilege($this->privileges())) );
+DAVTicket.php:        dbg_error_log( 'DAVTicket', 'Found an expired ticket: %s - %s', $ticket_id, $t->expires );
+DAVTicket.php:    if ( isset($this->target_resource_id) ) {
+DAVTicket.php:      $qry = new AwlQuery( 'SELECT dav_name FROM caldav_data WHERE dav_id = :dav_id', array(':dav_id' => $this->target_resource_id ) );
+DAVTicket.php:      if ( $qry->Exec('DAVTicket',__LINE__,__FILE__) && $qry->rows() == 1 && $r = $qry->Fetch() ) {
+DAVTicket.php:        $this->dav_name = $r->dav_name;
+DAVTicket.php:    return $this->dav_name;
+DAVTicket.php:    return $this->ticket_id;
+DAVTicket.php:    return ($this->privileges & $this->grantor_collection_privileges);
+DAVTicket.php:    $length = strlen($this->dav_name);
+DAVTicket.php:    return (substr($test_path, 0, $length) == $this->dav_name);
+DAVTicket.php:    return ($test_resource_id == $this->target_collection_id || $test_resource_id == $this->target_resource_id);
+drivers_ldap.php:require_once("auth-functions.php");
+drivers_ldap.php:  /**#@-*/
+drivers_ldap.php:      $this->__construct($config);
+drivers_ldap.php:          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
+drivers_ldap.php:          ldap_set_option($this->connect, LDAP_OPT_REFERRALS, $config['optReferrals']);
+drivers_ldap.php:          $this->connect=ldap_connect($host, $port);
+drivers_ldap.php:          $this->connect=ldap_connect($host);
+drivers_ldap.php:      if (! $this->connect){
+drivers_ldap.php:          $c->messages[] = sprintf(translate( 'drivers_ldap : Unable to connect to LDAP with port %s on host %s'), $port, $host );
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        if (!ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Failed to set LDAP to use protocol version 3, TLS not supported');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        if (!ldap_start_tls($this->connect)) {
+drivers_ldap.php:          $c->messages[] = i18n('drivers_ldap : Could not start TLS: ldap_start_tls() failed');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_read';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_read';
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_list';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+drivers_ldap.php:        $this->ldap_query_one = 'ldap_search';
+drivers_ldap.php:        $this->ldap_query_all = 'ldap_search';
+drivers_ldap.php:      if (!ldap_bind($this->connect, (isset($config['bindDN']) ? $config['bindDN'] : null), (isset($config['passDN']) ? $config['passDN'] : null) ) ){
+drivers_ldap.php:          $c->messages[] = i18n( 'drivers_ldap : Unable to bind to LDAP - check your configuration for bindDN and passDN, and that your LDAP server is reachable');
+drivers_ldap.php:          $this->valid=false;
+drivers_ldap.php:      $this->valid = true;
+drivers_ldap.php:      $this->baseDNUsers  = is_string($config['baseDNUsers']) ? array($config['baseDNUsers']) : $config['baseDNUsers'];
+drivers_ldap.php:      $this->filterUsers  = (isset($config['filterUsers'])  ? $config['filterUsers']  : null);
+drivers_ldap.php:      $this->baseDNGroups = is_string($config['baseDNGroups']) ? array($config['baseDNGroups']) : $config['baseDNGroups'];
+drivers_ldap.php:      $this->filterGroups = (isset($config['filterGroups']) ? $config['filterGroups'] : null);
+drivers_ldap.php:    $query = $this->ldap_query_all;
+drivers_ldap.php:    foreach($this->baseDNUsers as $baseDNUsers) {
+drivers_ldap.php:      $entry = $query($this->connect,$baseDNUsers,$this->filterUsers,$attributes);
+drivers_ldap.php:      if (!ldap_first_entry($this->connect,$entry)) {
+drivers_ldap.php:        $c->messages[] = sprintf(translate('Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<'),
+drivers_ldap.php:                                 $this->filterUsers,
+drivers_ldap.php:      for($i = ldap_first_entry($this->connect,$entry);
+drivers_ldap.php:          $i && $arr = ldap_get_attributes($this->connect,$i);
+drivers_ldap.php:          $i = ldap_next_entry($this->connect,$i) ) {
+drivers_ldap.php:    $query = $this->ldap_query_all;
+drivers_ldap.php:    foreach($this->baseDNGroups as $baseDNGroups) {
+drivers_ldap.php:      $entry = $query($this->connect,$baseDNGroups,$this->filterGroups,$attributes);
+drivers_ldap.php:      if (!ldap_first_entry($this->connect,$entry)) {
+drivers_ldap.php:        $c->messages[] = sprintf(translate('Error NoGroupFound with filter >%s<, attributes >%s< , dn >%s<'),
+drivers_ldap.php:                                 $this->filterGroups,
+drivers_ldap.php:      for($i = ldap_first_entry($this->connect,$entry);
+drivers_ldap.php:          $i && $arr = ldap_get_attributes($this->connect,$i);
+drivers_ldap.php:          $i = ldap_next_entry($this->connect,$i) ) {
+drivers_ldap.php:    $query = $this->ldap_query_one;
+drivers_ldap.php:    foreach($this->baseDNUsers as $baseDNUsers) {
+drivers_ldap.php:      $entry = $query($this->connect, $baseDNUsers, $filter, $attributes);
+drivers_ldap.php:      if (ldap_first_entry($this->connect,$entry) )
+drivers_ldap.php:    if ( !ldap_first_entry($this->connect, $entry) ){
+drivers_ldap.php:    $dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
+drivers_ldap.php:    if ( isset($c->authenticate_hook['config']['i_use_mode_kerberos']) && $c->authenticate_hook['config']['i_use_mode_kerberos'] == "i_know_what_i_am_doing") {
+drivers_ldap.php:      if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
+drivers_ldap.php:    $i = ldap_first_entry($this->connect,$entry);
+drivers_ldap.php:    $arr = ldap_get_attributes($this->connect,$i);
+drivers_ldap.php:    $ldapDrivers =& new ldapDrivers($c->authenticate_hook['config']);
+drivers_ldap.php:  if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+drivers_ldap.php:    foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+drivers_ldap.php:        $usr->{$field} =  $value;
+drivers_ldap.php:        dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+drivers_ldap.php:      $usr->{$field} =  $ldap_values[$value];
+drivers_ldap.php:      dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+drivers_ldap.php:  if ( !$ldapDriver->valid ) {
+drivers_ldap.php:  $mapping = $c->authenticate_hook['config']['mapping_field'];
+drivers_ldap.php:  if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
+drivers_ldap.php:    $filter_munge = $ldapDriver->filterUsers;
+drivers_ldap.php:  else if ( isset($ldapDriver->filterUsers) && $ldapDriver->filterUsers != '' ) {
+drivers_ldap.php:    $filter_munge = "($ldapDriver->filterUsers)";
+drivers_ldap.php:  $valid = $ldapDriver->requestUser( $filter, $attributes, $username, $password );
+drivers_ldap.php:  foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:  $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:    $db_timestamp = $usr->updated;
+drivers_ldap.php:    $db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
+drivers_ldap.php:  if($ldapDriver->valid){
+drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['group_mapping_field'];
+drivers_ldap.php:    $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes);
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_group = $qry->Fetch()) {
+drivers_ldap.php:      $db_groups[$db_group->group_name] = $db_group->group_name;
+drivers_ldap.php:      $db_group_members[$db_group->group_name][] = $db_group->member_name;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating groups : %s'),join(', ',$groups_to_create));
+drivers_ldap.php:        if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+drivers_ldap.php:          foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+drivers_ldap.php:              $usr->{$field} =  $value;
+drivers_ldap.php:              dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+drivers_ldap.php:        $user->user_no = 0;
+drivers_ldap.php:            $user->{$field} =  $ldap_values[$value];
+drivers_ldap.php:            dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+drivers_ldap.php:        if ($user->fullname=="") {
+drivers_ldap.php:          $user->fullname = $group;
+drivers_ldap.php:        if ($user->displayname=="") {
+drivers_ldap.php:          $user->displayname = $group;
+drivers_ldap.php:        $user->username = $group;
+drivers_ldap.php:        $user->updated = "now";  /** @todo Use the 'updated' timestamp from LDAP for groups too */
+drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- adding users %s to group : %s'),join(',',$ldap_groups_info[$group][$mapping['members']]),$group);
+drivers_ldap.php:          $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- updating groups : %s'),join(', ',$groups_to_update));
+drivers_ldap.php:          $c->messages[] = sprintf(i18n('- adding %s to group : %s'),join(', ', $add_users ), $group);
+drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:          $c->messages[] = sprintf(i18n('- removing %s from group : %s'),join(', ', $remove_users ), $group);
+drivers_ldap.php:            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivate groups : %s'),join(', ',$groups_to_deactivate));
+drivers_ldap.php:        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:  if($ldapDriver->valid){
+drivers_ldap.php:    $mapping = $c->authenticate_hook['config']['mapping_field'];
+drivers_ldap.php:    $ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+drivers_ldap.php:      $db_users[] = $db_user->username;
+drivers_ldap.php:      $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated);
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- creating record for users :  %s'),join(', ',$users_to_create));
+drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:      if ( isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v]) ) continue;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('- deactivating users : %s'),join(', ',$users_to_deactivate));
+drivers_ldap.php:      $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+drivers_ldap.php:        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+drivers_ldap.php:        $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- updating user records : %s'),join(', ',$users_to_update));
+drivers_ldap.php:        $c->messages[] = sprintf(i18n('- nothing done on : %s'),join(', ', $users_nothing_done));
+drivers_ldap.php:    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+drivers_ldap.php:    while($db_user = $qry->Fetch()) {
+drivers_ldap.php:      $admins = $db_user->admins;
+drivers_ldap.php:      $c->messages[] = sprintf(i18n('Warning: there are no active admin users, you should fix this before logging out.'));
+drivers_pwauth_pam.php:require_once("auth-functions.php");
+drivers_pwauth_pam.php:  /**#@-*/
+drivers_pwauth_pam.php:    $this->__construct($config);
+drivers_pwauth_pam.php:      $c->messages[] =
+drivers_pwauth_pam.php:      $this->valid=false;
+drivers_pwauth_pam.php:  $program = $c->authenticate_hook['config']['path'];
+drivers_pwauth_pam.php:  $email_base = $c->authenticate_hook['config']['email_base'];
+drivers_squid_pam.php:require_once("auth-functions.php");
+drivers_squid_pam.php:  /**#@-*/
+drivers_squid_pam.php:      $this->__construct($config);
+drivers_squid_pam.php:          $c->messages[] = sprintf(i18n( 'drivers_squid_pam : Unable to find %s file'), $config );
+drivers_squid_pam.php:          $this->valid=false;
+drivers_squid_pam.php:  $cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth";
+drivers_squid_pam.php:              'email' => $username . "@" . $c->authenticate_hook['config']['email_base'],
+freebusy-functions.php:include_once('RRule-v2.php');
+freebusy-functions.php:  if ( !isset($bin_privs) ) $bin_privs = $request->Privileges();
+freebusy-functions.php:    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+freebusy-functions.php:  $params = array( ':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
+freebusy-functions.php:  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+freebusy-functions.php:  if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+freebusy-functions.php:    while( $calendar_object = $qry->Fetch() ) {
+freebusy-functions.php:      if ( $calendar_object->status == 'TENTATIVE' ) {
+freebusy-functions.php:        $extra = ';BUSY-TENTATIVE';
+freebusy-functions.php:      dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+freebusy-functions.php:      $ics = new vComponent($calendar_object->caldav_data);
+freebusy-functions.php:      $expansion = $expanded->GetComponents( array('VEVENT'=>true,'VTODO'=>true,'VJOURNAL'=>true) );
+freebusy-functions.php://        printf( "Type: %s\n", $v->GetType());
+freebusy-functions.php://        echo "-----------------------------------------------------\n";
+freebusy-functions.php:        $start_date = $v->GetProperty('DTSTART');
+freebusy-functions.php:        $start_date = new RepeatRuleDateTime($start_date->Value());
+freebusy-functions.php:        $duration = $v->GetProperty('DURATION');
+freebusy-functions.php:        $duration = ( !isset($duration) ? 'P1D' : $duration->Value());
+freebusy-functions.php:        $end_date->modify( $duration );
+freebusy-functions.php:        $thisfb = $start_date->UTC() .'/'. $end_date->UTC() . $extra;
+freebusy-functions.php:  $freebusy->SetType('VFREEBUSY');
+freebusy-functions.php:  $freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
+freebusy-functions.php:  $freebusy->AddProperty('DTSTART', $range_start->UTC());
+freebusy-functions.php:  $freebusy->AddProperty('DTEND', $range_end->UTC());
+freebusy-functions.php:    $freebusy->AddProperty( 'FREEBUSY', $text[0], (isset($text[1]) ? array('FBTYPE' => $text[1]) : null) );
+HTTPAuthSession.php:  * User e-mail
+HTTPAuthSession.php:  /**#@-*/
+HTTPAuthSession.php:    if ( isset($c->http_auth_mode) && $c->http_auth_mode == "Digest" ) {
+HTTPAuthSession.php:      $this->DigestAuthSession();
+HTTPAuthSession.php:      $this->BasicAuthSession();
+HTTPAuthSession.php:  * @param string $auth_header The WWW-Authenticate header details.
+HTTPAuthSession.php:      $auth_header = sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name);
+HTTPAuthSession.php:    header('Content-type: text/plain; ; charset="utf-8"' );
+HTTPAuthSession.php:    else if ( isset($c->authenticate_hook['server_auth_type']) && $c->authenticate_hook['server_auth_type'] == $_SERVER['AUTH_TYPE']
+HTTPAuthSession.php:      if ( ! isset($c->authenticate_hook['call']) ) {
+HTTPAuthSession.php:        $c->authenticate_hook['call'] = 'auth_external';
+HTTPAuthSession.php:      if ( $u = $this->CheckPassword( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
+HTTPAuthSession.php:        $this->AssignSessionDetails($u);
+HTTPAuthSession.php:    if ( isset($c->allow_unauthenticated) && $c->allow_unauthenticated ) {
+HTTPAuthSession.php:      $this->user_no = -1;
+HTTPAuthSession.php:      $this->username = 'guest';
+HTTPAuthSession.php:      $this->fullname = 'Unauthenticated User';
+HTTPAuthSession.php:      $this->email = 'invalid';
+HTTPAuthSession.php:    $this->AuthFailedResponse();
+HTTPAuthSession.php:      if ( $data = $this->ParseDigestHeader($_SERVER['PHP_AUTH_DIGEST']) ) {
+HTTPAuthSession.php:        $A1 = md5($data['username'] . ':' . $c->system_name . ':' . $user_password);
+HTTPAuthSession.php:          $this->AssignSessionDetails($u);
+HTTPAuthSession.php:    $opaque = md5($c->system_name);
+HTTPAuthSession.php:    $this->AuthFailedResponse( sprintf('WWW-Authenticate: Digest realm="%s", qop="auth,auth-int", nonce="%s", opaque="%s"', $c->system_name, $nonce, $opaque ) );
+HTTPAuthSession.php:  *  - largely sourced from the PHP documentation
+HTTPAuthSession.php:    if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && function_exists($c->authenticate_hook['call']) ) {
+HTTPAuthSession.php:      *   - Accept a username / password
+HTTPAuthSession.php:      *   - Confirm the username / password are correct
+HTTPAuthSession.php:      *   - Create (or update) a 'usr' record in our database
+HTTPAuthSession.php:      *   - Return the 'usr' record as an object
+HTTPAuthSession.php:      *   - Return === false when authentication fails
+HTTPAuthSession.php:      *   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+HTTPAuthSession.php:      $hook_response = call_user_func( $c->authenticate_hook['call'], $username, $password );
+HTTPAuthSession.php:      if (isset($c->authenticate_hook['optional']) && $c->authenticate_hook['optional']) {
+HTTPAuthSession.php:      dbg_error_log( "BasicAuth", ":CheckPassword: Name:%s, Pass:%s, File:%s, Active:%s", $username, $password, $usr->password, ($usr->active?'Yes':'No') );
+HTTPAuthSession.php:      if ( $usr->active && session_validate_password( $password, $usr->password ) ) {
+HTTPAuthSession.php:    return ( isset($this->logged_in) && $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
+HTTPAuthSession.php:    $this->roles = array();
+HTTPAuthSession.php:                                array( ':user_no' => $this->user_no) );
+HTTPAuthSession.php:    if ( $qry->Exec('BasicAuth') && $qry->rows() > 0 ) {
+HTTPAuthSession.php:      while( $role = $qry->Fetch() ) {
+HTTPAuthSession.php:        $this->roles[$role->role_name] = true;
+HTTPAuthSession.php:    if ( !isset($u->principal_id) ) {
+HTTPAuthSession.php:      // If they don't have a principal_id set then we should re-read from our local database
+HTTPAuthSession.php:      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+HTTPAuthSession.php:      if ( $qry->Exec() && $qry->rows() == 1 ) {
+HTTPAuthSession.php:        $u = $qry->Fetch();
+HTTPAuthSession.php:      $this->{$k} = $v;
+HTTPAuthSession.php:    $this->GetRoles();
+HTTPAuthSession.php:    $this->logged_in = true;
+HTTPAuthSession.php:    if ( function_exists("awl_set_locale") && isset($this->locale) && $this->locale != "" ) {
+HTTPAuthSession.php:      awl_set_locale($this->locale);
+interactive-page.php:$home_menu->AddOption(translate('Logout'), $c->base_url.'/index.php?logout', translate('Log out of DAViCal') );
+interactive-page.php:$help_menu->AddOption(translate('DAViCal Homepage'),'http://www.davical.org/',translate('The DAViCal Home Page'), false, 6000, true );
+interactive-page.php:$help_menu->AddOption(translate('DAViCal Wiki'),'http://wiki.davical.org/'.$wiki_help,translate('Visit the DAViCal Wiki'), false, 7000, true );
+interactive-page.php:$help_menu->AddOption(translate('Request Feature'),'http://davical.uservoice.com/',translate('Go to the DAViCal Feature Requests'), false, 8000, true );
+interactive-page.php:$help_menu->AddOption(translate('Report Bug'),'http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785',translate('Report a bug in the system'), false, 9000, true );
+interactive-page.php:$user_menu->AddOption(translate('View My Details'),$c->base_url.'/admin.php?action=edit&t=principal&id='.$session->principal_id,translate('View my own principal record'));
+interactive-page.php:$user_menu->AddOption(translate('List Users'),$c->base_url.'/admin.php?action=browse&t=principal&type=1');
+interactive-page.php:$user_menu->AddOption(translate('List Resources'),$c->base_url.'/admin.php?action=browse&t=principal&type=2');
+interactive-page.php:$user_menu->AddOption(translate('List Groups'),$c->base_url.'/admin.php?action=browse&t=principal&type=3');
+interactive-page.php:if ( $session->AllowedTo('Admin' )) {
+interactive-page.php:  $admin_menu->AddOption(translate('Setup'),$c->base_url.'/setup.php',translate('Setup DAViCal') );
+interactive-page.php:  $admin_menu->AddOption(translate('Upgrade Database'),$c->base_url.'/upgrade.php',translate('Upgrade DAViCal database schema') );
+interactive-page.php:  $admin_menu->AddOption(translate('Import Calendars'),$c->base_url.'/tools.php',translate('Operations on your calendar') );
+interactive-page.php:  $user_menu->AddOption(translate('Inactive Principals'),$c->base_url.'/admin.php?action=browse&t=principal&active=f');
+interactive-page.php:  $user_menu->AddOption(translate('Create Principal'),$c->base_url.'/admin.php?action=edit&t=principal',translate('Create a new principal (i.e. a new user, resource or group)'));
+interactive-page.php:$main_menu->AddSubMenu($home_menu, translate('Home'), $c->base_url.'/index.php', translate('Home Page'), false, 1000);
+interactive-page.php:$main_menu->AddSubMenu($user_menu, translate('User Functions'), $c->base_url.'/admin.php?action=browse&t=principal&type=1', translate('Browse all users'), false, 2000);
+interactive-page.php:$main_menu->AddSubMenu($admin_menu, translate('Administration'), $c->base_url.'/index.php', translate('Administration'), false, 3000);
+interactive-page.php:$main_menu->AddSubMenu($help_menu, translate('Help'), $c->base_url.'/help.php',translate('Help on the current screen'), false, 9000);
+iSchedule.php:*  - verifying Domain Key signatures
+iSchedule.php:*  - delivering remote scheduling requests to local users inboxes
+iSchedule.php:*  - Utility functions which we can use to decide whether this
+iSchedule.php:		$this->selector = 'cal';
+iSchedule.php:		if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
+iSchedule.php:			$this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
+iSchedule.php:		$dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
+iSchedule.php:			$this->dk = $dkim [ 0 ] [ 'txt' ];
+iSchedule.php:			$this->failed = true;
+iSchedule.php:		if ( $this->failed == true )
+iSchedule.php:		$clean = preg_replace ( '/[\s\t]*([;=])[\s\t]*/', '$1', $this->dk );
+iSchedule.php:		$this->parsed = array();
+iSchedule.php:				$this->parsed [ $key ] = $value;
+iSchedule.php:				$this->parsed_ignored [ $key ] = $value;
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		if ( isset ( $this->parsed [ 's' ] ) )
+iSchedule.php:			if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) )
+iSchedule.php:		if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' )
+iSchedule.php:		if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) )
+iSchedule.php:			if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
+iSchedule.php:				$this->failOnError = false;
+iSchedule.php:			if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
+iSchedule.php:				$this->subdomainsOK = false;
+iSchedule.php:		if ( isset ( $this->parsed [ 'g' ] ) )
+iSchedule.php:			$this->remote_user_rule = $this->parsed [ 'g' ];
+iSchedule.php:		if ( isset ( $this->parsed [ 'p' ] ) )
+iSchedule.php:			$data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( preg_replace ( '/_/', '', $this->parsed [ 'p' ] ), 64 )) . "\n-----END PUBLIC KEY-----"; 
+iSchedule.php:			$this->remote_public_key = $data;
+iSchedule.php:		$this->failed = false;
+iSchedule.php:		$this->remote_ssl = false;
+iSchedule.php:		$r = dns_get_record ( '_ischedules._tcp.' . $this->domain , DNS_SRV );
+iSchedule.php:			$this->remote_ssl = true;
+iSchedule.php:			$r = dns_get_record ( '_ischedule._tcp.' . $this->domain , DNS_SRV );
+iSchedule.php:		elseif ( $this->try_anyway == true )
+iSchedule.php:				$remote_server = $this->domain;
+iSchedule.php:		$this->remote_server = $remote_server;
+iSchedule.php:		$this->remote_port = $remote_port;
+iSchedule.php:		$remote_capabilities = file_get_contents ( 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' . 
+iSchedule.php:			$this->remote_server . ':' . $this->remote_port . 
+iSchedule.php:			'/.well-known/ischedule?query=capabilities' );
+iSchedule.php:		$xmltree = BuildXMLTree( $request->xml_tags, $position);
+iSchedule.php:			  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+iSchedule.php:		$dk['s'] = $this->selector;
+iSchedule.php:		$dk['d'] = $this->domain;
+iSchedule.php:		$dk['c'] = 'simple-http';
+iSchedule.php:		 //a=rsa-sha1; d=caveman.name; s=cal; c=simple-http; q=dns/txt; h=Originator:Recipient:Host:Content-Type; b
+iSchedule.php:	* @param string $sig the value of the DKIM-Signature header
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		// the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
+iSchedule.php:		// but it does define the value, it should be simple-http.  RFC4871 also defines two methods
+iSchedule.php:		if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
+iSchedule.php:			$this->body_cannon = $matches[2];
+iSchedule.php:			$this->body_cannon = $matches[1];
+iSchedule.php:		$this->header_cannon = $matches[1];
+iSchedule.php:		if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) 
+iSchedule.php:		$this->remote_server = $dkim['d'];
+iSchedule.php:			$this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
+iSchedule.php:		$this->remote_selector = $dkim['s'];
+iSchedule.php:		$this->signed_headers = preg_split ( '/:/', $dkim['h'] );
+iSchedule.php:		foreach ( $this->signed_headers as $h )
+iSchedule.php:			if ( ( ! isset ( $_SERVER['HTTP_' . strtr ( strtoupper ( $h ), '-', '_' ) ] ) &&
+iSchedule.php:             ! isset ( $_SERVER[ strtr ( strtoupper ( $h ), '-', '_' ) ] ) ) 
+iSchedule.php:				  || strtolower ( $h ) == 'dkim-signature' )
+iSchedule.php:			$this->signed_length = $dkim['l'];
+iSchedule.php:		$this->failed = false;
+iSchedule.php:		$this->DKSig = $dkim;
+iSchedule.php:			$this->remote_user = $matches[1];
+iSchedule.php:			$this->domain = $matches[2];
+iSchedule.php:		$this->failed = true;
+iSchedule.php:		foreach ( $this->signed_headers as $h )
+iSchedule.php:			if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
+iSchedule.php:				$signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+iSchedule.php:				$signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+iSchedule.php:		$body = $request->raw_post;
+iSchedule.php:		if ( ! isset ( $this->signed_length ) )
+iSchedule.php:			$this->signed_length = strlen ( $body );
+iSchedule.php:			$body = substr ( $body, 0, $this->signed_length );
+iSchedule.php:		if ( isset ( $this->remote_user_rule ) )
+iSchedule.php:			if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
+iSchedule.php:		$body_hash = base64_encode ( hash ( preg_replace ( '/^.*(sha[1256]+).*/','$1', $this->DKSig['a'] ), $body , true ) );
+iSchedule.php:		if ( $this->DKSig['bh'] != $body_hash )
+iSchedule.php:		$signed .= 'DKIM-Signature: ' . $sig;
+iSchedule.php:		$verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key );
+iSchedule.php:		$this->failed = false;
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature missing') );
+iSchedule.php:    $err = $this->parseDKIM ( $sig );
+iSchedule.php:		if ( $err !== true || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature invalid ' ) . "\n" . $err . "\n" . $sig );
+iSchedule.php:		if ( ! $this->getTxt () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(DNS ERROR)') );
+iSchedule.php:		if ( ! $this->parseTxt () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Parse ERROR)') );
+iSchedule.php:		if ( ! $this->validateKey () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Validation ERROR)') );
+iSchedule.php:		if ( ! $this->verifySignature () || $this->failed )
+iSchedule.php:			$request->DoResponse( 403, translate('DKIM signature validation failed(Signature verification ERROR)') . $this->verifySignature() );
+iSchedule.php:if ( $d->validateRequest ( ) )
+iSchedule.php:	include ( 'caldav-POST.php' );
+log_caldav_action.php:  if ( !isset($c->action_log_name) ) {
+log_caldav_action.php:  $logfile = fopen( $c->action_log_name, "a+" );
+other_translated_strings.php:i18n("Admin <!-- in the sense of 'systems admin' -->");
+other_translated_strings.php:i18n("Group <!-- in the sense of a group of people -->");
+other_translated_strings.php:i18n("Public <!-- in the sense of being available to all users -->");
+other_translated_strings.php:i18n("Resource <!-- a thing which might be booked: a room, a carpark, a projector... -->");
+other_translated_strings.php:i18n("No. <!-- short for 'Number' -->");
+other_translated_strings.php:i18n("No <!-- not 'Yes' -->");
+other_translated_strings.php:i18n("Id <!-- short for 'Identifier' -->");
+other_translated_strings.php:i18n("forget me not <!-- this is a colloquial phrase in english (the name of a flower) and is an option allowing people to log in automatically in future -->");
+page-footer.php:  if ( isset($c->scripts) && is_array($c->scripts) ) {
+page-footer.php:    foreach ( $c->scripts AS $script ) {
+page-footer.php:  if ( isset($c->dbg['statistics']) && $c->dbg['statistics'] ) {
+page-footer.php:    $script_time = microtime(true) - $c->script_start_time;
+page-footer.php:                        $_SERVER['REQUEST_METHOD'], 200, $script_time, $c->total_query_time, $_SERVER['REQUEST_URI']);
+page-header.php:if ( !isset($c->page_title) ) {
+page-header.php:  $c->page_title = translate('DAViCal CalDAV Server');
+page-header.php:  if ( $GLOBALS['session']->AllowedTo('Admin') || $GLOBALS['session']->AllowedTo('Support') ) {
+page-header.php:  return ' <a class="help" href="'.$c->base_url.'/help.php?h='.$help_topic.'" title="'.translate('Show help on').' &39;'.$help_topic.'&39;" target="_new">['.$display_url.']</a> ';
+page-header.php:  header( 'Content-type: text/html; charset="utf-8"' );
+page-header.php:  echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+page-header.php:<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+page-header.php:<title>$c->page_title</title>
+page-header.php:  foreach ( $c->stylesheets AS $stylesheet ) {
+page-header.php:  if ( isset($c->local_styles) ) {
+page-header.php:    foreach ( $c->local_styles AS $stylesheet ) {
+page-header.php:  if ( isset($c->print_styles) ) {
+page-header.php:    foreach ( $c->print_styles AS $stylesheet ) {
+page-header.php:  if ( isset($main_menu) ) echo $main_menu->RenderAsCSS();
+page-header.php:  if ( isset($related_menu) && $related_menu->Size() > 0 ) {
+page-header.php:    echo $related_menu->Render( true );
+page-header.php:  if ( isset($c->messages) && is_array($c->messages) && count($c->messages) > 0 ) {
+page-header.php:    foreach( $c->messages AS $i => $msg ) {
+PublicSession.php:  * User e-mail
+PublicSession.php:  /**#@-*/
+PublicSession.php:    $this->user_no = -1;
+PublicSession.php:    $this->principal_id = -1;
+PublicSession.php:    $this->email = null;
+PublicSession.php:    $this->username = 'guest';
+PublicSession.php:    $this->fullname = 'Anonymous';
+PublicSession.php:    $this->groups = ( isset($c->public_groups) ? $c->public_groups : array() );
+PublicSession.php:    $this->roles = array( 'Public' => true );
+PublicSession.php:    $this->logged_in = false;
+PublicSession.php:    return ( isset($this->roles[$whatever]) && $this->roles[$whatever] );
+pubsub.php:		$this->status = "online";
+pubsub.php:		$this->setupXmlParser ();
+pubsub.php:		if ( ! isset ( $this->jid ) )
+pubsub.php:			return $this->connection = false;
+pubsub.php:		if ( ! isset ( $this->idle ) )
+pubsub.php:			$this->idle = true;
+pubsub.php:		if ( ! isset ( $this->resource ) )
+pubsub.php:			$this->resource = 'caldav' . getmypid();
+pubsub.php:		if ( ! preg_match ( '/^\//', $this->resource ) )
+pubsub.php:			$this->resource = '/' . $this->resource;
+pubsub.php:		$temp = explode ( '@', $this->jid );
+pubsub.php:		$this->username = $temp[0];
+pubsub.php:		if ( ! isset ( $this->server ) )
+pubsub.php:			$this->server = $temp[1];
+pubsub.php:		$r = dns_get_record("_xmpp-client._tcp.". $this->server , DNS_SRV);
+pubsub.php:			$this->original_server   = $this->server;
+pubsub.php:			$this->server            = $r[0]['target'];
+pubsub.php:			$this->original_port     = $this->port;
+pubsub.php:			$this->port              = $r[0]['port'];
+pubsub.php:		if ( ! isset ( $this->port ) )
+pubsub.php:			$this->port = 5222;
+pubsub.php:		if ( 'ssl' == $this->tls || ( ! isset ( $this->tls ) && 5223 == $this->port ) )
+pubsub.php:			$url = 'ssl://' . $this->server;
+pubsub.php:		elseif ( 'tls' == $this->tls || ( ! isset ( $this->tls ) && 5222 == $this->port ) )
+pubsub.php:			$url = 'tcp://' . $this->server;
+pubsub.php:			$url = 'tcp://' . $this->server;
+pubsub.php:		if ( isset ( $this->original_server ) )
+pubsub.php:			$this->server = $this->original_server;
+pubsub.php:		$this->connection = stream_socket_client ( $url . ':' . $this->port, $errno, $errstring, 10, STREAM_CLIENT_ASYNC_CONNECT );
+pubsub.php:		if ( false === $this->connection )
+pubsub.php:		$this->initializeQueue ( );
+pubsub.php:		socket_set_blocking ( $this->connection, false );
+pubsub.php:		if ( $this->debug ) $this->log ( 'handling features' );
+pubsub.php:		if ( 'STARTTLS' == $node->firstChild->nodeName )
+pubsub.php:			$this->sendQueue[] = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
+pubsub.php:		$elements = $this->query ( '*/MECHANISM', $node );
+pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching MECHANISM nodes ");
+pubsub.php:				$auth_mech[] = $e->nodeValue;
+pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . preg_replace('/@.*$/','',$this->jid) . "\x00" . $this->password) . "</auth>";
+pubsub.php:			elseif ( in_array ( 'DIGEST-MD5', $auth_mech ) ) // this code and the associated function are UNTESTED
+pubsub.php:				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
+pubsub.php:				$this->recvHandlers['challenge'] = 'digestAuth' ;
+pubsub.php:			$this->recvHandlers['success'] = 'handleSuccess' ;
+pubsub.php:		$elements = $this->query ( '*/BIND', $node );
+pubsub.php:		if ( ! is_null ( $elements ) && $elements->length > 0 )
+pubsub.php:			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching BIND nodes ");
+pubsub.php:			$this->ready = true;
+pubsub.php:		stream_set_blocking ( $this->connection, true );
+pubsub.php:		stream_socket_enable_crypto ( $this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
+pubsub.php:		stream_set_blocking ( $this->connection, false );
+pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		// this code is based solely on the description found @ http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html
+pubsub.php:		$contents = $node->nodeValue;
+pubsub.php:			if ( $challenge['realm'] == $this->server ) // might fail need to handle a response with multiple realms
+pubsub.php:				$X =  md5 ( preg_replace('/@.*$/','',$this->jid) . ':' . $this->server . ':' .  $this->password, true );
+pubsub.php:				$HA1 = md5 ( $X . ':' . $challenge['nonce'] . ':' . $cnonce . ':' . $this->jid . $this->resource );
+pubsub.php:				$HA2 = md5 ( "AUTHENTICATE:xmpp/" . $this->server );
+pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" .
+pubsub.php:					base64_encode("username=\"" . preg_replace('/@.*$/','',$this->jid) . "\"," .
+pubsub.php:					"realm=\"" . $this->server . "\",nonce=\"" . $challenge['nonce'] . "\",cnonce=\"". $cnonce . "\"," .
+pubsub.php:					"nc=00000001,qop=auth,digest-uri=\"xmpp/" . $this->server . "\",response=" . $resp .
+pubsub.php:					",charset=utf-8,authzid=\"". $this->jid . $this->resource . "\"" ) . "</response>" // note the PID component to the resource, just incase
+pubsub.php:				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>" ;
+pubsub.php:		$this->loggedIn = true;
+pubsub.php:		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>" . preg_replace('/^\//','',$this->resource) . "</resource></bind></iq>";
+pubsub.php:		$this->recvHandlers['stream:error'] = 'handleError' ;
+pubsub.php:		$this->recvHandlers['iq'] = 'handleIq' ;
+pubsub.php:		$this->recvHandlers['message'] = 'handleMessage' ;
+pubsub.php:		$this->mesgcount = 1;
+pubsub.php:		if ( $this->debug ) $this->log ( "Handle IQ id:" . $node->getAttribute ( 'id' ) . ' type:' . $node->getAttribute ( 'type' ) . "");
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'result' || $node->getAttribute ( 'type' ) == 'error' )
+pubsub.php:			$commandId = $node->getAttribute ( 'id' );
+pubsub.php:			$this->command[$commandId] = true;
+pubsub.php:			if ( isset ( $this->handleCommand[$commandId] ) )
+pubsub.php:				$this->finishedCommands[$commandId] = true;
+pubsub.php:				if ( method_exists ( $this, $this->handleCommand[$commandId] ) )
+pubsub.php:					call_user_func_array ( array ( $this, $this->handleCommand[$commandId] ),  array ( &$node ) );
+pubsub.php:					call_user_func_array ( $this->handleCommand[$commandId], array ( &$node ) );
+pubsub.php:		if ( $node->getAttribute ( 'id' ) == $this->mesgcount && $this->mesgcount < 3 )
+pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='" . ( $this->mesgcount++ ) . "'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
+pubsub.php:			$this->sendQueue[] = "<iq xmlns='jabber:client' type='get' id='" . ( $this->mesgcount++ ) . "'><query xmlns='jabber:iq:roster' /></iq>";
+pubsub.php:		if ( $node->getAttribute ( 'id' ) == '2' && $this->command['2'] == true )
+pubsub.php:			$this->nextreply = $this->mesgcount++;
+pubsub.php:			$this->sendQueue[] = "<presence id='" . $this->nextreply . "' ><status>" . $this->status . '</status></presence>';
+pubsub.php:			$this->ready = true;
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'chat' )
+pubsub.php:			$this->command[$node->getAttribute ( 'id' )] = true;
+pubsub.php:			$elements = $this->query ( '//*/body', $node );
+pubsub.php:			if ( 0 < $elements->length )
+pubsub.php:				$temp = $elements->items(0);
+pubsub.php:				if ( $this->debug ) $this->log ( "received message " . $temp->nodeValue );
+pubsub.php:		$this->log ( 'STREAM ERROR OCCURRED! XMPP closing connection, this is probably a bug' );
+pubsub.php:		$this->idle = false;
+pubsub.php:		$this->close ();
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$send  = "<iq type='get' from='" . $this->jid . $this->resource . "' to='$to' id='" . $msg . "'>";
+pubsub.php:		$this->handleCommand[$msg] = 'discoResult';
+pubsub.php:		$this->sendQueue[] = $send;
+pubsub.php:		$this->go();
+pubsub.php:		if ( $this->debug ) $this->log ( $node->ownerDocument->saveXML($node) );
+pubsub.php:		$id = $node->getAttribute ( 'id' );
+pubsub.php:		$identity = $this->query ( '*/IDENTITY', $node );
+pubsub.php:		if ( @is_array ( $this->pubsub [ 'create' ] [ $id ] ) && 0 == $identity->length )
+pubsub.php:			$this->pubsubCreateNode( $this->pubsub [ 'create' ] [ $id ] [ 0 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 1 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 2 ],
+pubsub.php:				$this->pubsub [ 'create' ] [ $id ] [ 3 ] );
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out .= "<message id='" . $msg . "' from='" . $this->jid . $this->resource . "' to='" . $to. "' >";
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:			$to = 'pubsub.' . $this->server;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$this->pubsub['create'][$this->mesgcount+1] = array ( $to, $type, $name, $configure );
+pubsub.php:		$this->disco ( $to, 'info', $node );
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="get" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->go();
+pubsub.php:			$to = 'pubsub.' . $this->server;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		$msg = $this->mesgcount++;
+pubsub.php:		if ( 'hometree' == $this->pubsubLayout )
+pubsub.php:			$node = '/home/' . $this->server . '/' . $this->username . $name;
+pubsub.php:		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+pubsub.php:		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="' . $name . '" jid="' . $this->jid . $this->resource . '"/>';
+pubsub.php:		$this->sendQueue[] = $out;
+pubsub.php:		$this->handleCommand[ $msg ] = 'pubsubResult';
+pubsub.php:		$this->go();
+pubsub.php:		if ( $this->debug ) $this->log ( "pubsub RESULT   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+pubsub.php:		if ( $node->getAttribute ( 'type' ) == 'error' )
+pubsub.php:			$errnode = $this->query ( 'ERROR', $node );
+pubsub.php:			if ( $errnode->length > 0 && (  '403' == $errnode->item( 0 )->getAttribute ( 'code' ) || '500' == $errnode->item( 0 )->getAttribute ( 'code' ) ) )
+pubsub.php:				if ( 'CREATE' == $node->firstChild->firstChild->tagName )
+pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+pubsub.php:					if ( $this->debug ) $this->log ( "403 error during CREATE for node '" . $pubnode . "' ");
+pubsub.php:					$name =  preg_replace ( '/^.*?\/' . $this->username . '\//','', $pubnode );
+pubsub.php:					if ( ! in_array ( 'create', $this->pubsubNext ) )
+pubsub.php:								'to' => $node->getAttribute ( 'from' ),
+pubsub.php:							array_unshift ( $this->pubsubNext, $v );
+pubsub.php:						$this->pubsubDoNext ( );
+pubsub.php:			elseif ( $errnode->length > 0 && '404' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+pubsub.php:				if ( 'PUBLISH' == $node->firstChild->firstChild->tagName )
+pubsub.php:					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+pubsub.php:					if ( $this->debug ) $this->log ( "404 error during PUBLISH for node '" . $pubnode . "' ");
+pubsub.php:					$publish = $this->query ( '//*/PUBLISH', $node );
+pubsub.php:					$this->pubsubNext[] = array (
+pubsub.php:						'to' => $node->getAttribute ( 'from' ),
+pubsub.php:						'name' => preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) ,
+pubsub.php:						'contents' => $publish->item( 0 )->firstChild->nodeValue );
+pubsub.php:					if ( $this->debug ) $this->log ( "attempting to create node '" . $this->pubsubNext[0]['name'] . "' ");
+pubsub.php:					$this->pubsubCreateNode ( $node->getAttribute ( 'from' ) ,'set', preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) );
+pubsub.php:			elseif ( $errnode->length > 0 && '409' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+pubsub.php:				if ( 'CANCEL' == $errnode->item( 0 )->firstChild->tagName || 'CONFLICT' == $errnode->item( 0 )->firstChild->tagName )
+pubsub.php:					$this->pubsubDoNext ( );
+pubsub.php:		elseif ( 0 < count ( $this->pubsubNext ) )
+pubsub.php:			$this->pubsubDoNext ( );
+pubsub.php:		if ( 0 < count ( $this->pubsubNext ) )
+pubsub.php:			$pub = array_shift ( $this->pubsubNext );
+pubsub.php:				if ( $this->debug ) $this->log ( "attempting to publish to node '" . $pub['name'] . "'  contents '" . $pub['contents'] . "'");
+pubsub.php:				$this->pubsubPublish ( $pub[$to], 'set', $pub['name'], $pub['contents'] );
+pubsub.php:				if ( $this->debug ) $this->log ( "attempting to create node '" . $pub['name'] . "' ");
+pubsub.php:				$this->pubsubCreateNode ( $pub[$to], 'set', $pub['name'] );
+pubsub.php:		$this->loggedIn = false;
+pubsub.php:		$this->streamTagBegin = '<'."?xml version='1.0'?"."><stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+pubsub.php:		$this->streamTagEnd = '</stream:stream>';
+pubsub.php:		$this->sendQueue[] = $this->streamTagBegin;
+pubsub.php:		$this->recvHandlers['stream:features'] = 'handleFeatures' ;
+pubsub.php:		$this->recvHandlers['features'] = 'handleFeatures' ;
+pubsub.php:		$this->recvHandlers['proceed'] = 'enableTLS' ;
+pubsub.php:		if ( $this->debug ) $this->log ( "SEND: $data");
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			if ( fwrite ( $this->connection, $data, $len) === $len )
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			$data = fgets ( $this->connection, 4096 );
+pubsub.php:				while ( 0 != strlen ( $moredata = fgets ( $this->connection, 1024 ) ) && 20 < $count++ )
+pubsub.php:				$this->stream .= $data;
+pubsub.php:				if ( $this->debug ) $this->log ( "RECV: $data" );
+pubsub.php:		$this->recvQueue = implode ( '', $this->sendQueue );
+pubsub.php:		$this->moredata = false;
+pubsub.php:		while ( false !== $this->connection )
+pubsub.php:			if ( 0 < count ( $this->sendQueue ) )
+pubsub.php:				while ( $data = array_shift ( $this->sendQueue ) )
+pubsub.php:					$this->send ( $data );
+pubsub.php:			$data = $this->recv ( );
+pubsub.php:			xml_parse ( $this->xmlparser, $data, false );
+pubsub.php:			while ( $rnode = array_shift ( $this->recvTags ) )
+pubsub.php:				$rname = strtolower ( $rnode->localName );
+pubsub.php:				if ( $this->debug ) $this->log ( " processing $rname ");
+pubsub.php:				if ( isset ( $this->recvHandlers[$rname] ) ) //&& is_callable ( $this->recvHandlers[$r->name] ) )
+pubsub.php:					if ( method_exists ( $this, $this->recvHandlers[$rname] ) )
+pubsub.php:						call_user_func_array ( array ( $this, $this->recvHandlers[$rname] ),  array ( &$rnode ) );
+pubsub.php:						call_user_func_array ( $this->recvHandlers[$rname], array ( &$rnode ) );
+pubsub.php:				if ( $this->idle === true )
+pubsub.php:					if ( $this->ready == true && count ( $this->handleCommand ) <= count ( $this->command ) )
+pubsub.php:		$this->depth++;
+pubsub.php:			$this->processDepth++;
+pubsub.php:				if ( strlen ( $matches[1] ) > 0 && ! isset ( $this->namespaces [ $matches[1] ] ) )
+pubsub.php:					$this->xquery->registerNamespace ( $matches[1], $v );
+pubsub.php:					$this->namespaces [ $matches[1] ] = $v;
+pubsub.php:					if ( $this->debug ) $this->log ( " adding namespace $k => $v ");
+pubsub.php:			$node = $this->doc->createElementNS ( $namespace, $name );
+pubsub.php:			$node = $this->doc->createElement ( $name );
+pubsub.php:			$node->setAttribute ( strtolower ( $k ), $v );
+pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->appendChild ( $node );
+pubsub.php:		$this->depth--;
+pubsub.php:		//if ( $this->debug ) $this->log ( "depth: " . $this->depth . " processDepth: " . $this->processDepth . " ");
+pubsub.php:		if ( $this->depth == $this->processDepth || 'STREAM:STREAM' == $name || 'STREAM:FEATURES' == $name || 'PROCEED' == $name )
+pubsub.php:			if ( $this->debug ) $this->log ( " adding $name to tags to process ");
+pubsub.php:			array_push ( $this->recvTags, $this->currentXMLNode ); // replace with tag
+pubsub.php:		$this->currentXMLNode = $this->currentXMLNode->parentNode;
+pubsub.php:		$this->currentXMLNode->appendChild ( $this->doc->createTextNode ( $text ) );
+pubsub.php:		$this->depth = 0;
+pubsub.php:		$this->xmlparser = xml_parser_create ( );
+pubsub.php:		xml_set_object ( $this->xmlparser, $this );
+pubsub.php:		xml_set_element_handler ( $this->xmlparser, 'startElement', 'endElement' );
+pubsub.php:		xml_set_character_data_handler ( $this->xmlparser, 'parseData' );
+pubsub.php:		$this->doc = new DOMDocument ();
+pubsub.php:		$this->xquery = new DOMXpath ( $this->doc );
+pubsub.php:		$this->xquery->registerNamespace ( 'stream', 'http://etherx.jabber.org/streams' );
+pubsub.php:		$this->currentXMLNode = $this->doc->appendChild ( $this->doc->createElement ( 'start' ) );
+pubsub.php:			return $this->xquery->query	( $expression );
+pubsub.php:			return $this->xquery->query	( $expression , $node );
+pubsub.php:			$this->jid = $jid;
+pubsub.php:			$this->password = $password;
+pubsub.php:		$this->ready = false;
+pubsub.php:		if ( false !== $this->connect () )
+pubsub.php:			$this->go ();
+pubsub.php:		if ( false !== $this->connection )
+pubsub.php:			$this->send ( '</stream:stream>');
+pubsub.php:			fclose ( $this->connection );
+pubsub.php:			$this->connection = false;
+pubsub.php:			$this->sendHandler[$command] = $handler;
+pubsub.php:			$this->recvHandler[$command] = $handler;
+pubsub.php:	$t->tls =  'none';
+pubsub.php:	$t->idle =  false;
+pubsub.php:	if ( 1 == $c->dbg["ALL"] || 1 == $c->dbg["push"] )
+pubsub.php:		$t->debug = true ;
+pubsub.php:		$t->debug = false ;
+pubsub.php:	$t->pubsubLayout = 'flat';
+pubsub.php:	$qry->Exec('pubsub');
+pubsub.php:	$row = $qry->Fetch();
+pubsub.php:	$t->open ( $c->notifications_server['jid'], $c->notifications_server['password'] );
+pubsub.php:	if ( isset ( $c->notifications_server['debug_jid'] ) )
+pubsub.php:		$t->sendMessage ( $c->notifications_server['debug_jid'], "ACTION: $action_type\nUSER: $user_no\nDAV NAME: $dav_name\nPRINCIPAL ID: " . $row->principal_id );
+pubsub.php:	$t->pubsubCreate ( '', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>' );
+pubsub.php:	$t->pubsubPublish ( '', 'set', '/davical-' . $row->principal_id , '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid );
+pubsub.php:	$t->close();
+RRule.php:  /**#@-*/
+RRule.php:      $this->_text = $input->_text;
+RRule.php:      $this->_epoch = $input->_epoch;
+RRule.php:      $this->_yy = $input->_yy;
+RRule.php:      $this->_mo = $input->_mo;
+RRule.php:      $this->_dd = $input->_dd;
+RRule.php:      $this->_hh = $input->_hh;
+RRule.php:      $this->_mi = $input->_mi;
+RRule.php:      $this->_ss = $input->_ss;
+RRule.php:      $this->_tz = $input->_tz;
+RRule.php:    $this->_wkst = 1; // Monday
+RRule.php:      $this->SetLocalDate($input);
+RRule.php:      $this->SetGMTDate($input);
+RRule.php:      $this->SetLocalDate(strtotime($input));
+RRule.php:      $this->SetEpochDate($input);
+RRule.php:    $this->_text = $input;
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_GMTEpochFromParts();
+RRule.php:    $this->_text = $input;
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_epoch = intval($input);
+RRule.php:    $this->_TextFromEpoch();
+RRule.php:    $this->_PartsFromText();
+RRule.php:    $this->_text = date('Ymd\THis', $this->_epoch );
+RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+RRule.php:    $this->_text = gmdate('Ymd\THis', $this->_epoch );
+RRule.php://    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+RRule.php:    $this->_yy = intval(substr($this->_text,0,4));
+RRule.php:    $this->_mo = intval(substr($this->_text,4,2));
+RRule.php:    $this->_dd = intval(substr($this->_text,6,2));
+RRule.php:    $this->_hh = intval(substr($this->_text,9,2));
+RRule.php:    $this->_mi = intval(substr($this->_text,11,2));
+RRule.php:    $this->_ss = intval(substr($this->_text,13,2));
+RRule.php:    $this->_epoch = gmmktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_epoch = mktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+RRule.php://    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_wkst = $ical_weekdays[$weekstart];
+RRule.php:  function Render( $fmt = 'Y-m-d H:i:s' ) {
+RRule.php:    return date( $fmt, $this->_epoch );
+RRule.php:    return gmdate( $fmt, $this->_epoch );
+RRule.php:  * No of days in a month 1(Jan) - 12(Dec)
+RRule.php:    if ( $mo === false ) $mo = $this->_mo;
+RRule.php:        if ( $yy === false ) $yy = $this->_yy;
+RRule.php:    if ( $dd == $this->_dd ) return; // Shortcut
+RRule.php:    $dd = min($dd,$this->DaysInMonth());
+RRule.php:    $this->_dd = $dd;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Adding %d months to %s", $mo, $this->_text );
+RRule.php:    $this->_mo += $mo;
+RRule.php:    while ( $this->_mo < 1 ) {
+RRule.php:      $this->_mo += 12;
+RRule.php:      $this->_yy--;
+RRule.php:    while ( $this->_mo > 12 ) {
+RRule.php:      $this->_mo -= 12;
+RRule.php:      $this->_yy++;
+RRule.php:    if ( ($this->_dd > 28 && $this->_mo == 2) || $this->_dd > 30 ) {
+RRule.php:      $dim = $this->DaysInMonth();
+RRule.php:      if ( $this->_dd > $dim ) {
+RRule.php:        $this->_dd = $dim;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Added %d months and got %s", $mo, $this->_text );
+RRule.php:    $at_start = $this->_text;
+RRule.php:    $this->_dd += $dd;
+RRule.php:    while ( 1 > $this->_dd ) {
+RRule.php:      $this->_mo--;
+RRule.php:      if ( $this->_mo < 1 ) {
+RRule.php:        $this->_mo += 12;
+RRule.php:        $this->_yy--;
+RRule.php:      $this->_dd += $this->DaysInMonth();
+RRule.php:    while ( ($dim = $this->DaysInMonth($this->_mo)) < $this->_dd ) {
+RRule.php:      $this->_dd -= $dim;
+RRule.php:      $this->_mo++;
+RRule.php:      if ( $this->_mo > 12 ) {
+RRule.php:        $this->_mo -= 12;
+RRule.php:        $this->_yy++;
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php://    dbg_error_log( "RRule", " Added %d days to %s and got %s", $dd, $at_start, $this->_text );
+RRule.php:    $sign = ( $sign == "-" ? -1 : 1);
+RRule.php://    dbg_error_log( "RRule", " Adding duration to '%s' of sign: %d,  days: %s,  time: %s", $this->_text, $sign, $days, $time );
+RRule.php:      $this->AddDays( $days * $sign );
+RRule.php://    dbg_error_log( "RRule", " Adding %02d:%02d:%02d * %d to %02d:%02d:%02d", $hh, $mi, $ss, $sign, $this->_hh, $this->_mi, $this->_ss );
+RRule.php:    $this->_hh += ($hh * $sign);
+RRule.php:    $this->_mi += ($mi * $sign);
+RRule.php:    $this->_ss += ($ss * $sign);
+RRule.php:    if ( $this->_ss < 0 ) {  $this->_mi -= (intval(abs($this->_ss/60))+1); $this->_ss += ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_ss > 59) {  $this->_mi += (intval(abs($this->_ss/60))+1); $this->_ss -= ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_mi < 0 ) {  $this->_hh -= (intval(abs($this->_mi/60))+1); $this->_mi += ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_mi > 59) {  $this->_hh += (intval(abs($this->_mi/60))+1); $this->_mi -= ((intval(abs($this->_mi/60))+1) * 60); }
+RRule.php:    if ( $this->_hh < 0 ) {  $this->AddDays( -1 * (intval(abs($this->_hh/24))+1) );  $this->_hh += ((intval(abs($this->_hh/24))+1)*24);  }
+RRule.php:    if ( $this->_hh > 23) {  $this->AddDays( (intval(abs($this->_hh/24))+1) );       $this->_hh -= ((intval(abs($this->_hh/24))+1)*24);  }
+RRule.php:    $this->_EpochFromParts();
+RRule.php:    $this->_TextFromEpoch();
+RRule.php:    if ( $from->_epoch < $this->_epoch ) {
+RRule.php:      return( "-". $from->DateDifference( $self ) );
+RRule.php://    if ( $from->_yy == $this->_yy && $from->_mo == $this->_mo ) {
+RRule.php:      $diff = $from->_epoch - $this->_epoch;
+RRule.php:*  ==> This code is not needed then :-)
+RRule.php:    $yy = $from->_yy - $this->_yy;
+RRule.php:    $mo = $from->_mo - $this->_mo;
+RRule.php:    $dd = $from->_dd - $this->_dd;
+RRule.php:    $hh = $from->_hh - $this->_hh;
+RRule.php:    $mi = $from->_mi - $this->_mi;
+RRule.php:    $ss = $from->_ss - $this->_ss;
+RRule.php:    if ( $ss < 0 ) {  $mi -= 1;   $ss += 60;  }
+RRule.php:    if ( $mi < 0 ) {  $hh -= 1;   $mi += 60;  }
+RRule.php:    if ( $hh < 0 ) {  $dd -= 1;   $hh += 24;  }
+RRule.php:    if ( $dd < 0 ) {  $mo -= 1;   $dd += $this->DaysInMonth();  } // Which will use $this->_(mo|yy) - seemingly sensible
+RRule.php:    if ( $mo < 0 ) {  $yy -= 1;   $mo += 12;  }
+RRule.php:  * @param string $monthlist A comma-separated list of months.
+RRule.php://    dbg_error_log( "RRule", " Testing BYMONTH %s against month %d", (isset($monthlist) ? $monthlist : "no month list"), $this->_mo );
+RRule.php:    return isset($months[$this->_mo]);
+RRule.php:    $days_in_month = $this->DaysInMonth();
+RRule.php:    $first_dow = (date('w',$this->_epoch) - $this->_dd + 36) % 7;
+RRule.php:      $days = $this->MonthDays($first_dow,$days_in_month,$v);
+RRule.php:    $days_in_month = $this->DaysInMonth();
+RRule.php:    $dow = date('w',$this->_epoch);
+RRule.php:      $dd = $this->_dd - $dow + $daynum;
+RRule.php:      if ( $daynum < $this->_wkst ) $dd += 7;
+RRule.php:      if ( $dd > $this->_dd || !$increasing ) $set[$dd] = $dd;
+RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text );
+RRule.php:      return ( $this->_text > $lesser->_text );
+RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser );
+RRule.php:    return ( $this->_text > $lesser );  // These sorts of dates are designed that way...
+RRule.php://      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text );
+RRule.php:      return ( $this->_text < $greater->_text );
+RRule.php://    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater );
+RRule.php:    return ( $this->_text < $greater );  // These sorts of dates are designed that way...
+RRule.php:  * Given a MonthDays string like "1MO", "-2WE" return an integer day of the month.
+RRule.php:    preg_match( '/([0-9-]*)(MO|TU|WE|TH|FR|SA|SU)/', $dayspec, $matches);
+RRule.php:    $first_matching_day = 1 + ($dow - $dow_first);
+RRule.php:        $numeric--;
+RRule.php:  * Given set position descriptions like '1', '3', '11', '-3' or '-1' and a set,
+RRule.php:    $positions = explode( '[^0-9-]', $bysplist );
+RRule.php:        $v--;
+RRule.php:                ( ";" x-name "=" text )
+RRule.php:     enddate    =/ date-time            ;An UTC value
+RRule.php:     minus      = "-"
+RRule.php:  /**#@-*/
+RRule.php:    $this->_first = new iCalDate($start);
+RRule.php:    $this->_finished = false;
+RRule.php:    $this->_started = false;
+RRule.php:    $this->_dates = array();
+RRule.php:    $this->_current = -1;
+RRule.php:    $this->_rule = preg_replace( '/\s/m', '', $rrule);
+RRule.php:    if ( substr($this->_rule, 0, 6) == 'RRULE:' ) {
+RRule.php:      $this->_rule = substr($this->_rule, 6);
+RRule.php:    dbg_error_log( "RRule", " new RRule: Start: %s, RRULE: %s", $start->Render(), $this->_rule );
+RRule.php:    $parts = explode(';',$this->_rule);
+RRule.php:    $this->_part = array( 'INTERVAL' => 1 );
+RRule.php:      $this->_part[$type] = $value;
+RRule.php:    if ( !isset($this->_part['FREQ']) ) {
+RRule.php:    if ( isset($this->_part['COUNT']) && isset($this->_part['UNTIL'])  ) {
+RRule.php:    if ( isset($this->_part['COUNT']) && intval($this->_part['COUNT']) < 1 ) {
+RRule.php:    if ( !preg_match( '/(YEAR|MONTH|WEEK|DAI)LY/', $this->_part['FREQ']) ) {
+RRule.php:    if ( $this->_part['FREQ'] == "YEARLY" ) {
+RRule.php:      $this->_part['INTERVAL'] *= 12;
+RRule.php:      $this->_part['FREQ'] = "MONTHLY";
+RRule.php:    $ptr = $this->_current;
+RRule.php://    dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() );
+RRule.php:      $days_in_month = $test->DaysInMonth();
+RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing for day %d based on %s, with %d days in month", $day, $test->Render(), $days_in_month );
+RRule.php:        $test->SetMonthDay($days_in_month);
+RRule.php:        $test->AddDays(1);
+RRule.php:        $day -= $days_in_month;
+RRule.php:        $test->SetMonthDay($day);
+RRule.php:        $test->SetMonthDay(1);
+RRule.php:        $test->AddDays(-1);
+RRule.php:        $days_in_month = $test->DaysInMonth();
+RRule.php:        $test->SetMonthDay($day);
+RRule.php:        $test->SetMonthDay($day);
+RRule.php://      dbg_error_log( "RRule", " WithinScope: Testing if %s is within scope", count($relative_days), $test->Render() );
+RRule.php:      if ( isset($this->_part['UNTIL']) && $test->GreaterThan($this->_part['UNTIL']) ) {
+RRule.php:        $this->_finished = true;
+RRule.php:      // if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue;
+RRule.php:      if ( !$test->LessThan($this->_first) ) {
+RRule.php://        dbg_error_log( "RRule", " WithinScope: Looks like %s is within scope", $test->Render() );
+RRule.php:      if ( isset($this->_part['COUNT']) && $ptr >= $this->_part['COUNT'] ) {
+RRule.php:        $this->_finished = true;
+RRule.php:    if ( $this->_current < 0 ) {
+RRule.php:      $next = new iCalDate($this->_first);
+RRule.php:      $this->_current++;
+RRule.php:      $next = new iCalDate($this->_dates[$this->_current]);
+RRule.php:      $this->_current++;
+RRule.php:      if ( isset($this->_dates[$this->_current]) ) {
+RRule.php://        dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+RRule.php:        return $this->_dates[$this->_current];
+RRule.php:        if ( isset($this->_part['COUNT']) && $this->_current >= $this->_part['COUNT'] ) // >= since _current is 0-based and COUNT is 1-based
+RRule.php:          $this->_finished = true;
+RRule.php:    if ( $this->_finished ) {
+RRule.php:    if ( isset($this->_part['WKST']) ) $next->SetWeekStart($this->_part['WKST']);
+RRule.php:    if ( $this->_part['FREQ'] == "MONTHLY" ) {
+RRule.php:        $limit--;
+RRule.php:          $limit--;
+RRule.php:          if ( $this->_started ) {
+RRule.php:            $next->AddMonths($this->_part['INTERVAL']);
+RRule.php:            $this->_started = true;
+RRule.php:        while ( isset($this->_part['BYMONTH']) && $limit > 0 && ! $next->TestByMonth($this->_part['BYMONTH']) );
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetMonthByDay($this->_part['BYDAY']);
+RRule.php:        else if ( isset($this->_part['BYMONTHDAY']) ) {
+RRule.php:          $days = $next->GetMonthByMonthDay($this->_part['BYMONTHDAY']);
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    else if ( $this->_part['FREQ'] == "WEEKLY" ) {
+RRule.php:        $limit--;
+RRule.php:        if ( $this->_started ) {
+RRule.php:          $next->AddDays($this->_part['INTERVAL'] * 7);
+RRule.php:          $this->_started = true;
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], false );
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    else if ( $this->_part['FREQ'] == "DAILY" ) {
+RRule.php:        $limit--;
+RRule.php:        if ( $this->_started ) {
+RRule.php:          $next->AddDays($this->_part['INTERVAL']);
+RRule.php:        if ( isset($this->_part['BYDAY']) ) {
+RRule.php:          $days = $next->GetWeekByDay($this->_part['BYDAY'], $this->_started );
+RRule.php:          $days[$next->_dd] = $next->_dd;
+RRule.php:        if ( isset($this->_part['BYSETPOS']) ) {
+RRule.php:          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+RRule.php:        $days = $this->WithinScope( $next, $days);
+RRule.php:        $this->_started = true;
+RRule.php:      while( $limit && count($days) < 1 && ! $this->_finished );
+RRule.php:    $ptr = $this->_current;
+RRule.php:      $this->_dates[$ptr++] = $v;
+RRule.php:    if ( isset($this->_dates[$this->_current]) ) {
+RRule.php://      dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+RRule.php:      return $this->_dates[$this->_current];
+RRule-v2.php:    $this->tz_defined = false;
+RRule-v2.php:      $this->tz_defined = $dtz;
+RRule-v2.php:        dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
+RRule-v2.php:        $this->tz_defined = false;
+RRule-v2.php:    if ( $this->tz_defined === false ) return false;
+RRule-v2.php:    $tzid = $this->getName();
+RRule-v2.php:    return $this->tz_defined;
+RRule-v2.php:  // public static $Format = 'Y-m-d H:i:s';
+RRule-v2.php:    $this->is_date = false;
+RRule-v2.php:    if ( preg_match('{;?VALUE=DATE[:;]}', $date, $matches) ) $this->is_date = true;
+RRule-v2.php:    elseif ( preg_match('{:([12]\d{3}) (0[1-9]|1[012]) (0[1-9]|[12]\d|3[01]Z?) $}x', $date, $matches) ) $this->is_date = true;
+RRule-v2.php:        $this->tzid = 'UTC';
+RRule-v2.php:        $this->tzid = $dtz->tzid();
+RRule-v2.php:        $this->tzid = null;
+RRule-v2.php:        if ( strlen($matches[1]) == 8 ) $this->is_date = true;
+RRule-v2.php:        $this->tzid = 'UTC';
+RRule-v2.php:        $this->tzid = null;
+RRule-v2.php:      $this->tzid = $dtz->tzid();
+RRule-v2.php:      $this->tzid = $dtz->getName();
+RRule-v2.php:    return (string)parent::format(self::$Format) . ' ' . parent::getTimeZone()->getName();
+RRule-v2.php:    return $this->format('Ymd');
+RRule-v2.php:    if ( preg_match('{^(-)?P((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?$}', $interval, $matches) ) {
+RRule-v2.php://    printf( "Modify '%s' by: >>%s<<\n", $this->__toString(), $interval );
+RRule-v2.php:    return $this->__toString();
+RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+RRule-v2.php:      $offset = 0 - $dtz->getOffset($gmt);
+RRule-v2.php:      $gmt->modify( $offset . ' seconds' );
+RRule-v2.php:    if ( $this->is_date ) return $gmt->format('Ymd');
+RRule-v2.php:    return $gmt->format('Ymd\THis\Z');
+RRule-v2.php:    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+RRule-v2.php:      $result = ';TZID='.$this->tzid;
+RRule-v2.php:    if ( $this->is_date ) {
+RRule-v2.php:      $result .= ';VALUE=DATE:' . $this->format('Ymd');
+RRule-v2.php:      $result .= ':' . $this->format('Ymd\THis');
+RRule-v2.php:      if ( isset($this->tzid) && $this->tzid == 'UTC' ) {
+RRule-v2.php:    return sprintf( 'PT%dM', intval(($end_stamp->epoch() - $this->epoch()) / 60) );
+RRule-v2.php:      $this->tzid = $tz->tzid();
+RRule-v2.php:    $this->base = ( is_object($basedate) ? $basedate : new RepeatRuleDateTime($basedate) );
+RRule-v2.php:    if ( preg_match('{FREQ=([A-Z]+)(;|$)}', $rrule, $m) ) $this->freq = $m[1];
+RRule-v2.php:    if ( preg_match('{UNTIL=([0-9TZ]+)(;|$)}', $rrule, $m) ) $this->until = new RepeatRuleDateTime($m[1]);
+RRule-v2.php:    if ( preg_match('{COUNT=([0-9]+)(;|$)}', $rrule, $m) ) $this->count = $m[1];
+RRule-v2.php:    if ( preg_match('{INTERVAL=([0-9]+)(;|$)}', $rrule, $m) ) $this->interval = $m[1];
+RRule-v2.php:    if ( preg_match('{WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)}', $rrule, $m) ) $this->wkst = $m[1];
+RRule-v2.php:    if ( preg_match('{BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)}', $rrule, $m) )  $this->byday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYYEARDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byyearday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYWEEKNO=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byweekno = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMONTHDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->bymonthday = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)}', $rrule, $m) ) $this->bymonth = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)}', $rrule, $m) ) $this->bysetpos = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYSECOND=([0-9,]+)(;|$)}', $rrule, $m) ) $this->bysecond = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYMINUTE=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byminute = explode(',',$m[1]);
+RRule-v2.php:    if ( preg_match('{BYHOUR=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byhour = explode(',',$m[1]);
+RRule-v2.php:    if ( !isset($this->interval) ) $this->interval = 1;
+RRule-v2.php:    switch( $this->freq ) {
+RRule-v2.php:      case 'SECONDLY': $this->freq_name = 'second'; break;
+RRule-v2.php:      case 'MINUTELY': $this->freq_name = 'minute'; break;
+RRule-v2.php:      case 'HOURLY':   $this->freq_name = 'hour';   break;
+RRule-v2.php:      case 'DAILY':    $this->freq_name = 'day';    break;
+RRule-v2.php:      case 'WEEKLY':   $this->freq_name = 'week';   break;
+RRule-v2.php:      case 'MONTHLY':  $this->freq_name = 'month';  break;
+RRule-v2.php:      case 'YEARLY':   $this->freq_name = 'year';   break;
+RRule-v2.php:    $this->frequency_string = sprintf('+%d %s', $this->interval, $this->freq_name );
+RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Frequency modify string is: '%s', base is: '%s'\n", $this->frequency_string, $this->base->format('c') );
+RRule-v2.php:    $this->Start();
+RRule-v2.php:    $this->base->setTimezone(new DateTimeZone($tzstring));
+RRule-v2.php:    $this->instances = array();
+RRule-v2.php:    $this->GetMoreInstances();
+RRule-v2.php:    $this->rewind();
+RRule-v2.php:    $this->finished = false;
+RRule-v2.php:    $this->position = -1;
+RRule-v2.php:    $this->position++;
+RRule-v2.php:    return $this->current();
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( $GLOBALS['debug_rrule'] ) printf( "Returning date from position %d: %s (%s)\n", $this->position, $this->instances[$this->position]->format('c'), $this->instances[$this->position]->UTC() );
+RRule-v2.php:    return $this->instances[$this->position];
+RRule-v2.php:    if ( !$this->valid() ) return null;
+RRule-v2.php:    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+RRule-v2.php:    if ( !isset($this->keys[$this->position]) ) {
+RRule-v2.php:      $this->keys[$this->position] = $this->instances[$this->position];
+RRule-v2.php:    return $this->keys[$this->position];
+RRule-v2.php:    if ( isset($this->instances[$this->position]) || !$this->finished ) return true;
+RRule-v2.php:    if ( $this->finished ) return;
+RRule-v2.php:    while( !$this->finished && !$got_more && $loops++ < $loop_limit ) {
+RRule-v2.php:      if ( !isset($this->current_base) ) {
+RRule-v2.php:        $this->current_base = clone($this->base);
+RRule-v2.php:        $this->current_base->modify( $this->frequency_string );
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Getting more instances from: '%s' - %d\n", $this->current_base->format('c'), count($this->instances) );
+RRule-v2.php:      $this->current_set = array( clone($this->current_base) );
+RRule-v2.php:      foreach( $rrule_expand_limit[$this->freq] AS $bytype => $action ) {
+RRule-v2.php:        if ( isset($this->{$bytype}) ) $this->{$action.'_'.$bytype}();
+RRule-v2.php:        if ( !isset($this->current_set[0]) ) break;
+RRule-v2.php:      sort($this->current_set);
+RRule-v2.php:      if ( isset($this->bysetpos) ) $this->limit_bysetpos();
+RRule-v2.php:      $position = count($this->instances) - 1;
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Inserting %d from current_set into position %d\n", count($this->current_set), $position + 1 );
+RRule-v2.php:      foreach( $this->current_set AS $k => $instance ) {
+RRule-v2.php:        if ( $instance < $this->base ) continue;
+RRule-v2.php:        if ( isset($this->until) && $instance > $this->until ) {
+RRule-v2.php:          $this->finished = true;
+RRule-v2.php:        if ( !isset($this->instances[$position]) || $instance != $this->instances[$position] ) {
+RRule-v2.php:          $this->instances[$position] = $instance;
+RRule-v2.php:          if ( $GLOBALS['debug_rrule'] ) printf( "Added date %s into position %d in current set\n", $instance->format('c'), $position );
+RRule-v2.php:          if ( isset($this->count) && ($position + 1) >= $this->count ) $this->finished = true;
+RRule-v2.php:    $date_parts = explode(',',$date->format('Y,m,d,H,i,s'));
+RRule-v2.php:    $tz = $date->getTimezone();
+RRule-v2.php:      $date->setDate( $date_parts[0], $date_parts[1], $date_parts[2] );
+RRule-v2.php:      $date->setTime( $date_parts[3], $date_parts[4], $date_parts[5] );
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $expanded = $this->date_mask( clone($instance), null, $month, null, null, null, null);
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYMONTH $month into date %s\n", $expanded->format('c') );
+RRule-v2.php:        $this->current_set[] = $expanded;
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonthday AS $k => $monthday ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
+RRule-v2.php:    $dow_of_instance = $day_in_week->format('w'); // 0 == Sunday
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      $offset = $dow - $dow_of_instance;
+RRule-v2.php:      $expanded->modify( sprintf('+%d day', $offset) );
+RRule-v2.php:      $this->current_set[] = $expanded;
+RRule-v2.php:      if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(W) $weekday into date %s\n", $expanded->format('c') );
+RRule-v2.php:    $first_of_month = $this->date_mask( clone($day_in_month), null, null, 1, null, null, null);
+RRule-v2.php:    $dow_of_first = $first_of_month->format('w'); // 0 == Sunday
+RRule-v2.php:    $days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+RRule-v2.php:        $first_dom = 1 + $dow - $dow_of_first;  if ( $first_dom < 1 ) $first_dom +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(M) $weekday in month of %s\n", $instance->format('c') );
+RRule-v2.php:          $whichweek--;
+RRule-v2.php:          if ( $matches[1] == '-' ) {
+RRule-v2.php:            while( $monthday > $days_in_month ) $monthday -= 7;
+RRule-v2.php:            $monthday -= (7 * $whichweek);
+RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:    $first_of_year = $this->date_mask( clone($day_in_year), null, 1, 1, null, null, null);
+RRule-v2.php:    $dow_of_first = $first_of_year->format('w'); // 0 == Sunday
+RRule-v2.php:    $days_in_year = 337 + cal_days_in_month(CAL_GREGORIAN, 2, $first_of_year->format('Y'));
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+RRule-v2.php:        $first_doy = 1 + $dow - $dow_of_first;  if ( $first_doy < 1 ) $first_doy +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(Y) $weekday from date %s\n", $instance->format('c') );
+RRule-v2.php:          $whichweek--;
+RRule-v2.php:          if ( $matches[1] == '-' ) {
+RRule-v2.php:            while( $yearday > $days_in_year ) $yearday -= 7;
+RRule-v2.php:            $yearday -= (7 * $whichweek);
+RRule-v2.php:            $expanded->modify(sprintf('+%d day', $yearday - 1));
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = $expanded;
+RRule-v2.php:          $expanded->modify(sprintf('+%d day', $first_doy - 1));
+RRule-v2.php:            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+RRule-v2.php:            $this->current_set[] = clone($expanded);
+RRule-v2.php:            $expanded->modify('+1 week');
+RRule-v2.php:    if ( !isset($this->current_set[0]) ) return;
+RRule-v2.php:    if ( $this->freq == 'MONTHLY' || $this->freq == 'YEARLY' ) {
+RRule-v2.php:      if ( isset($this->bymonthday) || isset($this->byyearday) ) {
+RRule-v2.php:        $this->limit_byday();  /** Per RFC5545 3.3.10 from note 1&2 to table */
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      if ( $this->freq == 'MONTHLY' ) {
+RRule-v2.php:        $this->expand_byday_in_month($instance);
+RRule-v2.php:      else if ( $this->freq == 'WEEKLY' ) {
+RRule-v2.php:        $this->expand_byday_in_week($instance);
+RRule-v2.php:        if ( isset($this->bymonth) ) {
+RRule-v2.php:          $this->expand_byday_in_month($instance);
+RRule-v2.php:        else if ( isset($this->byweekno) ) {
+RRule-v2.php:          $this->expand_byday_in_week($instance);
+RRule-v2.php:          $this->expand_byday_in_year($instance);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, $hour, null, null);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $month ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, $minute, null);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->bymonth AS $k => $second ) {
+RRule-v2.php:        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, null, $second);
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:      foreach( $this->{$element_name} AS $k => $element_value ) {
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' ? %s\n", $instance->format('c'), $instance->format($fmt_char), $element_value, ($instance->format($fmt_char) == $element_value ? 'Yes' : 'No') );
+RRule-v2.php:        if ( $instance->format($fmt_char) == $element_value ) $this->current_set[] = $instance;
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:    foreach( $this->byday AS $k => $weekday ) {
+RRule-v2.php:        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' (%d) ? %s\n", $instance->format('c'), $instance->format($fmt_char), $weekday, $dow, ($instance->format($fmt_char) == $dow ? 'Yes' : 'No') );
+RRule-v2.php:        if ( $instance->format($fmt_char) == $dow ) $this->current_set[] = $instance;
+RRule-v2.php:  private function limit_bymonth()    {   $this->limit_generally( 'm', 'bymonth' );     }
+RRule-v2.php:  private function limit_byyearday()  {   $this->limit_generally( 'z', 'byyearday' );   }
+RRule-v2.php:  private function limit_bymonthday() {   $this->limit_generally( 'd', 'bymonthday' );  }
+RRule-v2.php:  private function limit_byhour()     {   $this->limit_generally( 'H', 'byhour' );      }
+RRule-v2.php:  private function limit_byminute()   {   $this->limit_generally( 'i', 'byminute' );    }
+RRule-v2.php:  private function limit_bysecond()   {   $this->limit_generally( 's', 'bysecond' );    }
+RRule-v2.php:    $instances = $this->current_set;
+RRule-v2.php:    $this->current_set = array();
+RRule-v2.php:    foreach( $this->bysetpos AS $k => $element_value ) {
+RRule-v2.php:        $this->current_set[] = $instances[$element_value - 1];
+RRule-v2.php:        $this->current_set[] = $instances[$count + $element_value];
+RRule-v2.php:  $properties = $component->GetProperties($property);
+RRule-v2.php:    $timezone = $p->GetParameterValue('TZID');
+RRule-v2.php:    $rdate = $p->Value();
+RRule-v2.php:      $expansion[$rdate->UTC()] = $component;
+RRule-v2.php:  $recur = $component->GetProperty($property);
+RRule-v2.php:  $recur = $recur->Value();
+RRule-v2.php:  $this_start = $component->GetProperty('DTSTART');
+RRule-v2.php:    $timezone = $this_start->GetParameterValue('TZID');
+RRule-v2.php:    $this_start = new RepeatRuleDateTime($this_start->Value(),$timezone);
+RRule-v2.php:  while( $date = $rule->next() ) {
+RRule-v2.php://    printf( "[%3d] %s\n", $i, $date->UTC() );
+RRule-v2.php:    $expansion[$date->UTC()] = $component;
+RRule-v2.php:* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
+RRule-v2.php:  $components = $vResource->GetComponents();
+RRule-v2.php:  if ( !isset($range_start) ) { $range_start = new RepeatRuleDateTime(); $range_start->modify('-6 weeks'); }
+RRule-v2.php:  if ( !isset($range_end) )   { $range_end   = clone($range_start);      $range_end->modify('+6 months');  }
+RRule-v2.php:    if ( $comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL' ) {
+RRule-v2.php:      $dtstart = $comp->GetProperty('DTSTART');
+RRule-v2.php:      $tzid = $dtstart->GetParameterValue('TZID');
+RRule-v2.php:      $dtstart = new RepeatRuleDateTime( $dtstart->Value(), $tzid );
+RRule-v2.php:      $instances[$dtstart->UTC()] = $comp;
+RRule-v2.php:    $p = $comp->GetProperty('RECURRENCE-ID');
+RRule-v2.php:    if ( isset($p) && $p->Value() != '' ) {
+RRule-v2.php:      $range = $p->GetParameterValue('RANGE');
+RRule-v2.php:      $recur_tzid = $p->GetParameterValue('TZID');
+RRule-v2.php:      $recur_utc = new RepeatRuleDateTime($p->Value(),$recur_tzid);
+RRule-v2.php:      $recur_utc = $recur_utc->UTC();
+RRule-v2.php:  $start_utc = $range_start->UTC();
+RRule-v2.php:  $end_utc = $range_end->UTC();
+RRule-v2.php:    $end_type = ($comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND');
+RRule-v2.php:    $duration = $comp->GetProperty('DURATION');
+RRule-v2.php:    if ( !isset($duration) || $duration->Value() == '' ) {
+RRule-v2.php:      $instance_start = $comp->GetProperty('DTSTART');
+RRule-v2.php:      $dtsrt = new RepeatRuleDateTime( $instance_start->Value(), $instance_start->GetParameterValue('TZID'));
+RRule-v2.php:      $instance_end = $comp->GetProperty($end_type);
+RRule-v2.php:      $dtend = new RepeatRuleDateTime( $instance_end->Value(), $instance_end->GetParameterValue('TZID'));
+RRule-v2.php:      $duration = $dtstart->RFC5545Duration( $dtend );
+RRule-v2.php:      $duration = $duration->Value();
+RRule-v2.php:        $latest_start->modify('-'.$duration);
+RRule-v2.php:        $early_start = $latest_start->UTC();
+RRule-v2.php:    $component->ClearProperties( array('DTSTART'=> true, 'DUE' => true, 'DTEND' => true) );
+RRule-v2.php:    $component->AddProperty('DTSTART', $utc );
+RRule-v2.php:    $component->AddProperty('DURATION', $duration );
+RRule-v2.php:    $vResource->SetComponents($new_components);
+RRule-v2.php:    $vResource->SetComponents(array());
+test-RRULE.php:header("Content-type: text/plain");
+test-RRULE.php:    $this->description = $description;
+test-RRULE.php:    $this->dtstart = $start;
+test-RRULE.php:    $this->recur = $recur;
+test-RRULE.php:    $this->result_description = $result_description;
+test-RRULE.php:    $rule = new RRule( new iCalDate($this->dtstart), $this->recur );
+test-RRULE.php:      $date = $rule->GetNext();
+test-RRULE.php:        $result .= "   " . $date->Render();
+test-RRULE.php:    $qry = new AwlQuery($sql, $this->dtstart, $this->recur);
+test-RRULE.php:    // printf( "%s\n", $qry->querystring);
+test-RRULE.php:    if ( $qry->Exec("test") && $qry->rows > 0 ) {
+test-RRULE.php:      while( $row = $qry->Fetch() ) {
+test-RRULE.php:        $result .= "   " . $row->event_date;
+test-RRULE.php:  , new RRuleTest( "Monthly, on the 1st monday, 2nd wednesday, 3rd friday and last sunday, forever", "20061117T073000", "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" )
+test-RRULE.php:  , new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" )
+test-RRULE.php:  , new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
+test-RRULE.php:  , new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
+test-RRULE.php:  echo "$test->dtstart - $test->recur\n";
+test-RRULE.php:  echo "$test->description\n";
+test-RRULE.php:  $php_result = $test->PHPTest();
+test-RRULE.php:  $sql_result = $test->SQLTest();
+test-RRULE.php:    echo "PHP & SQL results are identical :-)\n";
+test-RRULE.php:    echo "PHP & SQL results differ :-(\n";
+vcard.php:  fn TEXT, -- fullname
+vcard.php:  n TEXT, -- Name Surname;First names
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $properties = $this->GetProperties( $wanted );
+vcard.php:      $pname = ':' . strtolower($v->Name());
+vcard.php:      if ( !isset($params[$pname]) /** @TODO: or this is one is in the user's language */ ) $params[$pname] = $v->Value();
+vcard.php:    $qry->QDo( $sql, $params );
+vcard.php:    $this->WriteAddresses($dav_id);
+vcard.php:    $this->WritePhones($dav_id);
+vcard.php:    $this->WriteEmails($dav_id);
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $addresses = $this->GetProperties('ADR');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_adr WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $adr->GetParameterValue('TYPE');
+vcard.php:      $address = explode(';',$adr->Value());
+vcard.php:      $params[':property'] = $adr->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_adr (dav_id, type, box_no, unit_no, street_address, locality, region, postcode, country, property)
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $telephones = $this->GetProperties('TEL');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_tel WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $tel->GetParameterValue('TYPE');
+vcard.php:      $params[':tel'] = $tel->Value();
+vcard.php:      $params[':property'] = $tel->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_tel (dav_id, type, tel, property) VALUES( :dav_id, :type, :tel, :property)', $params );
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
+vcard.php:  property TEXT -- The full text of the property
+vcard.php:    $emails = $this->GetProperties('EMAIL');
+vcard.php:    $in_transaction = ($qry->TransactionState() == 1);
+vcard.php:    if ( ! $in_transaction ) $qry->Begin();
+vcard.php:    $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params );
+vcard.php:      $params[':type'] = $email->GetParameterValue('TYPE');
+vcard.php:      $params[':email'] = $email->Value();
+vcard.php:      $params[':property'] = $email->Render();
+vcard.php:      $qry->QDo( 'INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params );
+vcard.php:    if ( ! $in_transaction ) $qry->Commit();
Index: contrib/davical/inc/caldav-REPORT-freebusy.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-freebusy.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-freebusy.php	(revision 3733)
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Handle the FREE-BUSY-QUERY variant of REPORT
+ */
+include_once("freebusy-functions.php");
+
+$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
+$fbq_start = $fbq_content[0]->GetAttribute('start');
+$fbq_end   = $fbq_content[0]->GetAttribute('end');
+if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
+  $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+}
+$range_start = new RepeatRuleDateTime($fbq_start);
+$range_end   = new RepeatRuleDateTime($fbq_end);
+
+
+/** We use the same code for the REPORT, the POST and the freebusy GET... */
+$freebusy = get_freebusy( '^'.$request->path.$request->DepthRegexTail(), $range_start, $range_end );
+
+$result = new iCalComponent();
+$result->VCalendar();
+$result->AddComponent($freebusy);
+
+$request->DoResponse( 200, $result->Render(), 'text/calendar' );
+// Won't return from that
+
Index: contrib/davical/inc/caldav-REPORT-calquery.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-calquery.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-calquery.php	(revision 3733)
@@ -0,0 +1,379 @@
+<?php
+
+$need_expansion = false;
+function check_for_expansion( $calendar_data_node ) {
+  global $need_expansion, $expand_range_start, $expand_range_end;
+
+  if ( !class_exists('DateTime') ) return; /** We don't support expansion on PHP5.1 */
+
+  $expansion = $calendar_data_node->GetElements('urn:ietf:params:xml:ns:caldav:expand');
+  if ( isset($expansion[0]) ) {
+    $need_expansion = true;
+    $expand_range_start = $expansion[0]->GetAttribute('start');
+    $expand_range_end = $expansion[0]->GetAttribute('end');
+    if ( isset($expand_range_start) ) $expand_range_start = new RepeatRuleDateTime($expand_range_start);
+    if ( isset($expand_range_end) )   $expand_range_end   = new RepeatRuleDateTime($expand_range_end);
+  }
+}
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:calendar-query');
+$proptype = $qry_content[0]->GetTag();
+$properties = array();
+switch( $proptype ) {
+  case 'DAV::prop':
+    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/'.$proptype.'/*');
+    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+      $properties[$propertyname] = 1;
+      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+    }
+    break;
+
+  case 'DAV::allprop':
+    $properties['allprop'] = 1;
+    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:caldav:calendar-data' ) check_for_expansion($v);
+      }
+    }
+    break;
+
+  default:
+    $propertyname = preg_replace( '/^.*:/', '', $proptype );
+    $properties[$propertyname] = 1;
+}
+
+/**
+ * There can only be *one* FILTER element, and it must contain *one* COMP-FILTER
+ * element.  In every case I can see this contained COMP-FILTER element will be a
+ * VCALENDAR, but perhaps there are others.  In our case we strip it if that is
+ * the case and leave it alone otherwise.
+ */
+$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:caldav:calendar-query/urn:ietf:params:xml:ns:caldav:filter/*');
+if ( count($qry_filters) == 1 ) {
+  $qry_filters = $qry_filters[0];  // There can only be one FILTER element
+  if ( $qry_filters->GetTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $qry_filters->GetAttribute("name") == "VCALENDAR" )
+    $qry_filters = $qry_filters->GetContent();  // Everything is inside a VCALENDAR AFAICS
+  else {
+    dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $qry_filters->GetTag(), $qry_filters->GetAttribute("name") );
+    $qry_filters = false;
+  }
+}
+else {
+  $qry_filters = false;
+}
+
+
+/**
+* While we can construct our SQL to apply some filters in the query, other filters
+* need to be checked against the retrieved record.  This is for handling those ones.
+*
+* @param array $filter An array of XMLElement which is the filter definition
+* @param string $item The database row retrieved for this calendar item
+*
+* @return boolean True if the check succeeded, false otherwise.
+*/
+function apply_filter( $filters, $item ) {
+  global $session, $c, $request;
+
+  if ( count($filters) == 0 ) return true;
+
+  dbg_error_log("calquery","Applying filter for item '%s'", $item->dav_name );
+  $ical = new iCalendar( array( "icalendar" => $item->caldav_data) );
+  return $ical->TestFilter($filters);
+}
+
+
+/**
+ * Process a filter fragment returning an SQL fragment
+ */
+$need_post_filter = false;
+function SqlFilterFragment( $filter, $components, $property = null, $parameter = null ) {
+  global $need_post_filter, $target_collection;
+  $sql = "";
+  $params = array();
+  if ( !is_array($filter) ) {
+    dbg_error_log( "calquery", "Filter is of type '%s', but should be an array of XML Tags.", gettype($filter) );
+  }
+
+  foreach( $filter AS $k => $v ) {
+    $tag = $v->GetTag();
+    dbg_error_log("calquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+
+    $not_defined = "";
+    switch( $tag ) {
+      case 'urn:ietf:params:xml:ns:caldav:is-not-defined':
+        $not_defined = "not-"; // then fall through to IS-DEFINED case
+      case 'urn:ietf:params:xml:ns:caldav:is-defined':
+        if ( isset( $parameter ) ) {
+          $need_post_filter = true;
+          dbg_error_log("calquery", "Could not handle 'is-%sdefined' on property %s, parameter %s in SQL", $not_defined, $property, $parameter );
+          return false;  // Not handled in SQL
+        }
+        if ( isset( $property ) ) {
+          switch( $property ) {
+            case 'created':
+            case 'completed':  /** @todo when it can be handled in the SQL - see around line 200 below */
+            case 'dtend':
+            case 'dtstamp':
+            case 'dtstart':
+              if ( ! $target_collection->IsSchedulingCollection() ) {
+                $property_defined_match = "IS NOT NULL";
+              }
+              break;
+
+            case 'priority':
+              $property_defined_match = "IS NOT NULL";
+              break;
+
+            default:
+              $property_defined_match = "LIKE '_%'";  // i.e. contains a single character or more
+          }
+          $sql .= sprintf( "AND %s %s%s ", $property, $not_defined, $property_defined_match );
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:time-range':
+        /**
+        * @todo We should probably allow time range queries against other properties, since eventually some client may want to do this.
+        */
+        $start_column = ($components[sizeof($components)-1] == 'VTODO' ? "due" : 'dtend');     // The column we compare against the START attribute
+        $finish_column = 'dtstart';  // The column we compare against the END attribute
+        $start = $v->GetAttribute("start");
+        $finish = $v->GetAttribute("end");
+        if ( isset($start) || isset($finish) ) {
+          $sql .= ' AND (rrule_event_overlaps( dtstart, dtend, rrule, :time_range_start, :time_range_end ) OR event_has_exceptions(caldav_data.caldav_data) ) ';
+          $params[':time_range_start'] = $start;
+          $params[':time_range_end'] = $finish;
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:text-match':
+        $search = $v->GetContent();
+        $negate = $v->GetAttribute("negate-condition");
+        $collation = $v->GetAttribute("collation");
+        switch( strtolower($collation) ) {
+          case 'i;octet':
+            $comparison = 'LIKE';
+            break;
+          case 'i;ascii-casemap':
+          default:
+            $comparison = 'ILIKE';
+            break;
+        }
+        $params[':text_match'] = '%'.$search.'%';
+        dbg_error_log("calquery", " text-match: (%s IS NULL OR %s%s %s '%s') ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+                                          $property, $comparison, $params[':text_match'] );
+        $sql .= sprintf( "AND (%s IS NULL OR %s%s %s :text_match) ", $property, (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+                                          $property, $comparison );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:comp-filter':
+        $comp_filter_name = $v->GetAttribute("name");
+        if ( count($components) == 0 ) {
+          $sql .= "AND caldav_data.caldav_type = :component_name_filter ";
+          $params[':component_name_filter'] = $comp_filter_name;
+        }
+        $components[] = $comp_filter_name;
+        $subfilter = $v->GetContent();
+        if ( is_array( $subfilter ) ) {
+          $success = SqlFilterFragment( $subfilter, $components, $property, $parameter );
+          if ( $success === false ) continue; else {
+            $sql .= $success['sql'];
+            $params = array_merge( $params, $success['params'] );
+          }
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:prop-filter':
+        $propertyname = $v->GetAttribute("name");
+        switch( $propertyname ) {
+          case 'PERCENT-COMPLETE':
+            $property = 'percent_complete';
+            break;
+
+          case 'UID':
+          case 'SUMMARY':
+          case 'LOCATION':
+          case 'DESCRIPTION':
+          case 'CLASS':
+          case 'TRANSP':
+          case 'RRULE':  // Likely that this is not much use
+          case 'URL':
+          case 'STATUS':
+          case 'CREATED':
+          case 'DTSTAMP':
+          case 'DTSTART':
+          case 'DTEND':
+          case 'DUE':
+          case 'PRIORITY':
+            $property = strtolower($propertyname);
+            break;
+
+          case 'COMPLETED':  /** @todo this should be moved into the properties supported in SQL. */
+          default:
+            $need_post_filter = true;
+            dbg_error_log("calquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+            continue;
+        }
+        $subfilter = $v->GetContent();
+        $success = SqlFilterFragment( $subfilter, $components, $property, $parameter );
+        if ( $success === false ) continue; else {
+          $sql .= $success['sql'];
+          $params = array_merge( $params, $success['params'] );
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:param-filter':
+        $need_post_filter = true;
+        return false; // Can't handle PARAM-FILTER conditions in the SQL
+        $parameter = $v->GetAttribute("name");
+        $subfilter = $v->GetContent();
+        $success = SqlFilterFragment( $subfilter, $components, $property, $parameter );
+        if ( $success === false ) continue; else {
+          $sql .= $success['sql'];
+          $params = array_merge( $params, $success['params'] );
+        }
+        break;
+
+      default:
+        dbg_error_log("calquery", "Could not handle unknown tag '%s' in calendar query report", $tag );
+        break;
+    }
+  }
+  dbg_error_log("calquery", "Generated SQL was '%s'", $sql );
+  return array( 'sql' => $sql, 'params' => $params );
+}
+
+/**
+ * Build an SQL 'WHERE' clause which implements (parts of) the filter. The
+ * elements of the filter which are implemented in the SQL will be removed.
+ *
+ * @param arrayref &$filter A reference to an array of XMLElement defining the filter
+ *
+ * @return string A string suitable for use as an SQL 'WHERE' clause selecting the desired records.
+ */
+function BuildSqlFilter( $filter ) {
+  $components = array();
+  return SqlFilterFragment( $filter, $components );
+}
+
+
+/**
+* Something that we can handle, at least roughly correctly.
+*/
+
+$responses = array();
+$target_collection = new DAVResource($request->path);
+$bound_from = $target_collection->bound_from();
+if ( !$target_collection->Exists() ) {
+  $request->DoResponse( 404 );
+}
+if ( ! ($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection()) ) {
+  $request->DoResponse( 403, translate('The calendar-query report must be run against a calendar or a scheduling collection') );
+}
+
+/**
+* @todo Once we are past DB version 1.2.1 we can change this query more radically.  The best performance to
+* date seems to be:
+*   SELECT caldav_data.*,calendar_item.* FROM collection JOIN calendar_item USING (collection_id,user_no)
+*         JOIN caldav_data USING (dav_id) WHERE collection.dav_name = '/user1/home/'
+*              AND caldav_data.caldav_type = 'VEVENT' ORDER BY caldav_data.user_no, caldav_data.dav_name;
+*/
+
+$params = array();
+//$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+//if ( is_array($qry_filters) ) {
+//  dbg_log_array( "calquery", "qry_filters", $qry_filters, true );
+//  $components = array();
+//  $filter_fragment =  SqlFilterFragment( $qry_filters, $components );
+//  if ( $filter_fragment !== false ) {
+//    $where .= ' '.$filter_fragment['sql'];
+//    $params = $filter_fragment['params'];
+//  }
+//}
+//if ( $target_collection->Privileges() != privilege_to_bits('DAV::all') ) {
+//  $where .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
+//}
+
+//if ( isset($c->hide_TODO) && $c->hide_TODO && ! $target_collection->HavePrivilegeTo('DAV::write-content') ) {
+//  $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') ";
+//}
+
+//if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+ // $where .= " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+//}
+
+//$sql = 'SELECT * FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name)'. $where;
+//if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+//$qry = new AwlQuery( $sql, $params );
+//if ( $qry->Exec("calquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+//  while( $calendar_object = $qry->Fetch() ) {
+//    if ( !$need_post_filter || apply_filter( $qry_filters, $calendar_object ) ) {
+//      if ( $bound_from != $target_collection->dav_name() ) {
+//        $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+//      }
+//      if ( $need_expansion ) {
+//        $vResource = new vComponent($calendar_object->caldav_data);
+//        $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+//        $calendar_object->caldav_data = $expanded->Render();
+//      }
+//      $responses[] = calendar_to_xml( $properties, $calendar_object );
+//    }
+//  }
+//}
+$nome = $target_collection->GetProperty('user_no');
+  if ( $target_collection->Privileges() != privilege_to_bits('DAV::all') ) {
+    $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
+  }
+
+  if ( isset($c->hide_TODO) && $c->hide_TODO && ! $target_collection->Privileges() == privilege_to_bits('all') ) {
+    $where .= "AND caldav_data.caldav_type NOT IN ('VTODO') ";
+  }
+  $sqlp = "SELECT cal_id FROM phpgw_cal_user WHERE  cal_type = 'u' AND cal_status = 'A' AND cal_login = :nome";
+    $qryp = new AwlQuery( $sqlp, array( ':nome' => $nome) );
+    if ( $qryp->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qryp->rows() > 0 ) {
+       while( $part = $qryp->Fetch() ) {
+           $sql = "SELECT * FROM phpgw_cal WHERE cal_id = $part->cal_id AND cal_type = 'E'";
+           $qry = new AwlQuery( $sql);
+           if ( $qry->Exec("REPORT-MULTIGET",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+              while( $calendar_object = $qry->Fetch() ) {
+                if ( $bound_from != $target_collection->dav_name() ) {
+                      $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+                    }
+                if ( $need_expansion ) {
+                   $vResource = new vComponent($calendar_object->caldav_data);
+                   $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                   $calendar_object->caldav_data = $expanded->Render();
+                     }
+                  $responses[] = calendar_to_xml( $properties, $calendar_object );
+              }
+           }
+            $sql = "SELECT * FROM phpgw_cal INNER JOIN phpgw_cal_repeats USING(cal_id) WHERE cal_id = $part->cal_id";
+            $qry = new AwlQuery( $sql);
+            if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+                  while( $calendar_object = $qry->Fetch() ) {
+                    if ( $bound_from != $target_collection->dav_name() ) {
+                      $calendar_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
+                    }
+                    if ( $need_expansion ) {
+                       $vResource = new vComponent($calendar_object->caldav_data);
+                        $expanded = expand_event_instances($vResource, $expand_range_start, $expand_range_end);
+                       $calendar_object->caldav_data = $expanded->Render();
+                     }
+                    $responses[] = calendar_to_xml( $properties, $calendar_object );
+               }
+           }
+      }
+   }
+
+
+
+
+$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/caldav-ACL.php
===================================================================
--- contrib/davical/inc/caldav-ACL.php	(revision 3733)
+++ contrib/davical/inc/caldav-ACL.php	(revision 3733)
@@ -0,0 +1,201 @@
+<?php
+/**
+* CalDAV Server - handle ACL method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+dbg_error_log("ACL", "method handler");
+
+require_once('DAVResource.php');
+
+$request->NeedPrivilege('DAV::write-acl');
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+  $fh = fopen('/tmp/MOVE.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+$resource = new DAVResource( $request->path );
+
+/**
+* Preconditions
+   (DAV:no-ace-conflict): The ACEs submitted in the ACL request MUST NOT
+   conflict with each other.  This is a catchall error code indicating
+   that an implementation-specific ACL restriction has been violated.
+
+   (DAV:no-protected-ace-conflict): The ACEs submitted in the ACL
+   request MUST NOT conflict with the protected ACEs on the resource.
+   For example, if the resource has a protected ACE granting DAV:write
+   to a given principal, then it would not be consistent if the ACL
+   request submitted an ACE denying DAV:write to the same principal.
+
+   (DAV:no-inherited-ace-conflict): The ACEs submitted in the ACL
+   request MUST NOT conflict with the inherited ACEs on the resource.
+   For example, if the resource inherits an ACE from its parent
+   collection granting DAV:write to a given principal, then it would not
+   be consistent if the ACL request submitted an ACE denying DAV:write
+   to the same principal.  Note that reporting of this error will be
+   implementation-dependent.  Implementations MUST either report this
+   error or allow the ACE to be set, and then let normal ACE evaluation
+   rules determine whether the new ACE has any impact on the privileges
+   available to a specific principal.
+
+   (DAV:limited-number-of-aces): The number of ACEs submitted in the ACL
+   request MUST NOT exceed the number of ACEs allowed on that resource.
+   However, ACL-compliant servers MUST support at least one ACE granting
+   privileges to a single principal, and one ACE granting privileges to
+   a group.
+
+   (DAV:deny-before-grant): All non-inherited deny ACEs MUST precede all
+   non-inherited grant ACEs.
+
+   (DAV:grant-only): The ACEs submitted in the ACL request MUST NOT
+   include a deny ACE.  This precondition applies only when the ACL
+   restrictions of the resource include the DAV:grant-only constraint
+   (defined in Section 5.6.1).
+
+   (DAV:no-invert):  The ACL request MUST NOT include a DAV:invert
+   element.  This precondition applies only when the ACL semantics of
+   the resource includes the DAV:no-invert constraint (defined in
+   Section 5.6.2).
+
+   (DAV:no-abstract): The ACL request MUST NOT attempt to grant or deny
+   an abstract privilege (see Section 5.3).
+
+   (DAV:not-supported-privilege): The ACEs submitted in the ACL request
+   MUST be supported by the resource.
+
+   (DAV:missing-required-principal): The result of the ACL request MUST
+   have at least one ACE for each principal identified in a
+   DAV:required-principal XML element in the ACL semantics of that
+   resource (see Section 5.5).
+
+   (DAV:recognized-principal): Every principal URL in the ACL request
+   MUST identify a principal resource.
+
+   (DAV:allowed-principal): The principals specified in the ACEs
+   submitted in the ACL request MUST be allowed as principals for the
+   resource.  For example, a server where only authenticated principals
+   can access resources would not allow the DAV:all or
+   DAV:unauthenticated principals to be used in an ACE, since these
+   would allow unauthenticated access to resources.
+*/
+
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+$aces = $xmltree->GetPath("/DAV::acl/*");
+
+$grantor = new DAVResource($request->path);
+if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
+$by_principal  = null;
+$by_collection = null;
+if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id');
+else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id');
+else $request->PreconditionFailed(403,'not-supported-privilege','ACLs may only be applied to Principals or Collections');
+
+$qry = new AwlQuery('BEGIN');
+$qry->Exec('ACL',__LINE__,__FILE__);
+
+foreach( $aces AS $k => $ace ) {
+  $elements = $ace->GetContent();
+  $principal = $elements[0];
+  $grant = $elements[1];
+  if ( $principal->GetTag() != 'DAV::principal' ) $request->MalformedRequest('ACL request must contain a principal, not '.$principal->GetTag());
+  $grant_tag = $grant->GetTag();
+  if ( $grant_tag == 'DAV::deny' )   $request->PreconditionFailed(403,'grant-only');
+  if ( $grant_tag == 'DAV::invert' ) $request->PreconditionFailed(403,'no-invert');
+  if ( $grant->GetTag() != 'DAV::grant' ) $request->MalformedRequest('ACL request must contain a principal for each ACE');
+
+  $privilege_names = array();
+  $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
+  foreach( $xml_privs AS $k => $priv ) {
+    $privilege_names[] = $priv->GetTag();
+  }
+  $privileges = privilege_to_bits($privilege_names);
+
+  $principal_content = $principal->GetContent();
+  if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
+  $principal_content = $principal_content[0];
+  switch( $principal_content->GetTag() ) {
+    case 'DAV::property':
+      $principal_property = $principal_content->GetContent();
+      if ( $principal_property[0]->GetTag() != 'DAV::owner' ) $request->PreconditionFailed(403, 'recognized-principal' );
+      if ( privilege_to_bits('all') != $privileges ) {
+        $request->PreconditionFailed(403, 'no-protected-ace-conflict', 'Owner must always have all permissions' );
+      }
+      continue;  // and then we ignore it, since it's protected
+      break;
+
+    case 'DAV::unauthenticated':
+      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+      break;
+
+    case 'DAV::href':
+      $principal_type = 'href';
+      $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) );
+      if ( ! $principal->Exists() || !$principal->IsPrincipal() )
+        $request->PreconditionFailed(403,'recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
+      $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_id') );
+      $where = 'WHERE to_principal=:to_principal AND ';
+      if ( isset($by_principal) ) {
+        $sqlparms[':by_principal'] = $by_principal;
+        $where .= 'by_principal = :by_principal';
+      }
+      else {
+        $sqlparms[':by_collection'] = $by_collection;
+        $where .= 'by_collection = :by_collection';
+      }
+      $qry = new AwlQuery('SELECT privileges FROM grants '.$where, $sqlparms);
+      if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
+        $sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) '.$where;
+      }
+      else {
+        $sqlparms[':by_principal'] = $by_principal;
+        $sqlparms[':by_collection'] = $by_collection;
+        $sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))';
+      }
+      $sqlparms[':privileges'] = $privileges;
+      $qry = new AwlQuery($sql, $sqlparms);
+      $qry->Exec('ACL',__LINE__,__FILE__);
+      break;
+
+    case 'DAV::authenticated':
+      $principal_type = 'authenticated';
+      if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
+      $sqlparms = array( ':privileges' => $privileges );
+      if ( isset($by_collection) ) {
+        $sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection';
+        $sqlparms[':by_collection'] = $by_collection;
+      }
+      else {
+        $sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal';
+        $sqlparms[':by_principal'] = $by_principal;
+      }
+      $qry = new AwlQuery($sql, $sqlparms);
+      $qry->Exec('ACL',__LINE__,__FILE__);
+      break;
+
+    case 'DAV::all':
+//      $principal_type = 'all';
+      $request->PreconditionFailed(403, 'allowed-principal', 'May not set privileges for unauthenticated users' );
+      break;
+
+    default:
+      $request->PreconditionFailed(403, 'recognized-principal' );
+      break;
+  }
+
+}
+
+$qry = new AwlQuery('COMMIT');
+$qry->Exec('ACL',__LINE__,__FILE__);
+
+
+$request->DoResponse( 200 );
Index: contrib/davical/inc/caldav-PROPPATCH.php
===================================================================
--- contrib/davical/inc/caldav-PROPPATCH.php	(revision 3733)
+++ contrib/davical/inc/caldav-PROPPATCH.php	(revision 3733)
@@ -0,0 +1,323 @@
+<?php
+/**
+* CalDAV Server - handle PROPPATCH method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+dbg_error_log("PROPPATCH", "method handler");
+
+require_once('iCalendar.php');
+require_once('DAVResource.php');
+
+$dav_resource = new DAVResource($request->path);
+if ( ! ($dav_resource->HavePrivilegeTo('DAV::write-properties') || $dav_resource->IsBinding() ) ) {
+  $request->DoResponse( 403 );
+}
+
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+
+// echo $xmltree->Render();
+
+if ( $xmltree->GetTag() != "DAV::propertyupdate" ) {
+  $request->DoResponse( 403 );
+}
+
+/**
+* Find the properties being set, and the properties being removed
+*/
+$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
+$rmprops  = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
+
+/**
+* We build full status responses for failures.  For success we just record
+* it, since the multistatus response only applies to failure.  While it is
+* not explicitly stated in RFC2518, from reading between the lines (8.2.1)
+* a success will return 200 OK [with an empty response].
+*/
+$failure   = array();
+$success   = array();
+
+/**
+* Not much for it but to process the incoming settings in a big loop, doing
+* the special-case stuff as needed and falling through to a default which
+* stuffs the property somewhere we will be able to retrieve it from later.
+*/
+$qry = new AwlQuery();
+$qry->Begin();
+$setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+foreach( $setprops AS $k => $setting ) {
+  $tag = $setting->GetTag();
+  $content = $setting->RenderContent();
+
+  switch( $tag ) {
+
+    case 'DAV::displayname':
+      /**
+      * Can't set displayname on resources - only collections or principals
+      */
+      if ( $dav_resource->IsCollection() || $dav_resource->IsPrincipal() ) {
+        if ( $dav_resource->IsBinding() ) {
+          $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name',
+                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+        }
+        else if ( $dav_resource->IsPrincipal() ) {
+          $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no',
+                                            array( ':displayname' => $content, ':user_no' => $request->user_no) );
+        }
+        else {
+          $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name',
+                                            array( ':displayname' => $content, ':dav_name' => $dav_resource->dav_name()) );
+        }
+        $success[$tag] = 1;
+      }
+      else {
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($tag)),
+            new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+            new XMLElement( 'responsedescription', array(
+                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                              translate("The displayname may only be set on collections, principals or bindings.") )
+                          )
+
+        ));
+      }
+      break;
+
+    case 'DAV::resourcetype':
+      /**
+      * We only allow resourcetype setting on a normal collection, and not on a resource, a principal or a bind.
+      * Only collections may be CalDAV calendars or addressbooks, and they may not be both.
+      */
+      $setcollection  = count($setting->GetPath('DAV::resourcetype/DAV::collection'));
+      $setaddressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+      if ( $dav_resource->IsCollection() && $setcollection && ! $dav_resource->IsPrincipal()
+                            && ! $dav_resource->IsBinding() && ! ($setaddressbook && $setcalendar) ) {
+        $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+        $resourcetypes = str_replace( "\n", "", implode('',$resourcetypes));
+        $qry->QDo('UPDATE collection SET is_calendar = :is_calendar::boolean, is_addressbook = :is_addressbook::boolean,
+                     resourcetypes = :resourcetypes WHERE dav_name = :dav_name',
+                    array( ':dav_name' => $dav_resource->dav_name(), ':resourcetypes' => $resourcetypes,
+                           ':is_calendar' => $setcalendar, ':is_addressbook' => $setaddressbook ) );
+        $success[$tag] = 1;
+      }
+      else {
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($tag)),
+            new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+            new XMLElement( 'responsedescription', array(
+                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                              translate("Resources may not be changed to / from collections.") )
+                          )
+        ));
+      }
+      break;
+
+    case 'urn:ietf:params:xml:ns:caldav:schedule-calendar-transp':
+      if ( $dav_resource->IsCollection() && ( $dav_resource->IsCalendar() || $setcalendar ) && !$dav_resource->IsBinding() ) {
+        $transparency = $setting->GetPath('urn:ietf:params:xml:ns:caldav:schedule-calendar-transp/*');
+        $transparency = preg_replace( '{^.*:}', '', $transparency[0]->GetTag());
+        $qry->QDo('UPDATE collection SET schedule_transp = :transparency WHERE dav_name = :dav_name',
+                    array( ':dav_name' => $dav_resource->dav_name(), ':transparency' => $transparency ) );
+        $success[$tag] = 1;
+      }
+      else {
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($tag)),
+              new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+              new XMLElement( 'responsedescription', array(
+                                new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                                translate("The CalDAV:schedule-calendar-transp property may only be set on calendars.") )
+                            )
+        ));
+      }
+      break;
+
+    case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+          new XMLElement( 'prop', new XMLElement($tag)),
+          new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
+          new XMLElement( 'responsedescription', translate("The calendar-free-busy-set is superseded by the schedule-transp property of a calendar collection.") )
+      ));
+      break;
+
+    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+        $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone');
+        $tzstring = $tzcomponent[0]->GetContent();
+        $calendar = new iCalendar( array( 'icalendar' => $tzstring) );
+        $timezones = $calendar->component->GetComponents('VTIMEZONE');
+        if ( $timezones === false || count($timezones) == 0 ) break;
+        $tz = $timezones[0];  // Backward compatibility
+        $tzid = $tz->GetPValue('TZID');
+        $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name',
+                                       array( ':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()) );
+      }
+      else {
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($tag)),
+            new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+            new XMLElement( 'responsedescription', array(
+                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                              translate("calendar-timezone property is only valid for a calendar.") )
+                          )
+        ));
+      }
+      break;
+
+    /**
+    * The following properties are read-only, so they will cause the request to fail
+    */
+    case 'http://calendarserver.org/ns/:getctag':
+    case 'DAV::owner':
+    case 'DAV::principal-collection-set':
+    case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+    case 'DAV::getetag':
+    case 'DAV::getcontentlength':
+    case 'DAV::getcontenttype':
+    case 'DAV::getlastmodified':
+    case 'DAV::creationdate':
+    case 'DAV::lockdiscovery':
+    case 'DAV::supportedlock':
+      $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+          new XMLElement( 'prop', new XMLElement($tag)),
+          new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+          new XMLElement( 'responsedescription', array(
+                               new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                               translate("Property is read-only") )
+                        )
+      ));
+      break;
+
+    /**
+    * If we don't have any special processing for the property, we just store it verbatim (which will be an XML fragment).
+    */
+    default:
+      $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text)',
+            array( ':dav_name' => $dav_resource->dav_name(), ':user_no' => $request->user_no, ':tag' => $tag, ':value' => $content) );
+      $success[$tag] = 1;
+      break;
+  }
+}
+
+
+
+foreach( $rmprops AS $k => $setting ) {
+  $tag = $setting->GetTag();
+  $content = $setting->RenderContent();
+
+  switch( $tag ) {
+
+    case 'DAV::resourcetype':
+      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+          new XMLElement( 'prop', new XMLElement($tag)),
+            new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+            new XMLElement( 'responsedescription', array(
+                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                              translate("DAV::resourcetype may only be set to a new value, it may not be removed.") )
+                          )
+      ));
+      break;
+
+    case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
+      if ( $dav_resource->IsCollection() && $dav_resource->IsCalendar() && ! $dav_resource->IsBinding() ) {
+        $qry->QDo('UPDATE collection SET timezone = NULL WHERE dav_name = :dav_name', array( ':dav_name' => $dav_resource->dav_name()) );
+      }
+      else {
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($tag)),
+            new XMLElement( 'status', 'HTTP/1.1 403 Forbidden' ),
+            new XMLElement( 'responsedescription', array(
+                              new XMLElement( 'error', new XMLElement( 'cannot-modify-protected-property') ),
+                              translate("calendar-timezone property is only valid for a calendar.") )
+                          )
+        ));
+      }
+      break;
+
+    /**
+    * The following properties are read-only, so they will cause the request to fail
+    */
+    case 'http://calendarserver.org/ns/:getctag':
+    case 'DAV::owner':
+    case 'DAV::principal-collection-set':
+    case 'urn:ietf:params:xml:ns:caldav:CALENDAR-USER-ADDRESS-SET':
+    case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+    case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+    case 'DAV::getetag':
+    case 'DAV::getcontentlength':
+    case 'DAV::getcontenttype':
+    case 'DAV::getlastmodified':
+    case 'DAV::creationdate':
+    case 'DAV::displayname':
+    case 'DAV::lockdiscovery':
+    case 'DAV::supportedlock':
+      $failure['rm-'.$tag] = new XMLElement( 'propstat', array(
+          new XMLElement( 'prop', new XMLElement($tag)),
+          new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
+          new XMLElement('responsedescription', translate("Property is read-only") )
+      ));
+      dbg_error_log( 'PROPPATCH', ' RMProperty %s is read only and cannot be removed', $tag);
+      break;
+
+    /**
+    * If we don't have any special processing then we must have to just delete it.  Nonexistence is not failure.
+    */
+    default:
+      $qry->QDo('DELETE FROM property WHERE dav_name=:dav_name AND property_name=:property_name',
+                  array( ':dav_name' => $dav_resource->dav_name(), ':property_name' => $tag) );
+      $success[$tag] = 1;
+      break;
+  }
+}
+
+
+/**
+* If we have encountered any instances of failure, the whole damn thing fails.
+*/
+if ( count($failure) > 0 ) {
+  foreach( $success AS $tag => $v ) {
+    // Unfortunately although these succeeded, we failed overall, so they didn't happen...
+    $failure[] = new XMLElement( 'propstat', array(
+        new XMLElement( 'prop', new XMLElement($tag)),
+        new XMLElement( 'status', 'HTTP/1.1 424 Failed Dependency' ),
+    ));
+  }
+
+  $url = ConstructURL($request->path);
+  array_unshift( $failure, new XMLElement('href', $url ) );
+  $failure[] = new XMLElement('responsedescription', translate("Some properties were not able to be changed.") );
+
+  $qry->Rollback();
+
+  $multistatus = new XMLElement( "multistatus", new XMLElement( 'response', $failure ), array('xmlns'=>'DAV:') );
+  $request->DoResponse( 207, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+
+}
+
+/**
+* Otherwise we will try and do the SQL. This is inside a transaction, so PostgreSQL guarantees the atomicity
+*/
+;
+if ( $qry->Commit() ) {
+  $url = ConstructURL($request->path);
+  $href = new XMLElement('href', $url );
+  $desc = new XMLElement('responsedescription', translate("All requested changes were made.") );
+
+  $multistatus = new XMLElement( "multistatus", new XMLElement( 'response', array( $href, $desc ) ), array('xmlns'=>'DAV:') );
+  $request->DoResponse( 200, $multistatus->Render(0,'<?xml version="1.0" encoding="utf-8" ?>'), 'text/xml; charset="utf-8"' );
+}
+
+/**
+* Or it was all crap.
+*/
+$request->DoResponse( 500 );
+
+exit(0);
+
Index: contrib/davical/inc/RRule.php
===================================================================
--- contrib/davical/inc/RRule.php	(revision 3733)
+++ contrib/davical/inc/RRule.php	(revision 3733)
@@ -0,0 +1,966 @@
+<?php
+/**
+* Class for parsing RRule and getting us the dates
+*
+* @package   awl
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst .Net Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+$ical_weekdays = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 );
+
+/**
+* A Class for handling dates in iCalendar format.  We do make the simplifying assumption
+* that all date handling in here is normalised to GMT.  One day we might provide some
+* functions to do that, but for now it is done externally.
+*
+* @package awl
+*/
+class iCalDate {
+  /**#@+
+  * @access private
+  */
+
+  /** Text version */
+  var $_text;
+
+  /** Epoch version */
+  var $_epoch;
+
+  /** Fragmented parts */
+  var $_yy;
+  var $_mo;
+  var $_dd;
+  var $_hh;
+  var $_mi;
+  var $_ss;
+  var $_tz;
+
+  /** Which day of the week does the week start on */
+  var $_wkst;
+
+  /**#@-*/
+
+  /**
+  * The constructor takes either an iCalendar date, a text string formatted as
+  * an iCalendar date, or epoch seconds.
+  */
+  function iCalDate( $input ) {
+    if ( gettype($input) == 'object' ) {
+      $this->_text = $input->_text;
+      $this->_epoch = $input->_epoch;
+      $this->_yy = $input->_yy;
+      $this->_mo = $input->_mo;
+      $this->_dd = $input->_dd;
+      $this->_hh = $input->_hh;
+      $this->_mi = $input->_mi;
+      $this->_ss = $input->_ss;
+      $this->_tz = $input->_tz;
+      return;
+    }
+
+    $this->_wkst = 1; // Monday
+    if ( preg_match( '/^\d{8}[T ]\d{6}$/', $input ) ) {
+      $this->SetLocalDate($input);
+    }
+    else if ( preg_match( '/^\d{8}[T ]\d{6}Z$/', $input ) ) {
+      $this->SetGMTDate($input);
+    }
+    else if ( intval($input) == 0 ) {
+      $this->SetLocalDate(strtotime($input));
+      return;
+    }
+    else {
+      $this->SetEpochDate($input);
+    }
+  }
+
+
+  /**
+  * Set the date from a text string
+  */
+  function SetGMTDate( $input ) {
+    $this->_text = $input;
+    $this->_PartsFromText();
+    $this->_GMTEpochFromParts();
+  }
+
+
+  /**
+  * Set the date from a text string
+  */
+  function SetLocalDate( $input ) {
+    $this->_text = $input;
+    $this->_PartsFromText();
+    $this->_EpochFromParts();
+  }
+
+
+  /**
+  * Set the date from an epoch
+  */
+  function SetEpochDate( $input ) {
+    $this->_epoch = intval($input);
+    $this->_TextFromEpoch();
+    $this->_PartsFromText();
+  }
+
+
+  /**
+  * Given an epoch date, convert it to text
+  */
+  function _TextFromEpoch() {
+    $this->_text = date('Ymd\THis', $this->_epoch );
+//    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+  }
+
+  /**
+  * Given a GMT epoch date, convert it to text
+  */
+  function _GMTTextFromEpoch() {
+    $this->_text = gmdate('Ymd\THis', $this->_epoch );
+//    dbg_error_log( "RRule", " Text %s from epoch %d", $this->_text, $this->_epoch );
+  }
+
+  /**
+  * Given a text date, convert it to parts
+  */
+  function _PartsFromText() {
+    $this->_yy = intval(substr($this->_text,0,4));
+    $this->_mo = intval(substr($this->_text,4,2));
+    $this->_dd = intval(substr($this->_text,6,2));
+    $this->_hh = intval(substr($this->_text,9,2));
+    $this->_mi = intval(substr($this->_text,11,2));
+    $this->_ss = intval(substr($this->_text,13,2));
+  }
+
+
+  /**
+  * Given a GMT text date, convert it to an epoch
+  */
+  function _GMTEpochFromParts() {
+    $this->_epoch = gmmktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+//    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+  }
+
+
+  /**
+  * Given a local text date, convert it to an epoch
+  */
+  function _EpochFromParts() {
+    $this->_epoch = mktime ( $this->_hh, $this->_mi, $this->_ss, $this->_mo, $this->_dd, $this->_yy );
+//    dbg_error_log( "RRule", " Epoch %d from %04d-%02d-%02d %02d:%02d:%02d", $this->_epoch, $this->_yy, $this->_mo, $this->_dd, $this->_hh, $this->_mi, $this->_ss );
+  }
+
+
+  /**
+  * Set the day of week used for calculation of week starts
+  *
+  * @param string $weekstart The day of the week which is the first business day.
+  */
+  function SetWeekStart($weekstart) {
+    global $ical_weekdays;
+    $this->_wkst = $ical_weekdays[$weekstart];
+  }
+
+
+  /**
+  * Set the day of week used for calculation of week starts
+  */
+  function Render( $fmt = 'Y-m-d H:i:s' ) {
+    return date( $fmt, $this->_epoch );
+  }
+
+
+  /**
+  * Render the date as GMT
+  */
+  function RenderGMT( $fmt = 'Ymd\THis\Z' ) {
+    return gmdate( $fmt, $this->_epoch );
+  }
+
+
+  /**
+  * No of days in a month 1(Jan) - 12(Dec)
+  */
+  function DaysInMonth( $mo=false, $yy=false ) {
+    if ( $mo === false ) $mo = $this->_mo;
+    switch( $mo ) {
+      case  1: // January
+      case  3: // March
+      case  5: // May
+      case  7: // July
+      case  8: // August
+      case 10: // October
+      case 12: // December
+        return 31;
+        break;
+
+      case  4: // April
+      case  6: // June
+      case  9: // September
+      case 11: // November
+        return 30;
+        break;
+
+      case  2: // February
+        if ( $yy === false ) $yy = $this->_yy;
+        if ( (($yy % 4) == 0) && ((($yy % 100) != 0) || (($yy % 400) == 0) ) ) return 29;
+        return 28;
+        break;
+
+      default:
+        dbg_error_log( "ERROR"," Invalid month of '%s' passed to DaysInMonth", $mo );
+        break;
+
+    }
+  }
+
+
+  /**
+  * Set the day in the month to what we have been given
+  */
+  function SetMonthDay( $dd ) {
+    if ( $dd == $this->_dd ) return; // Shortcut
+    $dd = min($dd,$this->DaysInMonth());
+    $this->_dd = $dd;
+    $this->_EpochFromParts();
+    $this->_TextFromEpoch();
+  }
+
+
+  /**
+  * Add some number of months to a date
+  */
+  function AddMonths( $mo ) {
+//    dbg_error_log( "RRule", " Adding %d months to %s", $mo, $this->_text );
+    $this->_mo += $mo;
+    while ( $this->_mo < 1 ) {
+      $this->_mo += 12;
+      $this->_yy--;
+    }
+    while ( $this->_mo > 12 ) {
+      $this->_mo -= 12;
+      $this->_yy++;
+    }
+
+    if ( ($this->_dd > 28 && $this->_mo == 2) || $this->_dd > 30 ) {
+      // Ensure the day of month is still reasonable and coerce to last day of month if needed
+      $dim = $this->DaysInMonth();
+      if ( $this->_dd > $dim ) {
+        $this->_dd = $dim;
+      }
+    }
+    $this->_EpochFromParts();
+    $this->_TextFromEpoch();
+//    dbg_error_log( "RRule", " Added %d months and got %s", $mo, $this->_text );
+  }
+
+
+  /**
+  * Add some integer number of days to a date
+  */
+  function AddDays( $dd ) {
+    $at_start = $this->_text;
+    $this->_dd += $dd;
+    while ( 1 > $this->_dd ) {
+      $this->_mo--;
+      if ( $this->_mo < 1 ) {
+        $this->_mo += 12;
+        $this->_yy--;
+      }
+      $this->_dd += $this->DaysInMonth();
+    }
+    while ( ($dim = $this->DaysInMonth($this->_mo)) < $this->_dd ) {
+      $this->_dd -= $dim;
+      $this->_mo++;
+      if ( $this->_mo > 12 ) {
+        $this->_mo -= 12;
+        $this->_yy++;
+      }
+    }
+    $this->_EpochFromParts();
+    $this->_TextFromEpoch();
+//    dbg_error_log( "RRule", " Added %d days to %s and got %s", $dd, $at_start, $this->_text );
+  }
+
+
+  /**
+  * Add duration
+  */
+  function AddDuration( $duration ) {
+    if ( strstr($duration,'T') === false ) $duration .= 'T';
+    list( $sign, $days, $time ) = preg_split( '/[PT]/', $duration );
+    $sign = ( $sign == "-" ? -1 : 1);
+//    dbg_error_log( "RRule", " Adding duration to '%s' of sign: %d,  days: %s,  time: %s", $this->_text, $sign, $days, $time );
+    if ( preg_match( '/(\d+)(D|W)/', $days, $matches ) ) {
+      $days = intval($matches[1]);
+      if ( $matches[2] == 'W' ) $days *= 7;
+      $this->AddDays( $days * $sign );
+    }
+    $hh = 0;    $mi = 0;    $ss = 0;
+    if ( preg_match( '/(\d+)(H)/', $time, $matches ) )  $hh = $matches[1];
+    if ( preg_match( '/(\d+)(M)/', $time, $matches ) )  $mi = $matches[1];
+    if ( preg_match( '/(\d+)(S)/', $time, $matches ) )  $ss = $matches[1];
+
+//    dbg_error_log( "RRule", " Adding %02d:%02d:%02d * %d to %02d:%02d:%02d", $hh, $mi, $ss, $sign, $this->_hh, $this->_mi, $this->_ss );
+    $this->_hh += ($hh * $sign);
+    $this->_mi += ($mi * $sign);
+    $this->_ss += ($ss * $sign);
+
+    if ( $this->_ss < 0 ) {  $this->_mi -= (intval(abs($this->_ss/60))+1); $this->_ss += ((intval(abs($this->_mi/60))+1) * 60); }
+    if ( $this->_ss > 59) {  $this->_mi += (intval(abs($this->_ss/60))+1); $this->_ss -= ((intval(abs($this->_mi/60))+1) * 60); }
+    if ( $this->_mi < 0 ) {  $this->_hh -= (intval(abs($this->_mi/60))+1); $this->_mi += ((intval(abs($this->_mi/60))+1) * 60); }
+    if ( $this->_mi > 59) {  $this->_hh += (intval(abs($this->_mi/60))+1); $this->_mi -= ((intval(abs($this->_mi/60))+1) * 60); }
+    if ( $this->_hh < 0 ) {  $this->AddDays( -1 * (intval(abs($this->_hh/24))+1) );  $this->_hh += ((intval(abs($this->_hh/24))+1)*24);  }
+    if ( $this->_hh > 23) {  $this->AddDays( (intval(abs($this->_hh/24))+1) );       $this->_hh -= ((intval(abs($this->_hh/24))+1)*24);  }
+
+    $this->_EpochFromParts();
+    $this->_TextFromEpoch();
+  }
+
+
+  /**
+  * Produce an iCalendar format DURATION for the difference between this an another iCalDate
+  *
+  * @param date $from The start of the period
+  * @return string The date difference, as an iCalendar duration format
+  */
+  function DateDifference( $from ) {
+    if ( !is_object($from) ) {
+      $from = new iCalDate($from);
+    }
+    if ( $from->_epoch < $this->_epoch ) {
+      /** One way to simplify is to always go for positive differences */
+      return( "-". $from->DateDifference( $self ) );
+    }
+//    if ( $from->_yy == $this->_yy && $from->_mo == $this->_mo ) {
+      /** Also somewhat simpler if we can use seconds */
+      $diff = $from->_epoch - $this->_epoch;
+      $result = "";
+      if ( $diff >= 86400) {
+        $result = intval($diff / 86400);
+        $diff = $diff % 86400;
+        if ( $diff == 0 && (($result % 7) == 0) ) {
+          // Duration is an integer number of weeks.
+          $result .= intval($result / 7) . "W";
+          return $result;
+        }
+        $result .= "D";
+      }
+      $result = "P".$result."T";
+      if ( $diff >= 3600) {
+        $result .= intval($diff / 3600) . "H";
+        $diff = $diff % 3600;
+      }
+      if ( $diff >= 60) {
+        $result .= intval($diff / 60) . "M";
+        $diff = $diff % 60;
+      }
+      if ( $diff > 0) {
+        $result .= intval($diff) . "S";
+      }
+      return $result;
+//    }
+
+/**
+* From an intense reading of RFC2445 it appears that durations which are not expressible
+* in Weeks/Days/Hours/Minutes/Seconds are invalid.
+*  ==> This code is not needed then :-)
+    $yy = $from->_yy - $this->_yy;
+    $mo = $from->_mo - $this->_mo;
+    $dd = $from->_dd - $this->_dd;
+    $hh = $from->_hh - $this->_hh;
+    $mi = $from->_mi - $this->_mi;
+    $ss = $from->_ss - $this->_ss;
+
+    if ( $ss < 0 ) {  $mi -= 1;   $ss += 60;  }
+    if ( $mi < 0 ) {  $hh -= 1;   $mi += 60;  }
+    if ( $hh < 0 ) {  $dd -= 1;   $hh += 24;  }
+    if ( $dd < 0 ) {  $mo -= 1;   $dd += $this->DaysInMonth();  } // Which will use $this->_(mo|yy) - seemingly sensible
+    if ( $mo < 0 ) {  $yy -= 1;   $mo += 12;  }
+
+    $result = "";
+    if ( $yy > 0) {    $result .= $yy."Y";   }
+    if ( $mo > 0) {    $result .= $mo."M";   }
+    if ( $dd > 0) {    $result .= $dd."D";   }
+    $result .= "T";
+    if ( $hh > 0) {    $result .= $hh."H";   }
+    if ( $mi > 0) {    $result .= $mi."M";   }
+    if ( $ss > 0) {    $result .= $ss."S";   }
+    return $result;
+*/
+  }
+
+  /**
+  * Test to see if our _mo matches something in the list of months we have received.
+  * @param string $monthlist A comma-separated list of months.
+  * @return boolean Whether this date falls within one of those months.
+  */
+  function TestByMonth( $monthlist ) {
+//    dbg_error_log( "RRule", " Testing BYMONTH %s against month %d", (isset($monthlist) ? $monthlist : "no month list"), $this->_mo );
+    if ( !isset($monthlist) ) return true;  // If BYMONTH is not specified any month is OK
+    $months = array_flip(explode( ',',$monthlist ));
+    return isset($months[$this->_mo]);
+  }
+
+  /**
+  * Applies any BYDAY to the month to return a set of days
+  * @param string $byday The BYDAY rule
+  * @return array An array of the day numbers for the month which meet the rule.
+  */
+  function GetMonthByDay($byday) {
+//    dbg_error_log( "RRule", " Applying BYDAY %s to month", $byday );
+    $days_in_month = $this->DaysInMonth();
+    $dayrules = explode(',',$byday);
+    $set = array();
+    $first_dow = (date('w',$this->_epoch) - $this->_dd + 36) % 7;
+    foreach( $dayrules AS $k => $v ) {
+      $days = $this->MonthDays($first_dow,$days_in_month,$v);
+      foreach( $days AS $k2 => $v2 ) {
+        $set[$v2] = $v2;
+      }
+    }
+    asort( $set, SORT_NUMERIC );
+    return $set;
+  }
+
+  /**
+  * Applies any BYMONTHDAY to the month to return a set of days
+  * @param string $bymonthday The BYMONTHDAY rule
+  * @return array An array of the day numbers for the month which meet the rule.
+  */
+  function GetMonthByMonthDay($bymonthday) {
+//    dbg_error_log( "RRule", " Applying BYMONTHDAY %s to month", $bymonthday );
+    $days_in_month = $this->DaysInMonth();
+    $dayrules = explode(',',$bymonthday);
+    $set = array();
+    foreach( $dayrules AS $k => $v ) {
+      $v = intval($v);
+      if ( $v > 0 && $v <= $days_in_month ) $set[$v] = $v;
+    }
+    asort( $set, SORT_NUMERIC );
+    return $set;
+  }
+
+
+  /**
+  * Applies any BYDAY to the week to return a set of days
+  * @param string $byday The BYDAY rule
+  * @param string $increasing When we are moving by months, we want any day of the week, but when by day we only want to increase. Default false.
+  * @return array An array of the day numbers for the week which meet the rule.
+  */
+  function GetWeekByDay($byday, $increasing = false) {
+    global $ical_weekdays;
+//    dbg_error_log( "RRule", " Applying BYDAY %s to week", $byday );
+    $days = explode(',',$byday);
+    $dow = date('w',$this->_epoch);
+    $set = array();
+    foreach( $days AS $k => $v ) {
+      $daynum = $ical_weekdays[$v];
+      $dd = $this->_dd - $dow + $daynum;
+      if ( $daynum < $this->_wkst ) $dd += 7;
+      if ( $dd > $this->_dd || !$increasing ) $set[$dd] = $dd;
+    }
+    asort( $set, SORT_NUMERIC );
+
+    return $set;
+  }
+
+
+  /**
+  * Test if $this is greater than the date parameter
+  * @param string $lesser The other date, as a local time string
+  * @return boolean True if $this > $lesser
+  */
+  function GreaterThan($lesser) {
+    if ( is_object($lesser) ) {
+//      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser->_text );
+      return ( $this->_text > $lesser->_text );
+    }
+//    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $lesser );
+    return ( $this->_text > $lesser );  // These sorts of dates are designed that way...
+  }
+
+
+  /**
+  * Test if $this is less than the date parameter
+  * @param string $greater The other date, as a local time string
+  * @return boolean True if $this < $greater
+  */
+  function LessThan($greater) {
+    if ( is_object($greater) ) {
+//      dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater->_text );
+      return ( $this->_text < $greater->_text );
+    }
+//    dbg_error_log( "RRule", " Comparing %s with %s", $this->_text, $greater );
+    return ( $this->_text < $greater );  // These sorts of dates are designed that way...
+  }
+
+
+  /**
+  * Given a MonthDays string like "1MO", "-2WE" return an integer day of the month.
+  *
+  * @param string $dow_first The day of week of the first of the month.
+  * @param string $days_in_month The number of days in the month.
+  * @param string $dayspec The specification for a month day (or days) which we parse.
+  *
+  * @return array An array of the day numbers for the month which meet the rule.
+  */
+  function &MonthDays($dow_first, $days_in_month, $dayspec) {
+    global $ical_weekdays;
+//    dbg_error_log( "RRule", "MonthDays: Getting days for '%s'. %d days starting on a %d", $dayspec, $days_in_month, $dow_first );
+    $set = array();
+    preg_match( '/([0-9-]*)(MO|TU|WE|TH|FR|SA|SU)/', $dayspec, $matches);
+    $numeric = intval($matches[1]);
+    $dow = $ical_weekdays[$matches[2]];
+
+    $first_matching_day = 1 + ($dow - $dow_first);
+    while ( $first_matching_day < 1 ) $first_matching_day += 7;
+
+//    dbg_error_log( "RRule", " MonthDays: Looking at %d for first match on (%s/%s), %d for numeric", $first_matching_day, $matches[1], $matches[2], $numeric );
+
+    while( $first_matching_day <= $days_in_month ) {
+      $set[] = $first_matching_day;
+      $first_matching_day += 7;
+    }
+
+    if ( $numeric != 0 ) {
+      if ( $numeric < 0 ) {
+        $numeric += count($set);
+      }
+      else {
+        $numeric--;
+      }
+      $answer = $set[$numeric];
+      $set = array( $answer => $answer );
+    }
+    else {
+      $answers = $set;
+      $set = array();
+      foreach( $answers AS $k => $v ) {
+        $set[$v] = $v;
+      }
+    }
+
+//    dbg_log_array( "RRule", 'MonthDays', $set, false );
+
+    return $set;
+  }
+
+
+  /**
+  * Given set position descriptions like '1', '3', '11', '-3' or '-1' and a set,
+  * return the subset matching the list of set positions.
+  *
+  * @param string $bysplist  The list of set positions.
+  * @param string $set The set of days that we will apply the positions to.
+  *
+  * @return array The subset which matches.
+  */
+  function &ApplyBySetPos($bysplist, $set) {
+//    dbg_error_log( "RRule", " ApplyBySetPos: Applying set position '%s' to set of %d days", $bysplist, count($set) );
+    $subset = array();
+    sort( $set, SORT_NUMERIC );
+    $max = count($set);
+    $positions = explode( '[^0-9-]', $bysplist );
+    foreach( $positions AS $k => $v ) {
+      if ( $v < 0 ) {
+        $v += $max;
+      }
+      else {
+        $v--;
+      }
+      $subset[$set[$v]] = $set[$v];
+    }
+    return $subset;
+  }
+}
+
+
+
+/**
+* A Class for handling Events on a calendar which repeat
+*
+* Here's the spec, from RFC2445:
+*
+     recur      = "FREQ"=freq *(
+
+                ; either UNTIL or COUNT may appear in a 'recur',
+                ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
+
+                ( ";" "UNTIL" "=" enddate ) /
+                ( ";" "COUNT" "=" 1*DIGIT ) /
+
+                ; the rest of these keywords are optional,
+                ; but MUST NOT occur more than once
+
+                ( ";" "INTERVAL" "=" 1*DIGIT )          /
+                ( ";" "BYSECOND" "=" byseclist )        /
+                ( ";" "BYMINUTE" "=" byminlist )        /
+                ( ";" "BYHOUR" "=" byhrlist )           /
+                ( ";" "BYDAY" "=" bywdaylist )          /
+                ( ";" "BYMONTHDAY" "=" bymodaylist )    /
+                ( ";" "BYYEARDAY" "=" byyrdaylist )     /
+                ( ";" "BYWEEKNO" "=" bywknolist )       /
+                ( ";" "BYMONTH" "=" bymolist )          /
+                ( ";" "BYSETPOS" "=" bysplist )         /
+                ( ";" "WKST" "=" weekday )              /
+                ( ";" x-name "=" text )
+                )
+
+     freq       = "SECONDLY" / "MINUTELY" / "HOURLY" / "DAILY"
+                / "WEEKLY" / "MONTHLY" / "YEARLY"
+
+     enddate    = date
+     enddate    =/ date-time            ;An UTC value
+
+     byseclist  = seconds / ( seconds *("," seconds) )
+
+     seconds    = 1DIGIT / 2DIGIT       ;0 to 59
+
+     byminlist  = minutes / ( minutes *("," minutes) )
+
+     minutes    = 1DIGIT / 2DIGIT       ;0 to 59
+
+     byhrlist   = hour / ( hour *("," hour) )
+
+     hour       = 1DIGIT / 2DIGIT       ;0 to 23
+
+     bywdaylist = weekdaynum / ( weekdaynum *("," weekdaynum) )
+
+     weekdaynum = [([plus] ordwk / minus ordwk)] weekday
+
+     plus       = "+"
+
+     minus      = "-"
+
+     ordwk      = 1DIGIT / 2DIGIT       ;1 to 53
+
+     weekday    = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
+     ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
+     ;FRIDAY, SATURDAY and SUNDAY days of the week.
+
+     bymodaylist = monthdaynum / ( monthdaynum *("," monthdaynum) )
+
+     monthdaynum = ([plus] ordmoday) / (minus ordmoday)
+
+     ordmoday   = 1DIGIT / 2DIGIT       ;1 to 31
+
+     byyrdaylist = yeardaynum / ( yeardaynum *("," yeardaynum) )
+
+     yeardaynum = ([plus] ordyrday) / (minus ordyrday)
+
+     ordyrday   = 1DIGIT / 2DIGIT / 3DIGIT      ;1 to 366
+
+     bywknolist = weeknum / ( weeknum *("," weeknum) )
+
+     weeknum    = ([plus] ordwk) / (minus ordwk)
+
+     bymolist   = monthnum / ( monthnum *("," monthnum) )
+
+     monthnum   = 1DIGIT / 2DIGIT       ;1 to 12
+
+     bysplist   = setposday / ( setposday *("," setposday) )
+
+     setposday  = yeardaynum
+*
+* At this point we are going to restrict ourselves to parts of the RRULE specification
+* seen in the wild.  And by "in the wild" I don't include within people's timezone
+* definitions.  We always convert time zones to canonical names and assume the lower
+* level libraries can do a better job with them than we can.
+*
+* We will concentrate on:
+*  FREQ=(YEARLY|MONTHLY|WEEKLY|DAILY)
+*  UNTIL=
+*  COUNT=
+*  INTERVAL=
+*  BYDAY=
+*  BYMONTHDAY=
+*  BYSETPOS=
+*  WKST=
+*  BYYEARDAY=
+*  BYWEEKNO=
+*  BYMONTH=
+*
+*
+* @package awl
+*/
+class RRule {
+  /**#@+
+  * @access private
+  */
+
+  /** The first instance */
+  var $_first;
+
+  /** The current instance pointer */
+  var $_current;
+
+  /** An array of all the dates so far */
+  var $_dates;
+
+  /** Whether we have calculated any of the dates */
+  var $_started;
+
+  /** Whether we have calculated all of the dates */
+  var $_finished;
+
+  /** The rule, in all it's glory */
+  var $_rule;
+
+  /** The rule, in all it's parts */
+  var $_part;
+
+  /**#@-*/
+
+  /**
+  * The constructor takes a start date and an RRULE definition.  Both of these
+  * follow the iCalendar standard.
+  */
+  function RRule( $start, $rrule ) {
+    $this->_first = new iCalDate($start);
+    $this->_finished = false;
+    $this->_started = false;
+    $this->_dates = array();
+    $this->_current = -1;
+
+    $this->_rule = preg_replace( '/\s/m', '', $rrule);
+    if ( substr($this->_rule, 0, 6) == 'RRULE:' ) {
+      $this->_rule = substr($this->_rule, 6);
+    }
+
+    dbg_error_log( "RRule", " new RRule: Start: %s, RRULE: %s", $start->Render(), $this->_rule );
+
+    $parts = explode(';',$this->_rule);
+    $this->_part = array( 'INTERVAL' => 1 );
+    foreach( $parts AS $k => $v ) {
+      list( $type, $value ) = explode( '=', $v, 2);
+//      dbg_error_log( "RRule", " Parts of %s explode into %s and %s", $v, $type, $value );
+      $this->_part[$type] = $value;
+    }
+
+    // A little bit of validation
+    if ( !isset($this->_part['FREQ']) ) {
+      dbg_error_log( "ERROR", " RRULE MUST have FREQ=value (%s)", $rrule );
+    }
+    if ( isset($this->_part['COUNT']) && isset($this->_part['UNTIL'])  ) {
+      dbg_error_log( "ERROR", " RRULE MUST NOT have both COUNT=value and UNTIL=value (%s)", $rrule );
+    }
+    if ( isset($this->_part['COUNT']) && intval($this->_part['COUNT']) < 1 ) {
+      dbg_error_log( "ERROR", " RRULE MUST NOT have both COUNT=value and UNTIL=value (%s)", $rrule );
+    }
+    if ( !preg_match( '/(YEAR|MONTH|WEEK|DAI)LY/', $this->_part['FREQ']) ) {
+      dbg_error_log( "ERROR", " RRULE Only FREQ=(YEARLY|MONTHLY|WEEKLY|DAILY) are supported at present (%s)", $rrule );
+    }
+    if ( $this->_part['FREQ'] == "YEARLY" ) {
+      $this->_part['INTERVAL'] *= 12;
+      $this->_part['FREQ'] = "MONTHLY";
+    }
+  }
+
+
+  /**
+  * Processes the array of $relative_days to $base and removes any
+  * which are not within the scope of our rule.
+  */
+  function WithinScope( $base, $relative_days ) {
+
+    $ok_days = array();
+
+    $ptr = $this->_current;
+
+//    dbg_error_log( "RRule", " WithinScope: Processing list of %d days relative to %s", count($relative_days), $base->Render() );
+    foreach( $relative_days AS $day => $v ) {
+
+      $test = new iCalDate($base);
+      $days_in_month = $test->DaysInMonth();
+
+//      dbg_error_log( "RRule", " WithinScope: Testing for day %d based on %s, with %d days in month", $day, $test->Render(), $days_in_month );
+      if ( $day > $days_in_month ) {
+        $test->SetMonthDay($days_in_month);
+        $test->AddDays(1);
+        $day -= $days_in_month;
+        $test->SetMonthDay($day);
+      }
+      else if ( $day < 1 ) {
+        $test->SetMonthDay(1);
+        $test->AddDays(-1);
+        $days_in_month = $test->DaysInMonth();
+        $day += $days_in_month;
+        $test->SetMonthDay($day);
+      }
+      else {
+        $test->SetMonthDay($day);
+      }
+
+//      dbg_error_log( "RRule", " WithinScope: Testing if %s is within scope", count($relative_days), $test->Render() );
+
+      if ( isset($this->_part['UNTIL']) && $test->GreaterThan($this->_part['UNTIL']) ) {
+        $this->_finished = true;
+        return $ok_days;
+      }
+
+      // if ( $this->_current >= 0 && $test->LessThan($this->_dates[$this->_current]) ) continue;
+
+      if ( !$test->LessThan($this->_first) ) {
+//        dbg_error_log( "RRule", " WithinScope: Looks like %s is within scope", $test->Render() );
+        $ok_days[$day] = $test;
+        $ptr++;
+      }
+
+      if ( isset($this->_part['COUNT']) && $ptr >= $this->_part['COUNT'] ) {
+        $this->_finished = true;
+        return $ok_days;
+      }
+
+    }
+
+    return $ok_days;
+  }
+
+
+  /**
+  * This is most of the meat of the RRULE processing, where we find the next date.
+  * We maintain an
+  */
+  function &GetNext( ) {
+
+    if ( $this->_current < 0 ) {
+      $next = new iCalDate($this->_first);
+      $this->_current++;
+    }
+    else {
+      $next = new iCalDate($this->_dates[$this->_current]);
+      $this->_current++;
+
+      /**
+      * If we have already found some dates we may just be able to return one of those.
+      */
+      if ( isset($this->_dates[$this->_current]) ) {
+//        dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+        return $this->_dates[$this->_current];
+      }
+      else {
+        if ( isset($this->_part['COUNT']) && $this->_current >= $this->_part['COUNT'] ) // >= since _current is 0-based and COUNT is 1-based
+          $this->_finished = true;
+      }
+    }
+
+    if ( $this->_finished ) {
+      $next = null;
+      return $next;
+    }
+
+    $days = array();
+    if ( isset($this->_part['WKST']) ) $next->SetWeekStart($this->_part['WKST']);
+    if ( $this->_part['FREQ'] == "MONTHLY" ) {
+//      dbg_error_log( "RRule", " GetNext: Calculating more dates for MONTHLY rule" );
+      $limit = 200;
+      do {
+        $limit--;
+        do {
+          $limit--;
+          if ( $this->_started ) {
+            $next->AddMonths($this->_part['INTERVAL']);
+          }
+          else {
+            $this->_started = true;
+          }
+        }
+        while ( isset($this->_part['BYMONTH']) && $limit > 0 && ! $next->TestByMonth($this->_part['BYMONTH']) );
+
+        if ( isset($this->_part['BYDAY']) ) {
+          $days = $next->GetMonthByDay($this->_part['BYDAY']);
+        }
+        else if ( isset($this->_part['BYMONTHDAY']) ) {
+          $days = $next->GetMonthByMonthDay($this->_part['BYMONTHDAY']);
+        }
+        else
+          $days[$next->_dd] = $next->_dd;
+
+        if ( isset($this->_part['BYSETPOS']) ) {
+          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+        }
+
+        $days = $this->WithinScope( $next, $days);
+      }
+      while( $limit && count($days) < 1 && ! $this->_finished );
+//      dbg_error_log( "RRule", " GetNext: Found %d days for MONTHLY rule", count($days) );
+
+    }
+    else if ( $this->_part['FREQ'] == "WEEKLY" ) {
+//      dbg_error_log( "RRule", " GetNext: Calculating more dates for WEEKLY rule" );
+      $limit = 200;
+      do {
+        $limit--;
+        if ( $this->_started ) {
+          $next->AddDays($this->_part['INTERVAL'] * 7);
+        }
+        else {
+          $this->_started = true;
+        }
+
+        if ( isset($this->_part['BYDAY']) ) {
+          $days = $next->GetWeekByDay($this->_part['BYDAY'], false );
+        }
+        else
+          $days[$next->_dd] = $next->_dd;
+
+        if ( isset($this->_part['BYSETPOS']) ) {
+          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+        }
+
+        $days = $this->WithinScope( $next, $days);
+      }
+      while( $limit && count($days) < 1 && ! $this->_finished );
+
+//      dbg_error_log( "RRule", " GetNext: Found %d days for WEEKLY rule", count($days) );
+    }
+    else if ( $this->_part['FREQ'] == "DAILY" ) {
+//      dbg_error_log( "RRule", " GetNext: Calculating more dates for DAILY rule" );
+      $limit = 100;
+      do {
+        $limit--;
+        if ( $this->_started ) {
+          $next->AddDays($this->_part['INTERVAL']);
+        }
+
+        if ( isset($this->_part['BYDAY']) ) {
+          $days = $next->GetWeekByDay($this->_part['BYDAY'], $this->_started );
+        }
+        else
+          $days[$next->_dd] = $next->_dd;
+
+        if ( isset($this->_part['BYSETPOS']) ) {
+          $days = $next->ApplyBySetpos($this->_part['BYSETPOS'], $days);
+        }
+
+        $days = $this->WithinScope( $next, $days);
+        $this->_started = true;
+      }
+      while( $limit && count($days) < 1 && ! $this->_finished );
+
+//      dbg_error_log( "RRule", " GetNext: Found %d days for DAILY rule", count($days) );
+    }
+
+    $ptr = $this->_current;
+    foreach( $days AS $k => $v ) {
+      $this->_dates[$ptr++] = $v;
+    }
+
+    if ( isset($this->_dates[$this->_current]) ) {
+//      dbg_error_log( "RRule", " GetNext: Returning %s, (%d'th)", $this->_dates[$this->_current]->Render(), $this->_current );
+      return $this->_dates[$this->_current];
+    }
+    else {
+//      dbg_error_log( "RRule", " GetNext: Returning null date" );
+      $next = null;
+      return $next;
+    }
+  }
+
+}
+
Index: contrib/davical/inc/freebusy-functions.php
===================================================================
--- contrib/davical/inc/freebusy-functions.php	(revision 3733)
+++ contrib/davical/inc/freebusy-functions.php	(revision 3733)
@@ -0,0 +1,563 @@
+<?php
+
+/**
+ * Function to include which handles building a free/busy response to
+ * be used in either the REPORT, response to a POST, or response to a
+ * a freebusy GET request.
+ */
+
+include_once('iCalendar.php');
+include_once('RRule-v2.php');
+include_once("drivers_ldap.php");
+
+
+function get_freebusy( $path_match, $mail, $range_start, $range_end, $bin_privs = null ) {
+  global $request;
+
+//  printf( "Path: %s\n", $path_match);
+//  print_r($range_start);
+//  print_r($range_end);
+  $fbq_start=$range_start;
+  $fbq_end=$range_end; 
+  //if ( !isset($bin_privs) ) $bin_privs = $request->Privileges();
+  if ( !isset($range_start) || !isset($range_end) ) {
+    $request->DoResponse( 400, 'All valid freebusy requests MUST contain a time-range filter' );
+  }
+  $filtro = "mail=".$mail;
+  $atributos = array("uidNumber");
+  $uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+  if ($uidnumber == false) {
+     dbg_error_log( "POST", "Responding with free/busy error: email usuario nÃ£o encontrado no diretorio");
+     $request->DoResponse( 501, 'Database error');
+  }
+  $cpfuser = $uidnumber['uidNumber'];
+     
+ //8888888888888888888888 Comentado para funcionar com Expresso owner: gabriel Malheiros 10.12.2010 8888888888888888888888888888888888888 
+ // $params = array( ':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC() );
+ // $where = ' WHERE caldav_data.dav_name ~ :path_match ';
+ // $where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) ';
+ // $where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) ";
+ // $where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) ";
+ // $where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) ";
+ // $where .= "AND collection.is_calendar AND collection.schedule_transp = 'opaque' ";
+
+ // if ( $bin_privs != privilege_to_bits('all') ) {
+ //   $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
+ // }
+
+
+ // $fbtimes = array();
+ // $sql = 'SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, ';
+ // $sql .= "to_char(calendar_item.dtstart at time zone 'GMT',".iCalendar::SqlUTCFormat().') AS start, ';
+ // $sql .= "to_char(calendar_item.dtend at time zone 'GMT',".iCalendar::SqlUTCFormat().') AS finish ';
+ // $sql .= 'FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name,collection_id) ';
+ // $sql .= 'INNER JOIN collection USING(collection_id)';
+ // $sql .= $where;
+ // if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+ // $qry = new AwlQuery( $sql, $params );
+ // if ( $qry->Exec("REPORT",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+ //   while( $calendar_object = $qry->Fetch() ) {
+ //     $extra = '';
+ //     if ( $calendar_object->status == 'TENTATIVE' ) {
+ //       $extra = ';BUSY-TENTATIVE';
+ //     }
+ //     dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+ //     $ics = new vComponent($calendar_object->caldav_data);
+ //     $expanded = expand_event_instances($ics, $range_start, $range_end);
+ //     $expansion = $expanded->GetComponents( array('VEVENT'=>true,'VTODO'=>true,'VJOURNAL'=>true) );
+ //     foreach( $expansion AS $k => $v ) {
+//        echo "=====================================================\n";
+//        printf( "Type: %s\n", $v->GetType());
+//        print_r($v);
+//        echo "-----------------------------------------------------\n";
+ //       $start_date = $v->GetProperty('DTSTART');
+ //       if ( !isset($start_date) ) continue;
+ //       $start_date = new RepeatRuleDateTime($start_date->Value());
+ //       $duration = $v->GetProperty('DURATION');
+ //       $duration = ( !isset($duration) ? 'P1D' : $duration->Value());
+ //       $end_date = clone($start_date);
+ //      $end_date->modify( $duration );
+ //       if ( $end_date < $range_start || $start_date > $range_end ) continue;
+ //       $thisfb = $start_date->UTC() .'/'. $end_date->UTC() . $extra;
+ //       array_push( $fbtimes, $thisfb );
+ //     }
+ //   }
+ // }
+
+ $qry = new AwlQuery( "SELECT p.cal_id,p.cal_type,p.datetime,p.edatetime,p.is_public,u.cal_status from phpgw_cal as p  inner join phpgw_cal_user as u on p.cal_id = u.cal_id  where p.owner= :id or u.cal_login = :id  and u.cal_status != 'R';", array(':id' => $cpfuser));
+    if ( $qry->Exec("POST",__LINE__,__FILE__) && $qry->rows() > 0 ) {                                                                                                                                                           
+      $fbtypee = array();
+      $fbtype = "";
+      while( $calendar_object = $qry->Fetch() ) {                                                                                                                                                                             
+        if ( $calendar_object->is_public == 1 ) {
+           if ( $calendar_object->cal_status == 'A' ) {
+              $extra = ';BUSY';
+            }
+           elseif ( $calendar_object->cal_status == 'U' ) {
+              $extra = ';BUSY-UNAVAILABLE';
+            }
+           elseif ( $calendar_object->cal_status == 'T' ) {
+              $extra = ';BUSY-TENTATIVE';
+            }
+           else
+            {
+             $extra = ';FREE';
+            }                                                                                                                                                                  
+           switch ( $calendar_object->cal_type ) {                                                                                                                                                                            
+                 case "E":                                                                                                                                                                                                    
+                     dbg_error_log( "POST", " FreeBusy: nao recorrente: %s, %s", $calendar_object->datetime, $calendar_object->edatetime );                                                                                   
+                     // $busy_tentative[] = $calendar_object;                                                                                                                                                                 
+                     $dateTime = date_create($fbq_start);                                                                                                                                                                     
+                     $DT_START = $dateTime->format("U");                                                                                                                                                                      
+                     $dateTime_end = date_create($fbq_end );                                                                                                                                                                  
+                     $DT_END = $dateTime_end->format("U");                                                                                                                                                                    
+                     if ( $calendar_object->datetime >= $DT_START && $calendar_object->edatetime <= $DT_END)                                                                                                                  
+                       { if(isset($fbtype)){                                                                                                                                                                                  
+                                                                                                                                                                                                                              
+                          $busydate_s = dataSegundosParaT($calendar_object->datetime,1);                                                                                                                                      
+                          $busydate_e = dataSegundosParaT($calendar_object->edatetime,1);                                                                                                                                     
+                          $fbtype = "$busydate_s/$busydate_e,".$extra;                                                                                                                                                              
+                          array_push( $fbtypee, $fbtype); 
+                          }                                                                                                                                                                                                   
+                         else                                                                                                                                                                                                 
+                          {                                                                                                                                                                                                   
+                          $busydate_s = dataSegundosParaT($calendar_object->datetime,1);                                                                                                                                      
+                          $busydate_e = dataSegundosParaT($calendar_object->edatetime,1);                                                                                                                                     
+                          $fbtype = "$busydate_s/$busydate_e,".$extra;                                                                                                                                                                                  
+                          array_push( $fbtypee, $fbtype); 
+                          }                                                                                                                                                                                                  
+                       }                                                                                                                                                                                                      
+                     break;                                                                                                                                                                                                   
+
+                 case "M":
+                    // Cancelled events are ignored
+                     dbg_error_log( "POST", " FreeBusy: recorrente: %s, %s", $calendar_object->datetime, $calendar_object->edatetime );
+                     $dateTime = date_create($fbq_start);                                                                              
+                     $DT_START = $dateTime->format("U");                                                                               
+                     $dateTime_end = date_create($fbq_end );                                                                           
+                     $DT_END = $dateTime_end->format("U");                                                                             
+                     $hora_extra = $dateTime->format("G") * 3600;                                                                      
+                     $difer  =  $calendar_object->edatetime - $calendar_object->datetime;                                              
+                     $qryp = new PgQuery( "SELECT * from phpgw_cal_repeats where cal_id = ?  ", $calendar_object->cal_id);             
+                     if ( $qryp->Exec("POST",__LINE__,__FILE__) && $qryp->rows > 0 ) {                                                 
+                        $calendar_repeats = $qryp->Fetch();                                                                            
+                        if ( $calendar_repeats->recur_enddate >= $DT_START && $calendar_repeats->recur_enddate <= $DT_END )            
+                         { $next_cal = date_create($calendar_object->datetime);                                                        
+                                                                                                                                       
+                          $data_diferencial =$DT_START - $calendar_object->datetime;                                                   
+                          // while ($data_incrementa <= $calendar_repeats->recur_enddate)                                              
+                           $intervalo = ($calendar_repeats->recur_interval == 0) ? "1" : "$calendar_repeats->recur_interval";          
+                               switch ( $calendar_repeats->recur_type )                                                                
+                                {                                                                                                      
+                                case "1":                                                                                              
+                                          $modulo = $intervalo * 86400;                                                                
+                                          $soma = $data_diferencial % $modulo;                                                         
+                                          dbg_error_log( "POST", " FreeBusy: diario: %s", $data_diferencial);                          
+                                          $data_incrementa = $DT_START  - $soma;                                                       
+                                          while ($data_incrementa <= $calendar_repeats->recur_enddate)                                 
+                                          {  if ($data_incrementa >= $DT_START )                                                       
+                                                {                                                                                      
+                                                 $end = $data_incrementa + $difer;                                                     
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);                                  
+                                                 $busydate_e = dataSegundosParaT($end,1);                                              
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;                                                
+                                                 array_push( $fbtypee, $fbtype);
+                                                }                                                                                      
+                                           $data_incrementa =  $data_incrementa + 86400 * $intervalo;                                  
+                                          }                                                                                            
+                                          break;                                                                                       
+                                case "2":                                                                                              
+                                         $modulo = $intervalo * 86400;                                                                 
+                                         $soma = $data_diferencial % $modulo;                                                          
+                                         $data_incrementa = $DT_START - $soma;                                                         
+                                         dbg_error_log( "POST", " FreeBusy: semanal: %s", $soma );                                     
+                                         while ($data_incrementa <= $calendar_repeats->recur_enddate)                                  
+                                          {                                                                                            
+                                            $difer_semana = $DT_START - $data_incrementa;                                              
+                                            if ($data_incrementa >= $DT_START || $difer_semana <= 604800 )                             
+
+                                            { 
+                                                $semana = str_split(converte_semana($calendar_repeats->recur_data));   
+                                                $lef = count($semana);                                                 
+                                                for ( $i=0;$i<$lef;$i++ )                                              
+                                                 {                                                                     
+                                                   $obj_semana = dataSegundosParaT($data_incrementa,1);                
+                                                   $dia_semana = date_create($obj_semana);                             
+                                                   $week = $dia_semana->format("w") + 1;                               
+                                                   $diferenca = $semana[$i] - $week;                                   
+                                                   $data_semana = $data_incrementa + $diferenca * 86400;               
+                                                   $end = $data_semana + $difer;                                       
+                                                   $busydate_s = dataSegundosParaT($data_semana,1);                    
+                                                   $busydate_e = dataSegundosParaT($end,1);                            
+                                                   $fbtype = "$busydate_s/$busydate_e,".$extra;                              
+                                                   array_push( $fbtypee, $fbtype);
+                                                  }                                                                     
+                                             }                                                                         
+                                           $data_incrementa =  $data_incrementa + 604800 * $intervalo;                 
+                                          }                                                                            
+                                          break;                                                                       
+                                case "3":                                                                              
+                                          $data_incrementa = $calendar_object->datetime;                               
+                                          while ($data_incrementa <= $calendar_repeats->recur_enddate)                 
+                                          dbg_error_log( "POST", " FreeBusy: mensal: %s", $soma );                     
+                                          {  if ($data_incrementa >= $DT_START )                                       
+                                                {                                                                      
+                                                 $end = $data_incrementa + $difer;                                     
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);                  
+                                                 $busydate_e = dataSegundosParaT($end,1);                              
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;                                
+                                                 array_push( $fbtypee, $fbtype);
+                                                }                                                                      
+                                           $i =$intervalo;                                                             
+                                           while ( $i>0 )                                                              
+                                           {  $mes_data = date_create($data_incrementa);                               
+                                              $dias_mes = $mes_data->format("t");                                      
+                                              $data_incrementa =  $data_incrementa + 86400 * $dias_mes;                
+                                              $mes_data = date_create($data_incrementa);                               
+                                              $dias_mes = $mes_data->format("t");                                      
+                                              $i--;                                                                    
+                                           }                                                                           
+                                         }                                                                             
+                                          break;                                                                       
+                                                                                                                       
+                                case "4":                                                                              
+                                          $obj_mes = dataSegundosParaT($calendar_object->datetime,1);                  
+                                          $data_mes = date_create($obj_mes);                                           
+                                          $mes = $data_mes->format("m");                                               
+                                          $ano = $data_mes->format("Y");                                               
+                                          $algo = "${ano}${mes}01T000000Z";                                            
+                                          $dia_primeiro = date_create($algo);                                          
+                                          // $semana_mes = $data_mes->format("W") - $dia_primeiro->format("W");        
+                                          $difer_semana = $calendar_object->datetime - $dia_primeiro->format("U");     
+                                          $dividendo = $difer_semana / 604800;                                         
+                                          $semana_mes = str_split($dividendo);                                         
+                                          $dia_semana = $data_mes->format("D");                                        
+                                          switch ($semana_mes[0])                                                      
+                                          {case "0":                                                                   
+                                               $final = "+0 week $dia_semana";                                         
+                                               break;                                                                  
+                                           case "1":                                                                   
+                                               $final = "+1 week $dia_semana";                                         
+                                               break;                                                                  
+                                           case "2":                                                                   
+                                               $final = "+2 week $dia_semana";                                         
+                                               break;                                                                  
+                                           case "3":                                                                   
+                                               $final = "+3 week $dia_semana";                                         
+                                               break;                                                                  
+                                           case "5":                                                                   
+                                               $final = "-1 week $dia_semana";                                         
+                                               break;                                                                  
+                                          }                                                                            
+                                          $data_incrementa = $calendar_object->datetime;                               
+                                          while ($data_incrementa <=  $DT_END)                                         
+                                          {                                                                            
+                                            $obj_mes = dataSegundosParaT($data_incrementa,1);                          
+                                            $mes_data = date_create($obj_mes);                                         
+
+                                              if ($data_incrementa >= $DT_START )
+                                                {                                
+                                                 $end = $data_incrementa + $difer;
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);
+                                                 $busydate_e = dataSegundosParaT($end,1);            
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                 array_push( $fbtypee, $fbtype);              
+                                                }                                                    
+                                              $data_proc = dataSegundosParaT($data_incrementa,1);    
+                                              $data_mais = date("U",strtotime("$data_proc +${intervalo} month"));
+                                              $mes = date("M",$data_mais);                                       
+                                              $ano = date("Y",$data_mais);                                       
+                                              $hora = date("G",$data_mais);                                      
+                                              $minuto = date("i",$data_mais);                                    
+                                              $data_incrementa = date("U",strtotime("$final ${mes} ${ano}"));    
+                                              $data_incrementa = $data_incrementa + $hora * 60 * 60 + $minuto * 60;                                              
+                                                                                                                                                                 
+                                         }                                                                                                                       
+                                                                                                                                                                 
+                                        break;                                                                                                                   
+
+                          
+                                case "5": 
+                                        $obj_mes = dataSegundosParaT($calendar_object->datetime,1);
+                                        $date_mes = date_create($obj_mes);                         
+                                        $mes = $date_mes->format("m");                             
+                                        $ano = $date_mes->format("Y");                             
+                                        $dia = $date_mes->format("d");                             
+                                        $hora = $date_mes->format("G");                            
+                                        $minuto = $date_mes->format("i");                          
+                                        $data_incrementa = $calendar_object->datetime ;            
+                                        dbg_error_log( "POST", " FreeBusy: anual: %s", $soma );    
+                                        while ($data_incrementa <= $calendar_repeats->recur_enddate)
+                                          {  if ($data_incrementa >= $DT_START )                    
+                                            {                                                       
+                                                $data_incrementa = $data_incrementa + $hora * 60 * 60 + $minuto * 60;
+                                                $end = $data_incrementa + $difer;                                    
+                                                $busydate_s = dataSegundosParaT($data_incrementa,1);                 
+                                                $busydate_e = dataSegundosParaT($end,1);                             
+                                                $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                array_push( $fbtypee, $fbtype);                               
+                                            }                                                                        
+                                          $i =$intervalo;                                                            
+                                           while ($i>0)                                                              
+                                           {                                                                         
+                                             $data_incrementa =  date("U",strtotime("$ano-$mes-$dia +1 year"));      
+                                             $data_incrementa = $data_incrementa + $hora * 60 * 60 + $minuto * 60;   
+                                             $i++;                                                                   
+                                           }                                                                         
+                                                                                                                     
+                                          }                                                                          
+                                        break;                                                                       
+
+                               }     
+                                           
+                         }                 
+                        else               
+                          { if ( $calendar_repeats->recur_enddate >= $DT_START || $calendar_repeats->recur_enddate == 0 )
+                              {$next_cal = date_create($calendar_object->datetime);                                                        
+                                                                                                                                           
+                                $data_diferencial =$DT_START - $calendar_object->datetime;                                                   
+                               // while ($data_incrementa <= $calendar_repeats->recur_enddate)                                               
+                               $intervalo = ($calendar_repeats->recur_interval == 0) ? "1" : "$calendar_repeats->recur_interval";            
+                               switch ( $calendar_repeats->recur_type )                                                                      
+                                  {                                                                                                          
+                                case "1":                                                                                                    
+                                          $modulo = $intervalo * 86400;                                                                      
+                                          $soma = $data_diferencial % $modulo;                                                               
+                                          //$hora_extra = $dateTime->format("G") * 3600;                                                     
+                                          dbg_error_log( "POST", " FreeBusy: diario: %s", $soma );                                                          
+                                          $data_incrementa = $DT_START - $soma;                                                                             
+                                          while ($data_incrementa <= $DT_END)                                                                               
+                                          {  if ($data_incrementa >= $DT_START )                                                                            
+                                                {                                                                                                           
+                                                 $end = $data_incrementa + $difer;                                                                          
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);                                                       
+                                                 $busydate_e = dataSegundosParaT($end,1);                                                                   
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                 array_push( $fbtypee, $fbtype);                                                                     
+                                                }                                                                                                           
+                                           $data_incrementa =  $data_incrementa + 86400 * $intervalo;                                                       
+                                          }                                                                                                                 
+                                          break;                                                                                                            
+                                case "2":                                                                                                                   
+                                         $modulo = $intervalo * 86400;                                                                                       
+                                         $soma = $data_diferencial % $modulo;                                                                                
+                                         $data_incrementa = $DT_START - $soma;                                                                               
+                                         while ($data_incrementa <=  $DT_END)                                                                                
+                                          {                                                                                                                  
+                                            $difer_semana = $DT_START - $data_incrementa;                                                                    
+                                            if ($data_incrementa >= $DT_START || $difer_semana <= 604800 )                                                        
+                                             {                                                                                                                    
+                                                $semana = str_split(converte_semana($calendar_repeats->recur_data));                                              
+                                                $lef = count($semana);                                                                                            
+                                                for ( $i=0;$i<$lef;$i++ )                                                                                         
+                                                 {                                                                                                                
+                                                   $obj_semana = dataSegundosParaT($data_incrementa,1);                                                           
+                                                   $dia_semana = date_create($obj_semana);                                                                        
+                                                   $week = $dia_semana->format("w") + 1;                                                                          
+                                                   $diferenca = $semana[$i] - $week;                                                                              
+                                                   $data_semana = $data_incrementa + $diferenca * 86400;                                                          
+                                                   $end = $data_semana + $difer;                                                                                  
+                                                   $busydate_s = dataSegundosParaT($data_semana,1);                                                               
+                                                   $busydate_e = dataSegundosParaT($end,1);                                                                       
+                                                   $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                   array_push( $fbtypee, $fbtype);                                                                         
+                                                 }                                                                                                                
+                                             }                                                                                                                    
+                                           $data_incrementa =  $data_incrementa + 604800 * $intervalo;                                                            
+                                          }                                                                                                                       
+                                          break;                                                                                                                  
+                                case "3":                                                                                                                         
+                                          $data_incrementa = $calendar_object->datetime;                                                                          
+                                          dbg_error_log( "POST", " FreeBusy: mensal: %s", $soma );                                                                
+                                          while ($data_incrementa <=  $DT_END)                                                                                    
+                                          {  if ($data_incrementa >= $DT_START )                                                                                  
+                                                {                                                                                                                 
+                                                 $end = $data_incrementa + $difer;                                                                                
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);                                                             
+                                                 $busydate_e = dataSegundosParaT($end,1);                                                                         
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                 array_push( $fbtypee, $fbtype);                                                                           
+                                                }                                                                                                                 
+                                           $i =$intervalo;                                                                                                        
+                                           while ( $i>0 )                                                                                                         
+                                           {                                                                                                                      
+                                              $obj_mes = dataSegundosParaT($data_incrementa,1);                                                                   
+                                              $mes_data = date_create($obj_mes);                                                                                  
+                                              $dias_mes = $mes_data->format("t");                                                                                 
+                                              $data_incrementa =  $data_incrementa + 86400 * $dias_mes;                                                           
+                                              $obj_mes = dataSegundosParaT($data_incrementa,1);                                                                   
+                                              $mes_data = date_create($obj_mes);                                                                                  
+                                              $dias_mes = $mes_data->format("t");                                                                                 
+                                              $i--;                                                                                                               
+                                           }                                                                                                                      
+                                         }                                                                                                                        
+                                          break;                                                                                                                  
+                                                                                                                                                                  
+                                case "4":                                                                                                                         
+                                          $obj_mes = dataSegundosParaT($calendar_object->datetime,1);                                                             
+                                          $data_mes = date_create($obj_mes);                                                                                      
+                                          $mes = $data_mes->format("m");                                                                                          
+                                          $ano = $data_mes->format("Y");                                                                                          
+                                          $algo = "${ano}${mes}01T000000Z";                                                                                       
+                                          $dia_primeiro = date_create($algo);                                                                                     
+                                          //$semana_mes = $data_mes->format("W") - $dia_primeiro->format("W");                                                    
+                                          $difer_semana = $calendar_object->datetime - $dia_primeiro->format("U");                                                
+                                          $dividendo = $difer_semana / 604800;                                                                                    
+                                          $semana_mes = str_split($dividendo);                                                                                    
+                                          $dia_semana = $data_mes->format("D");                                                                                   
+                                          switch ($semana_mes[0])                                                                                                 
+                                          {case "0":                                                                                                              
+                                               $final = "+0 week $dia_semana";                                                                                    
+                                               break;                                                                                                             
+                                           case "1":                                                                                                              
+                                               $final = "+1 week $dia_semana";                                                                                    
+                                               break;                                                                                                             
+                                           case "2":                                                                                                              
+                                               $final = "+2 week $dia_semana";                                                                                    
+                                               break;                                                                                                             
+                                           case "3":                                                                                                              
+                                               $final = "+3 week $dia_semana";                                                                                    
+                                               break;                                                                                                             
+                                           case "5":                                                                                                              
+                                               $final = "-1 week $dia_semana";                                                                                    
+                                               break;                                                                                                             
+                                          }                                                                                                                       
+                                                                                                                                                                  
+                                          $data_incrementa = $calendar_object->datetime;                                                                          
+                                          while ($data_incrementa <=  $DT_END)                                                                                    
+                                          {                                                                                                                       
+                                            $obj_mes = dataSegundosParaT($data_incrementa,1);                                                                     
+                                            $mes_data = date_create($obj_mes);                                                                                    
+
+                                              if ($data_incrementa >= $DT_START )                                                       
+                                                {                                                                                       
+                                                 $end = $data_incrementa + $difer;                                                      
+                                                 $busydate_s = dataSegundosParaT($data_incrementa,1);                                    
+                                                 $busydate_e = dataSegundosParaT($end,1);                                                
+                                                 $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                 array_push( $fbtypee, $fbtype);                                                  
+                                                }                                                                                        
+                                              $data_proc = dataSegundosParaT($data_incrementa,1);                                        
+                                              $data_mais = date("U",strtotime("$data_proc +${intervalo} month"));                        
+                                              $mes = date("M",$data_mais);                                                               
+                                              $ano = date("Y",$data_mais);                                                               
+                                              $hora = date("G",$data_mais);                                                              
+                                              $minuto = date("i",$data_mais);                                                            
+                                              $data_incrementa = date("U",strtotime("$final ${mes} ${ano}"));                            
+                                              $data_incrementa = $data_incrementa + $hora * 60 * 60 + $minuto * 60;                      
+                                         }                                                                                               
+                                         break;                                                                                          
+
+                          
+                                case "5": 
+                                        $obj_mes = dataSegundosParaT($calendar_object->datetime,1);
+                                        $date_mes = date_create($obj_mes);                         
+                                        $mes = $date_mes->format("m");                             
+                                        $ano = $date_mes->format("Y");                             
+                                        $dia = $date_mes->format("d");                             
+                                        dbg_error_log( "POST", " FreeBusy: Anual: %s", $soma );                      
+                                        $hora = $date_mes->format("G");                                              
+                                        $minuto = $date_mes->format("i");                                            
+                                        $data_incrementa = $calendar_object->datetime ;                              
+                                        while ($data_incrementa <=  $DT_END)                                         
+                                          {  if ($data_incrementa >= $DT_START )                                     
+                                            {                                                                        
+                                                $end = $data_incrementa + $difer;                                    
+                                                $busydate_s = dataSegundosParaT($data_incrementa,1);                   
+                                                $busydate_e = dataSegundosParaT($end,1);                               
+                                                $fbtype = "$busydate_s/$busydate_e,".$extra;
+                                                array_push( $fbtypee, $fbtype);                                 
+                                            }                                                                          
+                                          $i = $intervalo;                                                             
+                                           while ( $i>0 )                                                              
+                                           {                                                                           
+                                              $data_incrementa =  date("U",strtotime("$ano-$mes-$dia +1 year"));       
+                                              $data_incrementa = $data_incrementa + $hora * 60 * 60 + $minuto * 60;    
+                                              $i--;                                                                    
+                                           }                                                                           
+                                                                                                                       
+                                          }                                                                            
+                                        break;                                                                         
+
+                                  }     
+
+                                  
+                                  
+                              }                      
+                          }                          
+                     }                               
+                     break;                          
+                                                     
+                 //default:                          
+                 //  dbg_error_log( "POST", " FreeBusy: Not transparent, tentative or cancelled: %s, %s", $calendar_object->start, $calendar_object->finish );
+                 //  $busy[] = $calendar_object;                                                                                                              
+                 //  break;                                                                                                                                   
+          }                                                                                                                                                   
+        }                                                                                                                                                     
+      }                                                                                                                                                       
+    }                                                                                                          
+
+
+
+
+  $freebusy = new iCalComponent();
+  $freebusy->SetType('VFREEBUSY');
+  $freebusy->AddProperty('DTSTAMP', date('Ymd\THis\Z'));
+  $freebusy->AddProperty('DTSTART', $range_start->UTC());
+  $freebusy->AddProperty('DTEND', $range_end->UTC());
+
+  sort( $fbtypee );
+  foreach( $fbtypee AS $k => $v ) {
+    $text = explode(';',$v,2);
+    $freebusy->AddProperty( 'FREEBUSY', $text[0], (isset($text[1]) ? array('FBTYPE' => $text[1]) : null) );
+  }
+
+  return $freebusy;
+}
+function dataSegundosParaT($dataseg, $opcao = 0){
+        if ($opcao!=1){                          
+                $teste = getdate($dataseg);      
+                return date("Ymd\THis",$teste[0]);
+        }                                         
+        $HorarioTimeZone = new DateTimeZone(date_default_timezone_get());
+        $HorarioTime = new DateTime("now", $HorarioTimeZone);            
+        $teste = getdate($dataseg - $HorarioTimeZone->getoffset($HorarioTime));
+        return date("Ymd\THis\Z",$teste[0]);                                   
+}                   
+function converte_semana($sema){
+        $retorna = "";
+        $param = 1;
+        while( $param <= 64 ){
+                $numero = $sema & $param;
+                if ( $numero != 0 ){
+                        switch ($numero){
+                                case 1:
+                                        $retorna .= "1";
+                                        break;
+                                case 2:
+                                        $retorna .= "2";
+                                        break;
+                                case 4:
+                                        $retorna .= "3";
+                                        break;
+                                case 8:
+                                        $retorna .= "4";
+                                        break;
+                                case 16:
+                                        $retorna .= "5";
+                                        break;
+                                case 32:
+                                        $retorna .= "6";
+                                        break;
+                                case 64:
+                                        $retorna .= "7";
+                                        break;
+                                default:
+                                        continue;
+                        }
+                }
+                $param = $param * 2;
+        }
+        //$nova = substr($retorna, 0, -1);
+        return $retorna;
+}
Index: contrib/davical/inc/caldav-DELETE.php
===================================================================
--- contrib/davical/inc/caldav-DELETE.php	(revision 3733)
+++ contrib/davical/inc/caldav-DELETE.php	(revision 3733)
@@ -0,0 +1,160 @@
+<?php
+/**
+* CalDAV Server - handle DELETE method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("delete", "DELETE method handler");
+include_once("iCalendar.php");
+require_once('DAVResource.php');
+include_once("drivers_ldap.php");
+$dav_resource = new DAVResource($request->path);
+$container = $dav_resource->FetchParentContainer();
+$container->NeedPrivilege('DAV::unbind');
+
+$nome = $container->GetProperty('user_no');
+$lock_opener = $request->FailIfLocked();
+$etag = md5($request->raw_post);
+$matches = preg_split('/\//', $request->path, -1, PREG_SPLIT_NO_EMPTY);
+$CALID = strtok($matches[2], "@");
+
+//$filtro = "uid=".$request->username;
+//$atributos = array("uidNumber");
+//$uidnumber = ldapDrivers::requestAtributo($filtro, $atributos);
+//    if ($uidnumber == false) {
+//                 dbg_error_log( "DELETE", "Responding with delete error:  usuario nÃ£o encontrado no diretorio");
+//                 $request->DoResponse( 501, 'Database error');
+//    }
+//$nome = $uidnumber['uidNumber'];
+
+function delete_collection( $nome,$CALID ) {
+  
+  $qry = new AwlQuery( "SELECT * FROM phpgw_cal WHERE owner = :owner AND cal_id = :cal_id", array( ':owner' => $nome , ':cal_id' => $CALID));
+  if ( $qry->Exec("DELETE") && $qry->rows() == 1 ){                                                
+    $delete_row = $qry->Fetch(); 
+    if ($qry->QDo("DELETE FROM phpgw_cal WHERE cal_id = :cal_id AND owner = :nome",array( ':cal_id' => $CALID, ':nome' => $nome))){
+         if($qry->QDO("DELETE FROM phpgw_cal_user WHERE cal_login = :nome AND cal_id = :cal_id",array( ':nome' => $nome , ':cal_id' => $CALID))){
+           if ( $delete_row->cal_type == 'M' )
+                {
+                  if($qry->QDO("DELETE FROM phpgw_cal_repeats WHERE cal_id = :cal_id",array( ':cal_id' => $CALID)))
+                    {
+                      @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+                      return true;
+                    }
+                   else
+                    { return false;
+                    }  
+                }
+           @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+           return true;
+          }
+         else
+          {return false;
+           }   
+      } 
+    else {
+       return false;
+       //$request->DoResponse( 500, translate("Error querying database.") );
+    }
+
+  } 
+  else
+  {
+    return false;                                                                                                                                                  
+  }
+  //$params = array( ':collection_id' => $id );
+  //$qry = new AwlQuery('SELECT child.collection_id AS child_id FROM collection child JOIN collection parent ON (parent.dav_name = child.parent_container) WHERE parent.collection_id = :collection_id', $params );
+  //if ( $qry->Exec('DELETE',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+  //  while( $row = $qry->Fetch() ) {
+  //    delete_collection($row->child_id);
+  //  }
+  //}
+
+  //if ( $qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params )
+  //  && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+  //  && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params )
+  //  && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params )
+  //  && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params ) ) {
+  //  @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+  //  return true;
+  //}
+  //return false;
+}
+
+function delete_collection_card( $nome,$CALID,$qry ) 
+{
+  $qryy = new AwlQuery( "SELECT A.id_contact,B.id_connection,C.id_address FROM phpgw_cc_contact as A left join  phpgw_cc_contact_conns as B using(id_contact) left join phpgw_cc_contact_addrs as C using(id_contact)  WHERE A.id_owner = :owner AND A.id_contact = :cal_id", array( ':owner' => $nome , ':cal_id' => $CALID));
+  if ( $qryy->Exec("DELETE") && $qryy->rows() > 0 )
+   {
+   if ($qry->QDo("DELETE FROM phpgw_cc_contact WHERE id_contact = :cal_id AND id_owner = :nome",array( ':cal_id' => $CALID, ':nome' => $nome)))
+    {
+      while($delete_row = $qryy->Fetch())
+      {
+        if ( isset($delete_row->id_connection))
+            {
+              $qry->QDO("DELETE FROM phpgw_cc_contact_conns WHERE id_contact= :id AND id_connection = :id_connection",array( ':id' => $CALID,':id_connection' => $delete_row->id_connection ));
+              $qry->QDO("DELETE FROM phpgw_cc_connections WHERE id_connection = :id_connection",array( ':id_connection' => $delete_row->id_connection ));
+             }  
+          if ( isset($delete_row->id_address))
+            { 
+              $qry->QDO("DELETE from phpgw_cc_contact_addrs where id_contact= :id AND id_address = :id_connection", array(':id' => $CALID ,':id_connection' => $delete_row->id_address));
+              $qry->QDO("DELETE from phpgw_cc_addresses  where id_address = :id_connection", array(':id_connection' => $delete_row->id_address));
+            }
+       }
+      @dbg_error_log( "DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+      return true;
+    }
+   else
+    { return false;
+    } 
+  } 
+  else
+  {
+    return false;
+  }
+}
+
+if ( !$container->Exists() )$request->DoResponse( 404 );
+
+if ( ! ( $container->resource_id() > 0 ) ) {
+  $request->DoResponse( 403 );
+}
+
+$qry = new AwlQuery();
+$qry->Begin();
+if ( $container->IsBinding() ) {
+  $params = array( ':dav_name' => $container->dav_name() );
+  if ( $qry->QDo("DELETE FROM dav_binding WHERE dav_name = :dav_name", $params ) && $qry->Commit() )
+  {
+    @dbg_error_log( "DELETE", "DELETE: Binding: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
+    $request->DoResponse( 204 );
+   }
+}
+else if ( $container->IsCollection() ) {
+  if ( $dav_resource->IsAddressbook())
+    {
+      if ( delete_collection_card( $nome,$CALID,$qry ) && $qry->Commit() ) {
+       $request->DoResponse( 204 );
+       }
+
+     }
+    else //if ( $dav_resource->IsCalendar())
+    {
+      if ( delete_collection( $nome,$CALID ) && $qry->Commit() ) {
+       $request->DoResponse( 204 );
+     }
+    }
+
+}
+else {
+  if ( (isset($request->etag_if_match) && $request->etag_if_match != $dav_resource->unique_tag() ) ) {
+    $request->DoResponse( 412, translate("Resource has changed on server - not deleted") );
+  }
+
+}
+$request->DoResponse( 500 );
+
Index: contrib/davical/inc/caldav-client-v2.php
===================================================================
--- contrib/davical/inc/caldav-client-v2.php	(revision 3733)
+++ contrib/davical/inc/caldav-client-v2.php	(revision 3733)
@@ -0,0 +1,1035 @@
+<?php
+/**
+* A Class for connecting to a caldav server
+*
+* @package   awl
+*
+* @subpackage   caldav
+* @author Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Andrew McMillan
+* @license   http://www.gnu.org/licenses/lgpl-3.0.txt  GNU LGPL version 3 or later
+*/
+
+require_once('XMLDocument.php');
+
+/**
+* A class for holding basic calendar information
+* @package awl
+*/
+class CalendarInfo {
+  public $url, $displayname, $getctag;
+
+  function __construct( $url, $displayname = null, $getctag = null ) {
+    $this->url = $url;
+    $this->displayname = $displayname;
+    $this->getctag = $getctag;
+  }
+
+  function __toString() {
+    return( '(URL: '.$this->url.'   Ctag: '.$this->getctag.'   Displayname: '.$this->displayname .')'. "\n" );
+  }
+}
+
+
+/**
+* A class for accessing DAViCal via CalDAV, as a client
+*
+* @package   awl
+*/
+class CalDAVClient {
+  /**
+  * Server, username, password, calendar
+  *
+  * @var string
+  */
+  protected $base_url, $user, $pass, $entry, $protocol, $server, $port;
+
+  /**
+  * The principal-URL we're using
+  */
+  protected $principal_url;
+
+  /**
+  * The calendar-URL we're using
+  */
+  protected $calendar_url;
+
+  /**
+  * The calendar-home-set we're using
+  */
+  protected $calendar_home_set;
+
+  /**
+  * The calendar_urls we have discovered
+  */
+  protected $calendar_urls;
+
+  /**
+  * The useragent which is send to the caldav server
+  *
+  * @var string
+  */
+  public $user_agent = 'DAViCalClient';
+
+  protected $headers = array();
+  protected $body = "";
+  protected $requestMethod = "GET";
+  protected $httpRequest = "";  // for debugging http headers sent
+  protected $xmlRequest = "";   // for debugging xml sent
+  protected $httpResponse = ""; // http headers received
+  protected $xmlResponse = "";  // xml received
+
+  protected $parser; // our XML parser object
+
+  /**
+  * Constructor, initialises the class
+  *
+  * @param string $base_url  The URL for the calendar server
+  * @param string $user      The name of the user logging in
+  * @param string $pass      The password for that user
+  */
+  function __construct( $base_url, $user, $pass ) {
+    $this->user = $user;
+    $this->pass = $pass;
+    $this->headers = array();
+
+    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+      $this->server = $matches[2];
+      $this->base_url = $matches[5];
+      if ( $matches[1] == 'https' ) {
+        $this->protocol = 'ssl';
+        $this->port = 443;
+      }
+      else {
+        $this->protocol = 'tcp';
+        $this->port = 80;
+      }
+      if ( $matches[4] != '' ) {
+        $this->port = intval($matches[4]);
+      }
+    }
+    else {
+      trigger_error("Invalid URL: '".$base_url."'", E_USER_ERROR);
+    }
+  }
+
+  /**
+  * Adds an If-Match or If-None-Match header
+  *
+  * @param bool $match to Match or Not to Match, that is the question!
+  * @param string $etag The etag to match / not match against.
+  */
+  function SetMatch( $match, $etag = '*' ) {
+    $this->headers['match'] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+  }
+
+  /**
+  * Add a Depth: header.  Valid values are 0, 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetDepth( $depth = '0' ) {
+    $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+  }
+
+  /**
+  * Add a Depth: header.  Valid values are 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetUserAgent( $user_agent = null ) {
+    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+    $this->user_agent = $user_agent;
+  }
+
+  /**
+  * Add a Content-type: header.
+  *
+  * @param string $type  The content type
+  */
+  function SetContentType( $type ) {
+    $this->headers['content-type'] = "Content-type: $type";
+  }
+
+  /**
+  * Set the calendar_url we will be using for a while.
+  *
+  * @param string $url The calendar_url
+  */
+  function SetCalendar( $url ) {
+    $this->calendar_url = $url;
+  }
+
+  /**
+  * Split response into httpResponse and xmlResponse
+  *
+  * @param string Response from server
+   */
+  function ParseResponse( $response ) {
+    $pos = strpos($response, '<?xml');
+    if ($pos === false) {
+      $this->httpResponse = trim($response);
+    }
+    else {
+      $this->httpResponse = trim(substr($response, 0, $pos));
+      $this->xmlResponse = trim(substr($response, $pos));
+      $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse );
+      $parser = xml_parser_create_ns('UTF-8');
+      xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 );
+      xml_parser_set_option ( $parser, XML_OPTION_CASE_FOLDING, 0 );
+
+      if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) {
+        printf( "XML parsing error: %s - %s\n", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)) );
+//        debug_print_backtrace();
+//        echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes );
+//        echo "\nTags array............................................................\n";  print_r( $this->xmltags );
+        printf( "\nXML Reponse:\n%s\n", $this->xmlResponse );
+      }
+
+      xml_parser_free($parser);
+    }
+  }
+
+  /**
+   * Output http request headers
+   *
+   * @return HTTP headers
+   */
+  function GetHttpRequest() {
+      return $this->httpRequest;
+  }
+  /**
+   * Output http response headers
+   *
+   * @return HTTP headers
+   */
+  function GetResponseHeaders() {
+      return $this->httpResponseHeaders;
+  }
+  /**
+   * Output http response body
+   *
+   * @return HTTP body
+   */
+  function GetResponseBody() {
+      return $this->httpResponseBody;
+  }
+  /**
+   * Output xml request
+   *
+   * @return raw xml
+   */
+  function GetXmlRequest() {
+      return $this->xmlRequest;
+  }
+  /**
+   * Output xml response
+   *
+   * @return raw xml
+   */
+  function GetXmlResponse() {
+      return $this->xmlResponse;
+  }
+
+  /**
+  * Send a request to the server
+  *
+  * @param string $url The URL to make the request to
+  *
+  * @return string The content of the response from the server
+  */
+  function DoRequest( $url = null ) {
+    if(!defined("_FSOCK_TIMEOUT")){ define("_FSOCK_TIMEOUT", 10); }
+    $headers = array();
+
+    if ( !isset($url) ) $url = $this->base_url;
+    $this->request_url = $url;
+    $url = preg_replace('{^https?://[^/]+}', '', $url);
+    // URLencode if it isn't already
+    if ( preg_match( '{[^%?&=+,.-_/a-z0-9]}', $url ) ) {
+      $url = str_replace(rawurlencode('/'),'/',rawurlencode($url));
+      $url = str_replace(rawurlencode('?'),'?',$url);
+      $url = str_replace(rawurlencode('&'),'&',$url);
+      $url = str_replace(rawurlencode('='),'=',$url);
+      $url = str_replace(rawurlencode('+'),'+',$url);
+      $url = str_replace(rawurlencode(','),',',$url);
+    }
+    $headers[] = $this->requestMethod." ". $url . " HTTP/1.1";
+    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+    $headers[] = "Host: ".$this->server .":".$this->port;
+
+    if ( !isset($this->headers['content-type']) ) $this->headers['content-type'] = "Content-type: text/plain";
+    foreach( $this->headers as $ii => $head ) {
+      $headers[] = $head;
+    }
+    $headers[] = "Content-Length: " . strlen($this->body);
+    $headers[] = "User-Agent: " . $this->user_agent;
+    $headers[] = 'Connection: close';
+    $this->httpRequest = join("\r\n",$headers);
+    $this->xmlRequest = $this->body;
+
+    $this->httpResponse = '';
+    $this->xmlResponse = '';
+
+    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+    if ( !(get_resource_type($fip) == 'stream') ) return false;
+    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+    $response = "";
+    while( !feof($fip) ) { $response .= fgets($fip,8192); }
+    fclose($fip);
+
+    list( $this->httpResponseHeaders, $this->httpResponseBody ) = preg_split( '{\r?\n\r?\n}s', $response, 2 );
+    if ( preg_match( '{Transfer-Encoding: chunked}i', $this->httpResponseHeaders ) ) $this->Unchunk();
+
+    $this->headers = array();  // reset the headers array for our next request
+    $this->ParseResponse($this->httpResponseBody);
+    return $response;
+  }
+
+
+  /**
+  * Unchunk a chunked response
+  */
+  function Unchunk() {
+    $content = '';
+    $chunks = $this->httpResponseBody;
+    // printf( "\n================================\n%s\n================================\n", $chunks );
+    do {
+      $bytes = 0;
+      if ( preg_match('{^((\r\n)?\s*([ 0-9a-fA-F]+)(;[^\n]*)?\r?\n)}', $chunks, $matches ) ) {
+        $octets = $matches[3];
+        $bytes = hexdec($octets);
+        $pos = strlen($matches[1]);
+        // printf( "Chunk size 0x%s (%d)\n", $octets, $bytes );
+        if ( $bytes > 0 ) {
+          // printf( "---------------------------------\n%s\n---------------------------------\n", substr($chunks,$pos,$bytes) );
+          $content .= substr($chunks,$pos,$bytes);
+          $chunks = substr($chunks,$pos + $bytes + 2);
+          // printf( "+++++++++++++++++++++++++++++++++\n%s\n+++++++++++++++++++++++++++++++++\n", $chunks );
+        }
+      }
+      else {
+        $content .= $chunks;
+      }
+    }
+    while( $bytes > 0 );
+    $this->httpResponseBody = $content;
+    // printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n%s\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", $content );
+  }
+
+
+  /**
+  * Send an OPTIONS request to the server
+  *
+  * @param string $url The URL to make the request to
+  *
+  * @return array The allowed options
+  */
+  function DoOptionsRequest( $url = null ) {
+    $this->requestMethod = "OPTIONS";
+    $this->body = "";
+    $headers = $this->DoRequest($url);
+    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+    $options = array_flip( preg_split( '/[, ]+/', $options_header ));
+    return $options;
+  }
+
+
+
+  /**
+  * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR)
+  *
+  * @param string $method The method (PROPFIND, REPORT, etc) to use with the request
+  * @param string $xml The XML to send along with the request
+  * @param string $url The URL to make the request to
+  *
+  * @return array An array of the allowed methods
+  */
+  function DoXMLRequest( $request_method, $xml, $url = null ) {
+    $this->body = $xml;
+    $this->requestMethod = $request_method;
+    $this->SetContentType("text/xml");
+    return $this->DoRequest($url);
+  }
+
+
+
+  /**
+  * Get a single item from the server.
+  *
+  * @param string $url The URL to GET
+  */
+  function DoGETRequest( $url ) {
+    $this->body = "";
+    $this->requestMethod = "GET";
+    return $this->DoRequest( $url );
+  }
+
+
+  /**
+  * Get the HEAD of a single item from the server.
+  *
+  * @param string $url The URL to HEAD
+  */
+  function DoHEADRequest( $url ) {
+    $this->body = "";
+    $this->requestMethod = "HEAD";
+    return $this->DoRequest( $url );
+  }
+
+
+  /**
+  * PUT a text/icalendar resource, returning the etag
+  *
+  * @param string $url The URL to make the request to
+  * @param string $icalendar The iCalendar resource to send to the server
+  * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource.
+  *
+  * @return string The content of the response from the server
+  */
+  function DoPUTRequest( $url, $icalendar, $etag = null ) {
+    $this->body = $icalendar;
+
+    $this->requestMethod = "PUT";
+    if ( $etag != null ) {
+      $this->SetMatch( ($etag != '*'), $etag );
+    }
+    $this->SetContentType('text/calendar; encoding="utf-8"');
+    $this->DoRequest($url);
+
+    $etag = null;
+    if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+    if ( !isset($etag) || $etag == '' ) {
+      printf( "No etag in:\n%s\n", $this->httpResponseHeaders );
+      $save_request = $this->httpRequest;
+      $save_response_headers = $this->httpResponseHeaders;
+      $this->DoHEADRequest( $url );
+      if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) $etag = $matches[1];
+      if ( !isset($etag) || $etag == '' ) {
+        printf( "Still No etag in:\n%s\n", $this->httpResponseHeaders );
+      }
+      $this->httpRequest = $save_request;
+      $this->httpResponseHeaders = $save_response_headers;
+    }
+    return $etag;
+  }
+
+
+  /**
+  * DELETE a text/icalendar resource
+  *
+  * @param string $url The URL to make the request to
+  * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL.
+  *
+  * @return int The HTTP Result Code for the DELETE
+  */
+  function DoDELETERequest( $url, $etag = null ) {
+    $this->body = "";
+
+    $this->requestMethod = "DELETE";
+    if ( $etag != null ) {
+      $this->SetMatch( true, $etag );
+    }
+    $this->DoRequest($url);
+    return $this->resultcode;
+  }
+
+
+  /**
+  * Get a single item from the server.
+  *
+  * @param string $url The URL to PROPFIND on
+  */
+  function DoPROPFINDRequest( $url, $props, $depth = 0 ) {
+    $this->SetDepth($depth);
+    $xml = new XMLDocument( array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ) );
+    $prop = new XMLElement('prop');
+    foreach( $props AS $v ) {
+      $xml->NSElement($prop,$v);
+    }
+
+    $this->body = $xml->Render('propfind',$prop );
+
+    $this->requestMethod = "PROPFIND";
+    $this->SetContentType("text/xml");
+    $this->DoRequest($url);
+    return $this->GetXmlResponse();
+  }
+
+
+  /**
+  * Get/Set the Principal URL
+  *
+  * @param $url string The Principal URL to set
+  */
+  function PrincipalURL( $url = null ) {
+    if ( isset($url) ) {
+      $this->principal_url = $url;
+    }
+    return $this->principal_url;
+  }
+
+
+  /**
+  * Get/Set the calendar-home-set URL
+  *
+  * @param $url array of string The calendar-home-set URLs to set
+  */
+  function CalendarHomeSet( $urls = null ) {
+    if ( isset($urls) ) {
+      if ( ! is_array($urls) ) $urls = array($urls);
+      $this->calendar_home_set = $urls;
+    }
+    return $this->calendar_home_set;
+  }
+
+
+  /**
+  * Get/Set the calendar-home-set URL
+  *
+  * @param $urls array of string The calendar URLs to set
+  */
+  function CalendarUrls( $urls = null ) {
+    if ( isset($urls) ) {
+      if ( ! is_array($urls) ) $urls = array($urls);
+      $this->calendar_urls = $urls;
+    }
+    return $this->calendar_urls;
+  }
+
+
+  /**
+  * Return the first occurrence of an href inside the named tag.
+  *
+  * @param string $tagname The tag name to find the href inside of
+  */
+  function HrefValueInside( $tagname ) {
+    foreach( $this->xmltags[$tagname] AS $k => $v ) {
+      $j = $v + 1;
+      if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) {
+        return rawurldecode($this->xmlnodes[$j]['value']);
+      }
+    }
+    return null;
+  }
+
+
+  /**
+  * Return the href containing this property.  Except only if it's inside a status != 200
+  *
+  * @param string $tagname The tag name of the property to find the href for
+  * @param integer $which Which instance of the tag should we use
+  */
+  function HrefForProp( $tagname, $i = 0 ) {
+    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+      $j = $this->xmltags[$tagname][$i];
+      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) {
+//        printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+        if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) return null;
+      }
+//      printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']);
+      if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+//        printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']);
+        return rawurldecode($this->xmlnodes[$j]['value']);
+      }
+    }
+    else {
+      printf( "xmltags[$tagname] or xmltags[$tagname][$i] is not set\n");
+    }
+    return null;
+  }
+
+
+  /**
+  * Return the href which has a resourcetype of the specified type
+  *
+  * @param string $tagname The tag name of the resourcetype to find the href for
+  * @param integer $which Which instance of the tag should we use
+  */
+  function HrefForResourcetype( $tagname, $i = 0 ) {
+    if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) {
+      $j = $this->xmltags[$tagname][$i];
+      while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' );
+      if ( $j > 0 ) {
+        while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' );
+        if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) {
+          return rawurldecode($this->xmlnodes[$j]['value']);
+        }
+      }
+    }
+    return null;
+  }
+
+
+  /**
+  * Return the <prop> ... </prop> of a propstat where the status is OK
+  *
+  * @param string $nodenum The node number in the xmlnodes which is the href
+  */
+  function GetOKProps( $nodenum ) {
+    $props = null;
+    $level = $this->xmlnodes[$nodenum]['level'];
+    $status = '';
+    while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) {
+      if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) {
+        if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) {
+          $props = array();
+          $status = '';
+        }
+        else {
+          if ( $status == 'HTTP/1.1 200 OK' ) break;
+        }
+      }
+      elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) {
+        break;
+      }
+      elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) {
+        $status = $this->xmlnodes[$nodenum]['value'];
+      }
+      else {
+        $props[] = $this->xmlnodes[$nodenum];
+      }
+    }
+    return $props;
+  }
+
+
+  /**
+  * Attack the given URL in an attempt to find a principal URL
+  *
+  * @param string $url The URL to find the principal-URL from
+  */
+  function FindPrincipal( $url ) {
+    $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL',
+                                  'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1);
+
+    $principal_url = $this->HrefForProp('DAV::principal');
+
+    if ( !isset($principal_url) ) {
+      foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) {
+        if ( !isset($principal_url) ) {
+          $principal_url = $this->HrefValueInside($href);
+        }
+      }
+    }
+
+    return $this->PrincipalURL($principal_url);
+  }
+
+
+  /**
+  * Attack the given URL in an attempt to find a principal URL
+  *
+  * @param string $url The URL to find the calendar-home-set from
+  */
+  function FindCalendarHome( $recursed=false ) {
+    if ( !isset($this->principal_url) ) {
+      $this->FindPrincipal();
+    }
+    if ( $recursed ) {
+      $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0);
+    }
+
+    $calendar_home = array();
+    foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) {
+      if ( $this->xmlnodes[$v]['type'] != 'open' ) continue;
+      while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) {
+//        printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']);
+        if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) )
+          $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']);
+      }
+    }
+
+    if ( !$recursed && count($calendar_home) < 1 ) {
+      $calendar_home = $this->FindCalendarHome(true);
+    }
+
+    return $this->CalendarHomeSet($calendar_home);
+  }
+
+
+  /**
+  * Find the calendars, from the calendar_home_set
+  */
+  function FindCalendars( $recursed=false ) {
+    if ( !isset($this->calendar_home_set[0]) ) {
+      $this->FindCalendarHome();
+    }
+    $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1);
+
+    $calendars = array();
+    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) {
+      $calendar_urls = array();
+      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) {
+        $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1;
+      }
+
+      foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) {
+        $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+
+        if ( !isset($calendar_urls[$href]) ) continue;
+
+//        printf("Seems '%s' is a calendar.\n", $href );
+
+        $calendar = new CalendarInfo($href);
+        $ok_props = $this->GetOKProps($hnode);
+        foreach( $ok_props AS $v ) {
+//          printf("Looking at: %s[%s]\n", $href, $v['tag'] );
+          switch( $v['tag'] ) {
+            case 'http://calendarserver.org/ns/:getctag':
+              $calendar->getctag = $v['value'];
+              break;
+            case 'DAV::displayname':
+              $calendar->displayname = $v['value'];
+              break;
+          }
+        }
+        $calendars[] = $calendar;
+      }
+    }
+
+    return $this->CalendarUrls($calendars);
+  }
+
+
+  /**
+  * Find the calendars, from the calendar_home_set
+  */
+  function GetCalendarDetails( $url = null ) {
+    if ( isset($url) ) $this->SetCalendar($url);
+
+    $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' );
+    $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0);
+
+    $hnode = $this->xmltags['DAV::href'][0];
+    $href = rawurldecode($this->xmlnodes[$hnode]['value']);
+
+    $calendar = new CalendarInfo($href);
+    $ok_props = $this->GetOKProps($hnode);
+    foreach( $ok_props AS $k => $v ) {
+      $name = preg_replace( '{^.*:}', '', $v['tag'] );
+      if ( isset($v['value'] ) ) {
+        $calendar->{$name} = $v['value'];
+      }
+/*      else {
+        printf( "Calendar property '%s' has no text content\n", $v['tag'] );
+      }*/
+    }
+
+    return $calendar;
+  }
+
+
+  /**
+  * Get all etags for a calendar
+  */
+  function GetCollectionETags( $url = null ) {
+    if ( isset($url) ) $this->SetCalendar($url);
+
+    $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1);
+
+    $etags = array();
+    if ( isset($this->xmltags['DAV::getetag']) ) {
+      foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) {
+        $href = $this->HrefForProp('DAV::getetag', $k);
+        if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) $etags[$href] = $this->xmlnodes[$v]['value'];
+      }
+    }
+
+    return $etags;
+  }
+
+
+  /**
+  * Get a bunch of events for a calendar with a calendar-multiget report
+  */
+  function CalendarMultiget( $event_hrefs, $url = null ) {
+
+    if ( isset($url) ) $this->SetCalendar($url);
+
+    $hrefs = '';
+    foreach( $event_hrefs AS $k => $href ) {
+      $href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
+      $hrefs .= '<href>'.$href.'</href>';
+    }
+    $this->body = <<<EOXML
+<?xml version="1.0" encoding="utf-8" ?>
+<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+<prop><getetag/><C:calendar-data/></prop>
+$hrefs
+</C:calendar-multiget>
+EOXML;
+
+    $this->requestMethod = "REPORT";
+    $this->SetContentType("text/xml");
+    $this->DoRequest( $this->calendar_url );
+
+    $events = array();
+    if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) {
+      foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) {
+        $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k);
+//        echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]);
+        $events[$href] = $this->xmlnodes[$v]['value'];
+      }
+    }
+    else {
+      foreach( $event_hrefs AS $k => $href ) {
+        $this->DoGETRequest($href);
+        $events[$href] = $this->httpResponseBody;
+      }
+    }
+
+    return $events;
+  }
+
+
+  /**
+  * Given XML for a calendar query, return an array of the events (/todos) in the
+  * response.  Each event in the array will have a 'href', 'etag' and '$response_type'
+  * part, where the 'href' is relative to the calendar and the '$response_type' contains the
+  * definition of the calendar data in iCalendar format.
+  *
+  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+  * @param string $url The URL of the calendar, or null to use the 'current' calendar_url
+  *
+  * @return array An array of the relative URLs, etags, and events from the server.  Each element of the array will
+  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+  *               etag (which only varies when the data changes) and the calendar data in iCalendar format.
+  */
+  function DoCalendarQuery( $filter, $url = null ) {
+
+    if ( isset($url) ) $this->SetCalendar($url);
+
+    $this->body = <<<EOXML
+<?xml version="1.0" encoding="utf-8" ?>
+<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+  <D:prop>
+    <C:calendar-data/>
+    <D:getetag/>
+  </D:prop>$filter
+</C:calendar-query>
+EOXML;
+
+    $this->requestMethod = "REPORT";
+    $this->SetContentType("text/xml");
+    $this->DoRequest( $this->calendar_url );
+
+    $report = array();
+    foreach( $this->xmltags as $k => $v ) {
+      switch( $v['tag'] ) {
+        case 'DAV::response':
+          if ( $v['type'] == 'open' ) {
+            $response = array();
+          }
+          elseif ( $v['type'] == 'close' ) {
+            $report[] = $response;
+          }
+          break;
+        case 'DAV::href':
+          $response['href'] = basename( rawurldecode($v['value']) );
+          break;
+        case 'DAV::getetag':
+          $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
+          break;
+        case 'urn:ietf:params:xml:ns:caldav:calendar-data':
+          $response['data'] = $v['value'];
+          break;
+      }
+    }
+    return $report;
+  }
+
+
+  /**
+  * Get the events in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEvents( $start = null, $finish = null, $relative_url = '' ) {
+    $filter = "";
+    if ( isset($start) && isset($finish) )
+        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+    else
+        $range = '';
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+      <C:comp-filter name="VEVENT">
+        $range
+      </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the todo's in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param boolean   $completed Whether to include completed tasks
+  * @param boolean   $cancelled Whether to include cancelled tasks
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetTodos( $start, $finish, $completed = false, $cancelled = false, $relative_url = "" ) {
+
+    if ( $start && $finish ) {
+$time_range = <<<EOTIME
+                <C:time-range start="$start" end="$finish"/>
+EOTIME;
+    }
+
+    // Warning!  May contain traces of double negatives...
+    $neg_cancelled = ( $cancelled === true ? "no" : "yes" );
+    $neg_completed = ( $cancelled === true ? "no" : "yes" );
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VTODO">
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+                </C:prop-filter>
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+                </C:prop-filter>$time_range
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by UID
+  *
+  * @param uid
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEntryByUid( $uid, $relative_url = '' ) {
+    $filter = "";
+    if ( $uid ) {
+      $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VEVENT">
+                <C:prop-filter name="UID">
+                        <C:text-match icollation="i;octet">$uid</C:text-match>
+                </C:prop-filter>
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+    }
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by HREF
+  *
+  * @param string    $href         The href from a call to GetEvents or GetTodos etc.
+  *
+  * @return string The iCalendar of the calendar entry
+  */
+  function GetEntryByHref( $href ) {
+    $href = str_replace( rawurlencode('/'),'/',rawurlencode($href));
+    return $this->DoGETRequest( $href );
+  }
+
+}
+
+/**
+* Usage example
+*
+* $cal = new CalDAVClient( "http://calendar.example.com/caldav.php/username/calendar/", "username", "password", "calendar" );
+* $options = $cal->DoOptionsRequest();
+* if ( isset($options["PROPFIND"]) ) {
+*   // Fetch some information about the events in that calendar
+*   $cal->SetDepth(1);
+*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+* }
+* // Fetch all events for February
+* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+* foreach ( $events AS $k => $event ) {
+*   do_something_with_event_data( $event['data'] );
+* }
+* $acc = array();
+* $acc["google"] = array(
+* "user"=>"kunsttherapie@gmail.com",
+* "pass"=>"xxxxx",
+* "server"=>"ssl://www.google.com",
+* "port"=>"443",
+* "uri"=>"https://www.google.com/calendar/dav/kunsttherapie@gmail.com/events/",
+* );
+*
+* $acc["davical"] = array(
+* "user"=>"some_user",
+* "pass"=>"big secret",
+* "server"=>"calendar.foo.bar",
+* "port"=>"80",
+* "uri"=>"http://calendar.foo.bar/caldav.php/some_user/home/",
+* );
+* //*******************************
+*
+* $account = $acc["davical"];
+*
+* //*******************************
+* $cal = new CalDAVClient( $account["uri"], $account["user"], $account["pass"], "", $account["server"], $account["port"] );
+* $options = $cal->DoOptionsRequest();
+* print_r($options);
+*
+* //*******************************
+* //*******************************
+*
+* $xmlC = <<<PROPP
+* <?xml version="1.0" encoding="utf-8" ?>
+* <D:propfind xmlns:D="DAV:" xmlns:C="http://calendarserver.org/ns/">
+*     <D:prop>
+*             <D:displayname />
+*             <C:getctag />
+*             <D:resourcetype />
+*
+*     </D:prop>
+* </D:propfind>
+* PROPP;
+* //if ( isset($options["PROPFIND"]) ) {
+*   // Fetch some information about the events in that calendar
+* //  $cal->SetDepth(1);
+* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+* //  print_r( $folder_xml);
+* //}
+*
+* // Fetch all events for February
+* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+* foreach ( $events as $k => $event ) {
+*     print_r($event['data']);
+*     print "\n---------------------------------------------\n";
+* }
+*
+* //*******************************
+* //*******************************
+*/
Index: contrib/davical/inc/caldav-REPORT-pps-set.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-pps-set.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-pps-set.php	(revision 3733)
@@ -0,0 +1,36 @@
+<?php
+/**
+* DAViCal CalDAV Server - handle principal-search-property-set report (RFC3744)
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+
+/**
+* Wrap an individual property name as needed
+*/
+function property_response( &$xmldoc, $property ) {
+  $prop = new XMLElement( 'prop' );
+  $xmldoc->NSElement($prop, $property );
+  return new XMLElement( 'principal-search-property', $prop );
+}
+
+$principal_search_property_set = array(
+  'DAV::displayname',
+  'urn:ietf:params:xml:ns:caldav:calendar-home-set',
+  'urn:ietf:params:xml:ns:caldav:calendar-user-address-set'
+);
+
+$responses = array();
+foreach( $principal_search_property_set AS $k => $tag ) {
+  $responses[] = property_response( $reply, $tag );
+}
+
+
+$report = new XMLElement( 'principal-search-property-set', $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $report );
Index: contrib/davical/inc/CalDAVPrincipal.php
===================================================================
--- contrib/davical/inc/CalDAVPrincipal.php	(revision 3733)
+++ contrib/davical/inc/CalDAVPrincipal.php	(revision 3733)
@@ -0,0 +1,704 @@
+<?php
+/**
+* An object representing a DAV 'Principal'
+*
+* @package   davical
+* @subpackage   Principal
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morhposs.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+/**
+* @var $_CalDAVPrincipalCache
+* A global variable holding a cache of any DAV Principals which are
+* read from the DB.
+*/
+$_CalDAVPrincipalCache = (object) array( 'p' => array(), 'u' => array() );
+
+
+/**
+* A class for things to do with a DAV Principal
+*
+* @package   davical
+*/
+class CalDAVPrincipal
+{
+  /**
+  * @var The home URL of the principal
+  */
+  var $url;
+
+  /**
+  * @var Identifies whether this principal exists in the DB yet
+  */
+  private $exists;
+
+  /**
+  * @var RFC4791: Identifies the URL(s) of any WebDAV collections that contain
+  * calendar collections owned by the associated principal resource.
+  */
+  private $calendar_home_set;
+
+  /**
+  * @var CardDAV: Identifies the URL(s) of any WebDAV collections that contain
+  * addressbook collections owned by the associated principal resource.
+  */
+  private $addressbook_home_set;
+
+  /**
+  * @var Obsolete: Identifies the URL(s) of any calendars participating in free/busy
+  */
+  private $calendar_free_busy_set;
+
+  /**
+  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+  * Inbox collection owned by the associated principal resource.
+  */
+  var $schedule_inbox_url;
+
+  /**
+  * @var draft-desruisseaux-caldav-sched-03: Identify the URL of the scheduling
+  * Outbox collection owned by the associated principal resource.
+  */
+  var $schedule_outbox_url;
+
+  /**
+  * @var Whether or not we are using an e-mail address based URL.
+  */
+  var $by_email;
+
+  /**
+  * @var RFC3744: The principals that are direct members of this group.
+  */
+  protected $_is_group;
+
+  /**
+  * @var RFC3744: The principals that are direct members of this group.
+  */
+  protected $group_member_set;
+
+  /**
+  * @var RFC3744: The groups in which the principal is directly a member.
+  */
+  protected $group_membership;
+
+  /**
+   * @var caldav-cu-proxy-02: The principals which this one has read permissions on.
+   */
+  protected $read_proxy_for;
+
+  /**
+   * @var caldav-cu-proxy-02: The principals which this one has read-write prmissions for.
+   */
+  protected $write_proxy_for;
+
+   /**
+   * @var caldav-cu-proxy-02: The principals which have read permissions on this one.
+   */
+  protected $read_proxy_group;
+
+  /**
+   * @var caldav-cu-proxy-02: The principals which have write permissions on this one.
+   */
+  protected $write_proxy_group;
+
+  /**
+   * @var CardDAV: The URL to an addressbook entry for this principal
+   */
+  protected $principal_address;
+
+  /**
+   * @var The username for this principal
+   */
+  protected $username;
+
+  /**
+   * @var The dav_name for this principal - a partial path
+   */
+  protected $dav_name;
+
+  /**
+  * Constructor
+  * @param mixed $parameters If null, an empty Principal is created.  If it
+  *              is an integer then that ID is read (if possible).  If it is
+  *              an array then the Principal matching the supplied elements
+  *              is read.  If it is an object then it is expected to be a 'usr'
+  *              record that was read elsewhere.
+  *
+  * @return boolean Whether we actually read data from the DB to initialise the record.
+  */
+  function __construct( $parameters = null ) {
+    global $session, $c;
+
+    $this->exists = null;
+    $this->url = null;
+
+    if ( $parameters == null ) return false;
+    $this->by_email = false;
+    if ( is_object($parameters) ) {
+      dbg_error_log( 'principal', 'Principal: record for %s', $parameters->username );
+      $usr = $parameters;
+    }
+    else if ( is_int($parameters) ) {
+      dbg_error_log( 'principal', 'Principal: %d', $parameters );
+      $usr = getUserByID($parameters);
+      $this->user_no = $parameters['user_no'];
+    }
+    else if ( is_array($parameters) ) {
+      if ( ! isset($parameters['options']['allow_by_email']) ) $parameters['options']['allow_by_email'] = false;
+      if ( isset($parameters['username']) ) {
+        $usr = getUserByName($parameters['username']);
+        $this->username = $parameters['username'];
+      }
+      else if ( isset($parameters['user_no']) ) {
+        $usr = getUserByID($parameters['user_no']);
+        $this->user_no = $parameters['user_no'];
+      }
+      else if ( isset($parameters['email']) && $parameters['options']['allow_by_email'] ) {
+        if ( $username = $this->UsernameFromEMail($parameters['email']) ) {
+          $usr = getUserByName($username);
+          $this->username = $username;
+        }
+      }
+      else if ( isset($parameters['path']) ) {
+        dbg_error_log( 'principal', 'Finding Principal from path: "%s", options.allow_by_email: "%s"', $parameters['path'], $parameters['options']['allow_by_email'] );
+        if ( $username = $this->UsernameFromPath($parameters['path'], $parameters['options']) ) {
+          $usr = getUserByName($username);
+          $this->username = $username;
+        }
+      }
+      else if ( isset($parameters['principal-property-search']) ) {
+        $usr = $this->PropertySearch($parameters['principal-property-search']);
+      }
+    }
+    if ( !isset($usr) || !is_object($usr) ) {
+      $this->exists = false;
+      return false;
+    }
+    $this->exists = true;
+    $this->InitialiseRecord($usr);
+
+    if ( is_array($parameters) && !isset($parameters['username']) && !isset($parameters['user_no'])
+                 && isset($parameters['path']) && preg_match('{^/(~|principals/)}', $parameters['path']) ) {
+      // Force it to match
+      $this->url = $parameters['path'];
+      $this->dav_name = $parameters['path'];
+    }
+  }
+
+
+  /**
+  * Initialise the Principal object from a $usr record from the DB.
+  * @param object $usr The usr record from the DB.
+  */
+  function InitialiseRecord($usr) {
+    global $c;
+    foreach( $usr AS $k => $v ) {
+      $this->{$k} = $v;
+    }
+    if ( !isset($this->modified) ) $this->modified = $this->updated;
+    if ( !isset($this->created) )  $this->created  = $this->joined;
+
+    $this->dav_etag = md5($this->username . $this->updated);
+
+    $this->_is_group = (isset($usr->type_id) && $usr->type_id == 3);
+
+    $this->principal_url = ConstructURL( '/'.$this->username.'/', true );
+    $this->url = $this->principal_url;
+
+    $this->principal_address = $this->principal_url . 'principal.vcf';
+
+    $this->user_address_set = array(
+       'mailto:'.$this->email,
+       ConstructURL( '/'.$this->username.'/', true ),
+//       ConstructURL( '/~'.$this->username.'/', true ),
+//       ConstructURL( '/__uuids__/'.$this->username.'/', true ),
+    );
+    $this->schedule_inbox_url = sprintf( '%s.in/', $this->url);
+    $this->schedule_outbox_url = sprintf( '%s.out/', $this->url);
+    $this->dropbox_url = sprintf( '%s.drop/', $this->url);
+    $this->notifications_url = sprintf( '%s.notify/', $this->url);
+
+    if ( isset ( $c->notifications_server ) ) {
+      $this->xmpp_uri = 'xmpp:pubsub.'.$c->notifications_server['host'].'?pubsub;node=/davical-'.$this->principal_id;
+      $this->xmpp_server = $c->notifications_server['host'];
+    }
+
+    if ( $this->_is_group ) {
+      $this->group_member_set = array();
+      $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=member_id) JOIN usr USING(user_no) WHERE group_id = :group_id ORDER BY principal.principal_id ', array( ':group_id' => $this->principal_id) );
+      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+        while( $member = $qry->Fetch() ) {
+          $this->group_member_set[] = ConstructURL( '/'. $member->username . '/', true);
+        }
+      }
+    }
+
+    $this->group_membership = array();
+    $qry = new AwlQuery('SELECT usr.username FROM group_member JOIN principal ON (principal_id=group_id) JOIN usr USING(user_no) WHERE member_id = :member_id UNION SELECT usr.username FROM group_member LEFT JOIN grants ON (to_principal=group_id) JOIN principal ON (principal_id=by_principal) JOIN usr USING(user_no) WHERE member_id = :member_id and by_principal != member_id ORDER BY 1', array( ':member_id' => $this->principal_id ) );
+    if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+      while( $group = $qry->Fetch() ) {
+        $this->group_membership[] = ConstructURL( '/'. $group->username . '/', true);
+      }
+    }
+
+    $this->read_proxy_group = null;
+    $this->write_proxy_group = null;
+    $this->write_proxy_for = null;
+    $this->read_proxy_for = null;
+
+    dbg_error_log( 'principal', ' User: %s (%d) URL: %s, Home: %s, By Email: %d', $this->username, $this->user_no, $this->url, $this->by_email );
+  }
+
+
+  /**
+  * Split this out so we do it as infrequently as possible, given the cost.
+  */
+  function FetchProxyGroups() {
+    global $c;
+
+    $this->read_proxy_group = array();
+    $this->write_proxy_group = array();
+    $this->write_proxy_for = array();
+    $this->read_proxy_for = array();
+
+    if ( !isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy === false ) {
+
+      $write_priv = privilege_to_bits(array('write'));
+      // whom are we a proxy for? who is a proxy for us?
+      // (as per Caldav Proxy section 5.1 Paragraph 7 and 5)
+      $sql = 'SELECT principal_id, username, pprivs(:request_principal::int8,principal_id,:scan_depth::int) FROM principal JOIN usr USING(user_no) WHERE principal_id IN (SELECT * from p_has_proxy_access_to(:request_principal,:scan_depth))';
+      $params = array( ':request_principal' => $this->principal_id, ':scan_depth' => $c->permission_scan_depth );
+      $qry = new AwlQuery($sql, $params);
+      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+        while( $relationship = $qry->Fetch() ) {
+          if ( (bindec($relationship->pprivs) & $write_priv) != 0 ) {
+            $this->write_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-write/', true);
+          }
+          else {
+            $this->read_proxy_for[] = ConstructURL( '/'. $relationship->username . '/', true);
+            $this->group_membership[] = ConstructURL( '/'. $relationship->username . '/calendar-proxy-read/', true);
+          }
+        }
+      }
+
+      $sql = 'SELECT principal_id, username, pprivs(:request_principal::int8,principal_id,:scan_depth::int) FROM principal JOIN usr USING(user_no) WHERE principal_id IN (SELECT * from grants_proxy_access_from_p(:request_principal,:scan_depth))';
+      $qry = new AwlQuery($sql, $params ); // reuse $params assigned for earlier query
+      if ( $qry->Exec('CalDAVPrincipal') && $qry->rows() > 0 ) {
+        while( $relationship = $qry->Fetch() ) {
+          if ( bindec($relationship->pprivs) & $write_priv ) {
+            $this->write_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+          }
+          else {
+            $this->read_proxy_group[] = ConstructURL( '/'. $relationship->username . '/', true);
+          }
+        }
+      }
+//      @dbg_error_log( 'principal', 'Read-proxy-for:    %s', implode(',',$this->read_proxy_for) );
+//      @dbg_error_log( 'principal', 'Write-proxy-for:   %s', implode(',',$this->write_proxy_for) );
+//      @dbg_error_log( 'principal', 'Read-proxy-group:  %s', implode(',',$this->read_proxy_group) );
+//      @dbg_error_log( 'principal', 'Write-proxy-group: %s', implode(',',$this->write_proxy_group) );
+    }
+  }
+
+
+  /**
+  * Accessor for the read proxy group
+  */
+  function ReadProxyGroup() {
+    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+    return $this->read_proxy_group;
+  }
+
+
+  /**
+  * Accessor for the write proxy group
+  */
+  function WriteProxyGroup() {
+    if ( !isset($this->write_proxy_group) ) $this->FetchProxyGroups();
+    return $this->write_proxy_group;
+  }
+
+
+  /**
+  * Accessor for read or write proxy
+  * @param string read/write - which sort of proxy list is requested.
+  */
+  function ProxyFor( $type ) {
+    if ( !isset($this->read_proxy_for) ) $this->FetchProxyGroups();
+    if ( $type == 'write' ) return $this->write_proxy_for;
+    return $this->read_proxy_for;
+  }
+
+
+  /**
+  * Accessor for the group membership - the groups this principal is a member of
+  */
+  function GroupMembership() {
+    if ( !isset($this->read_proxy_group) ) $this->FetchProxyGroups();
+    return $this->group_membership;
+  }
+
+
+  /**
+  * Accessor for the group member set - the members of this group
+  */
+  function GroupMemberSet() {
+    if ( ! $this->_is_group ) return null;
+    return $this->group_member_set;
+  }
+
+
+  /**
+  * Work out the username, based on elements of the path.
+  * @param string $path The path to be used.
+  * @param array $options The request options, controlling whether e-mail paths are allowed.
+  */
+  function UsernameFromPath( $path, $options = null ) {
+    global $session, $c;
+
+    if ( $path == '/' || $path == '' ) {
+      dbg_error_log( 'principal', 'No useful path split possible' );
+      return $session->username;
+    }
+
+    $path_split = explode('/', $path );
+    @dbg_error_log( 'principal', 'Path split into at least /// %s /// %s /// %s', $path_split[1], $path_split[2], $path_split[3] );
+
+    $username = $path_split[1];
+    if ( $path_split[1] == 'principals' && isset($path_split[3]) ) $username = $path_split[3];
+    if ( substr($username,0,1) == '~' ) $username = substr($username,1);
+
+    if ( isset($options['allow_by_email']) && $options['allow_by_email'] && preg_match( '#^(\S+@\S+[.]\S+)$#', $username) ) {
+      $username = $this->UsernameFromEMail($username);
+    }
+    return $username;
+  }
+
+
+  /**
+  * Work out the username, based on the given e-mail
+  * @param string $email The email address to be used.
+  */
+  function UsernameFromEMail( $email ) {
+    @dbg_error_log( 'principal', 'Retrieving username from e-mail address "%s" ', $email );
+    $qry = new AwlQuery('SELECT username FROM usr WHERE email = :email', array( ':email' => $email ) );
+    if ( $qry->Exec('principal') && $user = $qry->Fetch() ) {
+      $username = $user->username;
+      $this->by_email = true;
+      return $username;
+    }
+    return null;
+  }
+
+
+  /**
+  * Does this principal exist?
+  * @return boolean Whether or not it exists.
+  */
+  function Exists() {
+    return $this->exists;
+  }
+
+
+  /**
+  * Is this a group principal?
+  * @return boolean Whether this is a group principal
+  */
+  function IsGroup() {
+    return $this->_is_group;
+  }
+
+
+  /**
+  * Return the username
+  * @return string The username
+  */
+  function username() {
+    return (isset($this->username)?$this->username:'username not set');
+  }
+
+
+  /**
+  * Return the partial path representing this principal
+  * @return string The dav_name
+  */
+  function dav_name() {
+    if ( !isset($this->dav_name) ) {
+      if ( !isset($this->username) ) $this->dav_name = '';
+      else $this->dav_name = '/'.$this->username.'/';
+    }
+    return $this->dav_name;
+  }
+
+
+  /**
+  * Get the calendar_home_set, as lazily as possible
+  */
+  function calendar_home_set() {
+    if ( !isset($this->calendar_home_set) ) {
+      $this->calendar_home_set = array();
+/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_calendar AND user_no = :user_no', array( ':user_no' => $this->user_no));
+      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+        if ( $qry->rows() > 0 ) {
+          while( $calendar = $qry->Fetch() ) {
+            $this->calendar_home_set[] = ConstructURL($calendar->parent_container, true);
+          }
+        }
+        else {*/
+          $this->calendar_home_set[] = $this->principal_url;
+//         }
+//       }
+    }
+    return $this->calendar_home_set;
+  }
+
+
+  /**
+  * Get the addressbook_home_set, as lazily as possible
+  */
+  function addressbook_home_set() {
+    if ( !isset($this->addressbook_home_set) ) {
+     $this->addressbook_home_set = array();
+/*      $qry = new AwlQuery('SELECT DISTINCT parent_container FROM collection WHERE is_addressbook AND user_no = :user_no', array( ':user_no' => $this->user_no));
+      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+        if ( $qry->rows() > 0 ) {
+          while( $addressbook = $qry->Fetch() ) {
+            $this->addressbook_home_set[] = ConstructURL($addressbook->parent_container, true);
+          }
+        }
+        else {*/
+          $this->addressbook_home_set[] = $this->principal_url;
+//         }
+//       }
+    }
+    return $this->addressbook_home_set;
+  }
+
+
+  /**
+  * Get the calendar_free_busy_set, as lazily as possible
+  */
+  function calendar_free_busy_set() {
+    if ( !isset($this->calendar_free_busy_set) ) {
+      /**
+      * calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV
+      * in favour of
+      */
+      $this->calendar_free_busy_set = array();
+      $qry = new AwlQuery('SELECT dav_name FROM collection WHERE user_no = :user_no AND is_calendar AND (schedule_transp = \'opaque\' OR schedule_transp IS NULL) ORDER BY user_no, collection_id',
+                        array( ':user_no' => $this->user_no) );
+      if ( $qry->Exec('principal',__LINE__,__FILE__) ) {
+        while( $calendar = $qry->Fetch() ) {
+          $this->calendar_free_busy_set[] = ConstructURL($calendar->dav_name, true);
+        }
+      }
+    }
+    return $this->calendar_free_busy_set;
+  }
+
+  /**
+  * Return the privileges bits for the current session user to this resource
+  */
+  function Privileges() {
+    if ( !isset($this->privileges) ) $this->privileges = 0;
+    if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
+    return $this->privileges;
+  }
+
+
+  /**
+  * Returns a representation of the principal as a collection
+  */
+  function AsCollection() {
+    $collection = (object) array(
+                            'collection_id' => (isset($this->principal_id) ? $this->principal_id : 0),
+                            'is_calendar' => false,
+                            'is_addressbook' => false,
+                            'is_principal' => true,
+                            'type'     => 'principal' . (substr($this->dav_name(), 0, 12) == '/principals/'?'_link':''),
+                            'user_no'  => (isset($this->user_no)  ? $this->user_no : 0),
+                            'username' => $this->username(),
+                            'dav_name' => $this->dav_name,
+                            'parent_container' => '/',
+                            'email'    => (isset($this->email)    ? $this->email : ''),
+                            'created'  => (isset($this->created)  ? $this->created : date('Ymd\THis')),
+                            'updated'  => (isset($this->updated)  ? $this->updated : date('Ymd\THis'))
+                  );
+    $collection->dav_etag = (isset($this->dav_etag) ? $this->dav_etag : md5($collection->username . $collection->updated));
+    $collection->dav_displayname =  (isset($this->dav_displayname) ? $this->dav_displayname : (isset($this->fullname) ? $this->fullname : $collection->username));
+
+    return $collection;
+  }
+
+  /**
+  * Returns properties which are specific to this principal
+  */
+  function PrincipalProperty( $tag, $prop, &$reply, &$denied ) {
+
+    dbg_error_log('principal',': RenderAsXML: Principal Property "%s"', $tag );
+    switch( $tag ) {
+      case 'DAV::getcontenttype':
+        $prop->NewElement('getcontenttype', 'httpd/unix-directory' );
+        break;
+
+      case 'DAV::resourcetype':
+        $prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
+        break;
+
+      case 'DAV::displayname':
+        $prop->NewElement('displayname', $this->fullname );
+        break;
+
+      case 'DAV::principal-URL':
+        $prop->NewElement('principal-URL', $reply->href($this->principal_url) );
+        break;
+
+      case 'DAV::getlastmodified':
+        $prop->NewElement('getlastmodified', ISODateToHTTPDate($this->modified) );
+        break;
+
+      case 'DAV::creationdate':
+        $prop->NewElement('creationdate', DateToISODate($this->created) );
+        break;
+
+      case 'DAV::getcontentlanguage':
+        /** Use the principal's locale by preference, otherwise system default */
+        $locale = (isset($c->current_locale) ? $c->current_locale : '');
+        if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
+        $prop->NewElement('getcontentlanguage', $locale );
+        break;
+
+      case 'DAV::group-member-set':
+        if ( ! $this->_is_group ) return false;
+        $prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
+        break;
+
+      case 'DAV::group-membership':
+        $prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
+        $reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
+        $reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
+        break;
+
+      case 'http://calendarserver.org/ns/:dropbox-home-URL':
+        $reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
+        break;
+
+      case 'http://calendarserver.org/ns/:xmpp-server':
+        if ( ! isset( $this->xmpp_uri ) ) return false;
+        $reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
+        break;
+
+      case 'http://calendarserver.org/ns/:xmpp-uri':
+        if ( ! isset( $this->xmpp_uri ) ) return false;
+        $reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:addressbook-home-set':
+        $reply->NSElement($prop, $tag, $reply->href( $this->addressbook_home_set() ) );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
+        $reply->NSElement($prop, $tag, $reply->href( $this->calendar_home_set() ) );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
+        $reply->CalDAVElement( $prop, 'calendar-free-busy-set', $reply->href( $this->calendar_free_busy_set() ) );
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
+        $reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
+        break;
+
+      case 'DAV::owner':
+        // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner
+        $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url ) );
+        break;
+
+      case 'DAV::principal-collection-set':
+        $reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
+        break;
+
+      // Empty tag responses.
+      case 'DAV::alternate-URI-set':
+        $prop->NewElement( $reply->Tag($tag));
+        break;
+
+      case 'SOME-DENIED-PROPERTY':  /** @todo indicating the style for future expansion */
+        $denied[] = $reply->Tag($tag);
+        break;
+
+      default:
+        return false;
+        break;
+    }
+
+    return true;
+  }
+
+
+  /**
+  * Render XML for a single Principal (user) from the DB
+  *
+  * @param array $properties The requested properties for this principal
+  * @param reference $reply A reference to the XMLDocument being used for the reply
+  * @param boolean $props_only Default false.  If true will only return the fragment with the properties, not a full response fragment.
+  *
+  * @return string An XML fragment with the requested properties for this principal
+  */
+  function RenderAsXML( $properties, &$reply, $props_only = false ) {
+    global $request;
+
+    dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
+
+    $prop = new XMLElement('prop');
+    $denied = array();
+    $not_found = array();
+    foreach( $properties AS $k => $tag ) {
+      if ( ! $this->PrincipalProperty( $tag, $prop, $reply, $denied ) ) {
+        dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
+        $not_found[] = $reply->Tag($tag);
+      }
+    }
+
+    if ( $props_only ) return $prop;
+
+    $status = new XMLElement('status', 'HTTP/1.1 200 OK' );
+
+    $propstat = new XMLElement( 'propstat', array( $prop, $status) );
+    $href = $reply->href($this->url );
+
+    $elements = array($href,$propstat);
+
+    if ( count($denied) > 0 ) {
+      $status = new XMLElement('status', 'HTTP/1.1 403 Forbidden' );
+      $noprop = new XMLElement('prop');
+      foreach( $denied AS $k => $v ) {
+        $noprop->NewElement( $v );
+      }
+      $elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
+    }
+
+    if ( count($not_found) > 0 ) {
+      $status = new XMLElement('status', 'HTTP/1.1 404 Not Found' );
+      $noprop = new XMLElement('prop');
+      foreach( $not_found AS $k => $v ) {
+        $noprop->NewElement( $v );
+      }
+      $elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
+    }
+
+    $response = new XMLElement( 'response', $elements );
+
+    return $response;
+  }
+
+}
Index: contrib/davical/inc/caldav-MKCOL.php
===================================================================
--- contrib/davical/inc/caldav-MKCOL.php	(revision 3733)
+++ contrib/davical/inc/caldav-MKCOL.php	(revision 3733)
@@ -0,0 +1,256 @@
+<?php
+/**
+* CalDAV Server - handle MKCOL and MKCALENDAR method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst IT Ltd, Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+dbg_error_log('MKCOL', 'method handler');
+require_once('AwlQuery.php');
+
+$request->NeedPrivilege('DAV::bind');
+$displayname = $request->path;
+
+// Enforce trailling '/' on collection name
+if ( ! preg_match( '#/$#', $request->path ) ) {
+  dbg_error_log( 'MKCOL', 'Add trailling "/" to "%s"', $request->path);
+  $request->path .= '/';
+}
+
+$parent_container = '/';
+if ( preg_match( '#^(.*/)([^/]+)(/)?$#', $request->path, $matches ) ) {
+  $parent_container = $matches[1];
+  $displayname = $matches[2];
+}
+
+require_once('DAVResource.php');
+$parent = new DAVResource( $parent_container );
+if ( $parent->IsSchedulingCollection( 'inbox' ) ) {
+  $request->PreconditionFailed(403, 'urn:ietf:params:xml:ns:caldav:no-mkcol-in-inbox' );
+}
+
+
+$request_type = $request->method;
+$is_calendar = ($request_type == 'MKCALENDAR');
+$is_addressbook = false;
+
+$resourcetypes = '<DAV::collection/>';
+if ($is_calendar) $resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+
+require_once('XMLDocument.php');
+$reply = new XMLDocument(array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ));
+
+$failure_code = null;
+
+$failure = array();
+$dav_properties = array();
+
+if ( isset($request->xml_tags) ) {
+  /**
+  * The MKCOL request may contain XML to set some DAV properties
+  */
+  $position = 0;
+  $xmltree = BuildXMLTree( $request->xml_tags, $position);
+  if ( $xmltree->GetTag() == 'DAV::mkcol' ) $request_type = 'extended-mkcol';
+
+  if ( $xmltree->GetTag() != 'urn:ietf:params:xml:ns:caldav:mkcalendar' && $request_type != 'extended-mkcol' ) {
+    $request->DoResponse( 406, sprintf('The XML is not a "DAV::mkcol" or "urn:ietf:params:xml:ns:caldav:mkcalendar" document (%s)', $xmltree->GetTag()) );
+  }
+  $setprops = $xmltree->GetContent();   // <set>
+  $setprops = $setprops[0]->GetContent();  // <prop>
+  $setprops = $setprops[0]->GetContent();  // the array of properties.
+
+  foreach( $setprops AS $k => $setting ) {
+    $tag = $setting->GetTag();
+    $content = $setting->RenderContent();
+
+    dbg_error_log( 'MKCOL', 'Processing tag "%s"', $tag);
+
+    switch( $tag ) {
+
+      case 'DAV::resourcetype':
+        /** Any value for resourcetype other than 'calendar' is ignored */
+        dbg_error_log( 'MKCOL', 'Extended MKCOL with resourcetype specified. "%s"', $content);
+        $is_addressbook = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:carddav:addressbook'));
+        $is_calendar    = count($setting->GetPath('DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
+        if ( $is_addressbook && $is_calendar ) {
+          $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+              new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+              new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
+              new XMLElement('responsedescription', translate('Collections may not be both CalDAV calendars and CardDAV addressbooks at the same time') )
+          ));
+        }
+        else {
+          $resourcetypes = $setting->GetPath('DAV::resourcetype/*');
+          $resourcetypes = str_replace( "\n", "", implode('',$resourcetypes));
+          $success[$tag] = 1;
+        }
+        break;
+
+      case 'DAV::displayname':
+        $displayname = $content;
+        /**
+        * @todo This is definitely a bug in SOHO Organizer and we probably should respond
+        * with an error, rather than silently doing what they *seem* to want us to do.
+        */
+        if ( preg_match( '/^SOHO.Organizer.6\./', $_SERVER['HTTP_USER_AGENT'] ) ) {
+          dbg_error_log( 'MKCOL', 'Displayname is "/" to "%s"', $request->path);
+          $parent_container = $request->path;
+          $request->path .= $content . '/';
+        }
+        $success[$tag] = 1;
+        break;
+
+      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':  /** Ignored, since we will support all component types */
+      case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+      case 'urn:ietf:params:xml:ns:caldav:calendar-data':  /** Ignored, since we will support iCalendar 2.0 */
+      case 'urn:ietf:params:xml:ns:caldav:max-resource-size':  /** Ignored, since we will support arbitrary size */
+      case 'urn:ietf:params:xml:ns:caldav:min-date-time':  /** Ignored, since we will support arbitrary time */
+      case 'urn:ietf:params:xml:ns:caldav:max-date-time':  /** Ignored, since we will support arbitrary time */
+      case 'urn:ietf:params:xml:ns:caldav:max-instances':  /** Ignored, since we will support arbitrary instances */
+        $success[$tag] = 1;
+        break;
+
+      /**
+      * The following properties are read-only, so they will cause the request to fail
+      */
+      case 'DAV::getetag':
+      case 'DAV::getcontentlength':
+      case 'DAV::getcontenttype':
+      case 'DAV::getlastmodified':
+      case 'DAV::creationdate':
+      case 'DAV::lockdiscovery':
+      case 'DAV::supportedlock':
+        $failure['set-'.$tag] = new XMLElement( 'propstat', array(
+            new XMLElement( 'prop', new XMLElement($reply->Tag($tag))),
+            new XMLElement( 'status', 'HTTP/1.1 409 Conflict' ),
+            new XMLElement('responsedescription', translate('Property is read-only') )
+        ));
+        if ( isset($failure_code) && $failure_code != 409 ) $failure_code = 207;
+        else if ( !isset($failure_code) ) $failure_code = 409;
+        break;
+
+      /**
+      * If we don't have any special processing for the property, we just store it verbatim (which will be an XML fragment).
+      */
+      default:
+        $dav_properties[$tag] = $content;
+        $success[$tag] = 1;
+        break;
+    }
+  }
+
+  /**
+  * If we have encountered any instances of failure, the whole damn thing fails.
+  */
+  if ( count($failure) > 0 ) {
+    $props = array();
+    $status = array();
+    foreach( $success AS $tag => $v ) {
+      // Unfortunately although these succeeded, we failed overall, so they didn't happen...
+      $props[] = new XMLElement($reply->Tag($tag));
+    }
+
+    $status[] = new XMLElement( 'propstat', array(
+      new XMLElement('prop', $props),
+      new XMLElement('status', 'HTTP/1.1 424 Failed Dependency' )
+    ));
+
+    if ( $request_type == 'extended-mkcol' ) {
+      $request->DoResponse( $failure_code, $reply->Render('mkcol-response', array_merge( $status, $failure ), 'text/xml; charset="utf-8"' ) );
+    }
+    else {
+      array_unshift( $failure, $reply->href( ConstructURL($request->path) ) );
+      $failure[] = new XMLElement('responsedescription', translate('Some properties were not able to be set.') );
+
+      $request->DoResponse( 207, $reply->Render('multistatus', new XMLElement( 'response', $failure )), 'text/xml; charset="utf-8"' );
+    }
+
+  }
+}
+
+$sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
+$qry = new AwlQuery( $sql, array( ':dav_name' => $request->path) );
+if ( ! $qry->Exec('MKCOL',__LINE__,__FILE__) ) {
+  $request->DoResponse( 500, translate('Error querying database.') );
+}
+if ( $qry->rows() != 0 ) {
+  $request->DoResponse( 405, translate('A collection already exists at that location.') );
+}
+
+$qry = new AwlQuery();
+$qry->Begin();
+
+if ( ! $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
+                                 is_calendar, is_addressbook, resourcetypes, created, modified )
+              VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
+                      :is_calendar, :is_addressbook, :resourcetypes, current_timestamp, current_timestamp )',
+           array(
+              ':user_no'          => $request->user_no,
+              ':parent_container' => $parent_container,
+              ':dav_name'         => $request->path,
+              ':dav_etag'         => md5($request->user_no. $request->path),
+              ':dav_displayname'  => $displayname,
+              ':is_calendar'      => ($is_calendar ? 't' : 'f'),
+              ':is_addressbook'   => ($is_addressbook ? 't' : 'f'),
+              ':resourcetypes'    => $resourcetypes
+           ) ) ) {
+  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+}
+foreach( $dav_properties AS $k => $v ) {
+  if ( ! $qry->QDo('SELECT set_dav_property( :dav_name, :user_no, :tag::text, :value::text )',
+             array( ':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v) ) ) {
+    $request->DoResponse( 500, translate('Error writing calendar properties to database.') );
+  }
+}
+if ( !$qry->Commit() ) {
+  $request->DoResponse( 500, translate('Error writing calendar details to database.') );
+}
+dbg_error_log( 'MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
+header('Cache-Control: no-cache'); /** RFC4791 mandates this at 5.3.1 */
+$request->DoResponse( 201, '' );
+
+/**
+* @todo We could also respond to the request...
+*
+*   <?xml version="1.0" encoding="utf-8" ?>
+*   <C:mkcalendar xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+*     <D:set>
+*       <D:prop>
+*         <D:displayname>Lisa's Events</D:displayname>
+*         <C:calendar-description xml:lang="en">Calendar restricted to events.</C:calendar-description>
+*         <C:supported-calendar-component-set>
+*           <C:comp name="VEVENT"/>
+*         </C:supported-calendar-component-set>
+*         <C:calendar-timezone><![CDATA[BEGIN:VCALENDAR
+*   PRODID:-//Example Corp.//CalDAV Client//EN
+*   VERSION:2.0
+*   BEGIN:VTIMEZONE
+*   TZID:US-Eastern
+*   LAST-MODIFIED:19870101T000000Z
+*   BEGIN:STANDARD
+*   DTSTART:19671029T020000
+*   RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+*   TZOFFSETFROM:-0400
+*   TZOFFSETTO:-0500
+*   TZNAME:Eastern Standard Time (US & Canada)
+*   END:STANDARD
+*   BEGIN:DAYLIGHT
+*   DTSTART:19870405T020000
+*   RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+*   TZOFFSETFROM:-0500
+*   TZOFFSETTO:-0400
+*   TZNAME:Eastern Daylight Time (US & Canada)
+*   END:DAYLIGHT
+*   END:VTIMEZONE
+*   END:VCALENDAR
+*   ]]></C:calendar-timezone>
+*       </D:prop>
+*     </D:set>
+*   </C:mkcalendar>
+*
+*/
+
Index: contrib/davical/inc/vcard.php
===================================================================
--- contrib/davical/inc/vcard.php	(revision 3733)
+++ contrib/davical/inc/vcard.php	(revision 3733)
@@ -0,0 +1,512 @@
+<?php
+/**
+* Extend the vComponent to specifically handle VCARD resources
+*/
+
+require_once('AwlQuery.php');
+require_once('vComponent.php');
+
+class VCard extends vComponent {
+
+  /**
+CREATE TABLE addressbook_resource (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE PRIMARY KEY,
+  version TEXT,
+  uid TEXT,
+  nickname TEXT,
+  fn TEXT, -- fullname
+  n TEXT, -- Name Surname;First names
+  note TEXT,
+  org TEXT,
+  url TEXT,
+  fburl TEXT,
+  caluri TEXT
+);
+  */
+  function Write( $dav_id, $exists = true ) {
+    $qry = new AwlQuery();
+
+    // Only run a local transaction if we're not in one already.
+    $in_transaction = ($qry->TransactionState() == 1);
+    if ( ! $in_transaction ) $qry->Begin();
+
+    if ( $exists ) {
+      $sql = 'UPDATE addressbook_resource SET version=:version, uid=:uid, nickname=:nickname, fn=:fn, n=:name,
+note=:note, org=:org, url=:url, fburl=:fburl, caladruri=:caladruri, caluri=:caluri WHERE dav_id=:dav_id';
+    }
+    else {
+      $sql = 'INSERT INTO addressbook_resource ( dav_id, version, uid, nickname, fn, n, note, org, url, fburl, caladruri, caluri )
+VALUES( :dav_id, :version, :uid, :nickname, :fn, :name, :note, :org, :url, :fburl, :caladruri, :caluri )';
+    }
+
+    $params = array( ':dav_id' => $dav_id );
+
+    $wanted = array('VERSION' => true, 'UID' => true, 'NICKNAME' => true, 'FN' => true, 'N' => true,
+                    'NOTE'=> true, 'ORG' => true, 'URL' => true, 'FBURL' => true, 'CALADRURI' => true, 'CALURI' => true);
+    $properties = $this->GetProperties( $wanted );
+    foreach( $wanted AS $k => $v ) {
+      $pname = ':' . strtolower($k);
+      if ( $pname == ':n' ) $pname = ':name';
+      $params[$pname] = null;
+    }
+    foreach( $properties AS $k => $v ) {
+      $pname = ':' . strtolower($v->Name());
+      if ( $pname == ':n' ) $pname = ':name';
+      if ( !isset($params[$pname]) /** @TODO: or this is one is in the user's language */ ) $params[$pname] = $v->Value();
+    }
+
+    $qry->QDo( $sql, $params );
+
+    $this->WriteAddresses($dav_id);
+    $this->WritePhones($dav_id);
+    $this->WriteEmails($dav_id);
+
+    if ( ! $in_transaction ) $qry->Commit();
+  }
+
+
+  /**
+CREATE TABLE addressbook_address_adr (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  box_no TEXT,
+  unit_no TEXT,
+  street_address TEXT,
+  locality TEXT,
+  region TEXT,
+  postcode TEXT,
+  country TEXT,
+  property TEXT -- The full text of the property
+);
+  */
+  function WriteAddresses( $dav_id ) {
+    $addresses = $this->GetProperties('ADR');
+    $qry = new AwlQuery();
+
+    // Only run a local transaction if we're not in one already.
+    $in_transaction = ($qry->TransactionState() == 1);
+    if ( ! $in_transaction ) $qry->Begin();
+
+    $params = array( ':dav_id' => $dav_id );
+    $qry->QDo('DELETE FROM addressbook_address_adr WHERE dav_id = :dav_id', $params );
+    foreach( $addresses AS $adr ) {
+      $params[':type'] = $adr->GetParameterValue('TYPE');
+      $address = explode(';',$adr->Value());
+
+      // We use @ to suppress the warnings here, because the NULL in the database suits us well.
+      @$params[':box_no']   = $address[0];
+      @$params[':unit_no']  = $address[1];
+      @$params[':street_address'] = $address[2];
+      @$params[':locality'] = $address[3];
+      @$params[':region']   = $address[4];
+      @$params[':postcode'] = $address[5];
+      @$params[':country']  = $address[6];
+      $params[':property'] = $adr->Render();
+      $qry->QDo( 'INSERT INTO addressbook_address_adr (dav_id, type, box_no, unit_no, street_address, locality, region, postcode, country, property)
+VALUES( :dav_id, :type, :box_no, :unit_no, :street_address, :locality, :region, :postcode, :country, :property)', $params );
+    }
+    if ( ! $in_transaction ) $qry->Commit();
+  }
+
+
+  /**
+CREATE TABLE addressbook_address_tel (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  tel TEXT,
+  property TEXT -- The full text of the property
+);
+  */
+  function WritePhones( $dav_id ) {
+    $telephones = $this->GetProperties('TEL');
+    $qry = new AwlQuery();
+
+    // Only run a local transaction if we're not in one already.
+    $in_transaction = ($qry->TransactionState() == 1);
+    if ( ! $in_transaction ) $qry->Begin();
+
+    $params = array( ':dav_id' => $dav_id );
+    $qry->QDo('DELETE FROM addressbook_address_tel WHERE dav_id = :dav_id', $params );
+    foreach( $telephones AS $tel ) {
+      $params[':type'] = $tel->GetParameterValue('TYPE');
+      if ( ! isset($params[':type']) ) $params[':type'] = 'voice';
+      $params[':tel'] = $tel->Value();
+      $params[':property'] = $tel->Render();
+      $qry->QDo( 'INSERT INTO addressbook_address_tel (dav_id, type, tel, property) VALUES( :dav_id, :type, :tel, :property)', $params );
+    }
+    if ( ! $in_transaction ) $qry->Commit();
+  }
+
+
+  /**
+CREATE TABLE addressbook_address_email (
+  dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
+  type TEXT,
+  email TEXT,
+  property TEXT -- The full text of the property
+);
+  */
+  function WriteEmails( $dav_id ) {
+    $emails = $this->GetProperties('EMAIL');
+    $qry = new AwlQuery();
+
+    // Only run a local transaction if we're not in one already.
+    $in_transaction = ($qry->TransactionState() == 1);
+    if ( ! $in_transaction ) $qry->Begin();
+
+    $params = array( ':dav_id' => $dav_id );
+    $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params );
+    foreach( $emails AS $email ) {
+      $params[':type'] = $email->GetParameterValue('TYPE');
+      $params[':email'] = $email->Value();
+      $params[':property'] = $email->Render();
+      $qry->QDo( 'INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params );
+    }
+    if ( ! $in_transaction ) $qry->Commit();
+  }
+/**************** Incluido para funcionar com expresso *****************
+************************************************************************
+***********************************************************************/
+
+  function Write_expresso( $uidnumber, $exists = true ) {
+
+    // Only run a local transaction if we're not in one already.
+    $uid_value = $this->GetProperties('UID');
+    foreach($uid_value AS $G => $H) 
+    {
+     $uid = preg_split('/@/', $H->Value());
+    } 
+     
+    $qry = new AwlQuery( "select id_contact from phpgw_cc_contact where id_contact= :id AND id_owner = :owner ", array(':id' => $uid[0],':owner' => $uidnumber));
+    if( $qry->Exec() && $qry->rows() > 0 )
+    { 
+      $response_code = 200; 
+      $params = array(':id_contact' => $uid[0]);
+      $sql = 'UPDATE phpgw_cc_contact SET  photo=:photo, alias=:nickname, family_names =:ft, given_names=:name,
+      names_ordered =:fn, birthdate =:bday, notes=:note  WHERE id_contact=:id_contact';
+    }
+    else 
+    {
+      $response_code = 204;
+      $qry = new AwlQuery( "select max(id_contact) from phpgw_cc_contact");
+      $qry->Exec(); 
+      $cont=$qry->fetch();
+      $params = array(':id_contact' => $cont->max + 1);
+      $sql = 'INSERT INTO  phpgw_cc_contact ( id_contact, id_owner, photo, alias, given_names, family_names, names_ordered, birthdate, notes )
+      VALUES ( :id_contact, :id_owner, :photo, :nickname, :name , :ft, :fn , :bday, :note)';
+    }
+
+    $params[':id_owner']=$uidnumber;
+    $wanted = array('PHOTO' => true,'NICKNAME' => true, 'FN' => true, 'N' => true,
+                    'NOTE'=> true, 'BDAY' => true);
+    $properties = $this->GetProperties( $wanted );
+    
+    foreach( $wanted AS $k => $v ) {
+      $pname = ':' . strtolower($k);
+      if ( $pname == ':n' ) $pname = ':name';
+      $params[$pname] = null;
+    }
+    foreach( $properties AS $k => $v ) {
+      $pname = ':' . strtolower($v->Name());
+      if ( $pname == ':n' ) $pname = ':name';
+      if ( $pname == ':name' ){
+                $nome=preg_split('/;/', $v->Value());             
+                $params[':name']=$nome[1];
+                $params[':ft']=$nome[0];
+                continue;
+       }  
+      if ( !isset($params[$pname]) /** @TODO: or this is one is in the user's language */ ) $params[$pname] = $v->Value();
+    }
+    $qry = new AwlQuery( $sql, $params );
+    $qry->Exec();
+    if ( $response_code == 200)
+          $id_contactt = $uid[0];
+    else
+          $id_contactt = $cont->max + 1;
+    $emails = $this->GetProperties('EMAIL');
+  
+    $tels = $this->GetProperties('TEL');
+    if ( isset($emails) || isset($tels))
+      {
+        $qry = new AwlQuery("select id_connection from phpgw_cc_contact_conns where id_contact= :id", array(':id' => $id_contactt ));
+        if ($qry->Exec() && $qry->rows() > 0)
+           {
+             while( $idconns = $qry->Fetch() ) {
+                   $qryy = new AwlQuery("DELETE from phpgw_cc_contact_conns where id_contact= :id AND id_connection = :id_connection", array(':id' => $id_contactt ,':id_connection' => $idconns->id_connection));
+                   $qryy->Exec(); 
+                   $qryy = new AwlQuery("DELETE from phpgw_cc_connections  where id_connection = :id_connection", array(':id_connection' => $idconns->id_connection));
+                   $qryy->Exec(); 
+                  
+              }     
+                if (isset($emails))                       
+                   { 
+                     foreach( $emails AS $email ) {
+                        $params[':type'] = $email->GetParameterValue('TYPE');
+                        $params[':email'] = $email->Value();
+                        $qry = new AwlQuery( "select max(id_connection) from phpgw_cc_connections");
+                        $qry->Exec();
+                        $cont=$qry->fetch();
+                        if ( strtoupper($params[':type']) == 'WORK' )
+                           {$params[':type'] = "Principal";
+                            $params[':default'] = true;
+                           }
+                        else if ( $params[':type'] == 'pref' )
+                           {$params[':type'] = "Principal";
+                            $params[':default'] = true;
+                           }
+                        else
+                          {
+                           $params[':default'] = false;
+                           $params[':type'] = "Alternativo";  
+                           }
+                        $params[':id_conns'] = $cont->max + 1;
+                        dbg_log_array( "PUTTTTTT", 'EMAILSSSS', $params, true );
+      			$qry = new AwlQuery( 'INSERT INTO phpgw_cc_connections (id_connection, connection_name, connection_value,  connection_is_default) VALUES( :id_conns, :type, :email, :default )', $params );
+      			$qry->Exec();
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_conns (id_contact, id_connection, id_typeof_contact_connection) VALUES( :id_contact, :id_conns, 1)', array(':id_contact' => $id_contactt , ':id_conns' => $cont->max + 1));
+                        $qry->Exec();
+                     }
+                   }
+               if (isset($tels)) 
+                   {
+                      foreach( $tels AS $tel ) {
+				  $params[':type'] = $tel->GetParameterValue('TYPE');
+     				  if ( ! isset($params[':type']) ) $params[':type'] = 'voice';
+     				  $params[':tel'] = $tel->Value();
+     				  $params[':property'] = $tel->Render();
+                                  $qry = new AwlQuery( "select max(id_connection) from phpgw_cc_connections");
+                                  $qry->Exec();
+                                  $cont=$qry->fetch();
+                                  if ( strtoupper($params[':type']) == 'WORK')
+                                    {$params[':type'] = "Trabalho";
+                                       $params[':default'] = true;
+                                    }
+                                  else if ( $params[':type'] == 'pref')
+                                    {$params[':type'] = "Celular";
+                                       $params[':default'] = true;
+                                    }
+
+                                  else if ( strtoupper($params[':type']) == 'HOME')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Casa";
+                                    }
+                                  else if ( strtoupper($params[':type']) == 'CELL')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Celular";
+                                    }
+                                  else if ( strtoupper($params[':type']) == 'FAX')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Fax";
+                                    }
+                                   $params[':id_conns'] = $cont->max + 1;
+                                   dbg_log_array( "PUTTTTTT", 'TELSSSSSSSSS', $params, true );
+                                   $qry = new AwlQuery( 'INSERT INTO phpgw_cc_connections (id_connection, connection_name, connection_value,  connection_is_default) VALUES( :id_conns, :type, :tel, :default )', $params );
+                                   $qry->Exec();
+                                   $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_conns (id_contact, id_connection, id_typeof_contact_connection) VALUES( :id_contact, :id_conns, 2)', array(':id_contact' => $id_contactt , ':id_conns' =>  $cont->max + 1));
+                                   $qry->Exec();
+
+                      }
+  
+                      
+                   }  
+               
+            }
+            else
+             {
+                if (isset($emails))
+                   {
+                     foreach( $emails AS $email ) {
+                        $params[':type'] = $email->GetParameterValue('TYPE');
+                        $params[':email'] = $email->Value();
+                        $qry = new AwlQuery( "select max(id_connection) from phpgw_cc_connections");
+                        $qry->Exec();
+                        $cont=$qry->fetch();
+                        if ( strtoupper($params[':type']) == 'WORK')
+                           {$params[':type'] = "Principal";
+                            $params[':default'] = true;
+                           }
+                        else if ( $params[':type'] == 'pref')
+                           {$params[':type'] = "Principal";
+                            $params[':default'] = true;
+                           }
+
+                        else
+                          {
+                           $params[':default'] = false;
+                           $params[':type'] = "Alternativo";
+                           }
+                        $params[':id_conns'] = $cont->max + 1;
+                        dbg_log_array( "PUTTTTTT", 'EMAILSSSS INSERT', $params, true );
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_connections (id_connection, connection_name, connection_value,  connection_is_default) VALUES( :id_conns, :type, :email, :default )', $params );
+                        $qry->Exec();
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_conns (id_contact, id_connection, id_typeof_contact_connection) VALUES( :id_contact, :id_conns, 1)', array(':id_contact' => $id_contactt , ':id_conns' =>  $cont->max + 1));
+                        $qry->Exec();
+                     }
+                   }
+               if (isset($tels))
+                   {
+                      foreach( $tels AS $tel ) {
+                                  $params[':type'] = $tel->GetParameterValue('TYPE');
+                                  if ( ! isset($params[':type']) ) $params[':type'] = 'voice';
+                                  $params[':tel'] = $tel->Value();
+                                  $params[':property'] = $tel->Render();
+                                  $qry = new AwlQuery( "select max(id_connection) from phpgw_cc_connections");
+                                  $qry->Exec();
+                                  $cont=$qry->fetch();
+                                   if ( strtoupper($params[':type']) == 'WORK')
+                                    {$params[':type'] = "Trabalho";
+                                       $params[':default'] = true;
+                                    }
+                                  else if ( $params[':type'] == 'pref')
+                                    {$params[':type'] = "Celular";
+                                       $params[':default'] = true;
+                                    }
+
+                                  else if ( strtoupper($params[':type']) == 'HOME')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Casa";
+                                    }
+                                  else if ( strtoupper($params[':type']) == 'CELL')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Celular";
+                                    }
+                                  else if ( strtoupper($params[':type']) == 'FAX')
+                                   {
+                                     $params[':default'] = false;
+                                     $params[':type'] = "Fax";
+                                    }
+                                   $params[':id_conns'] = $cont->max + 1;
+                                   dbg_log_array( "PUTTTTTT", 'TELSSSSS INSERT', $params, true );
+                                   $qry = new AwlQuery( 'INSERT INTO phpgw_cc_connections (id_connection, connection_name, connection_value,  connection_is_default) VALUES( :id_conns, :type, :tel, :default )', $params );
+                                   $qry->Exec();
+                                   $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_conns (id_contact, id_connection, id_typeof_contact_connection) VALUES( :id_contact, :id_conns, 2)', array(':id_contact' => $id_contactt , ':id_conns' =>  $cont->max + 1));
+                                   $qry->Exec();
+
+                      }
+
+                                  
+                
+                  }
+          } 
+    } 
+   $addresses = $this->GetProperties('ADR');
+   if ( isset($addresses))
+    {
+        $qry = new AwlQuery("select id_address from phpgw_cc_contact_addrs where id_contact= :id", array(':id' => $id_contactt));
+        if ($qry->Exec() && $qry->rows() > 0)                                                                                        
+           {                                                                                                                         
+             while( $idconns = $qry->Fetch() ) {                                                                                     
+                   $qryy = new AwlQuery("DELETE from phpgw_cc_contact_addrs where id_contact= :id AND id_address = :id_connection", array(':id' => $id_contactt ,':id_connection' => $idconns->id_address));
+                   $qryy->Exec();                                                                                                                                                                                 
+                   $qryy = new AwlQuery("DELETE from phpgw_cc_addresses  where id_address = :id_connection", array(':id_connection' => $idconns->id_address));                                            
+                   $qryy->Exec();                                                                                                                                                                                 
+                                                                                                                                                                                                                 
+              }                                                                                                                                                                                                  
+                foreach( $addresses AS $adr ) {                                                                                                                                                              
+			 $params[':type'] = $adr->GetParameterValue('TYPE');
+      			 $address = explode(';',$adr->Value());
+
+      			 // We use @ to suppress the warnings here, because the NULL in the database suits us well.
+     			 @$params[':box_no']   = $address[0];
+      			 @$params[':unit_no']  = $address[1];
+     			 @$params[':street_address'] = $address[2];
+      			 @$params[':locality'] = $address[3];
+      			 @$params[':region']   = $address[4];
+      			 @$params[':postcode'] = $address[5];
+     			 @$params[':country']  = substr($address[6],0,2);
+                         $qry = new AwlQuery( "select max(id_address) from phpgw_cc_addresses");                                                                                                             
+                         $qry->Exec();                                                                                                                                                                            
+                         $cont=$qry->fetch();                                                                                                                                                                     
+                         if ( strtoupper($params[':type']) == 'WORK')                                                                                                                                                         
+                           {$params[':type'] = 2;                                                                                                                                                      
+                            $params[':default'] = true;                                                                                                                                                          
+                           }                                                                                                                                                                                     
+                         else                                                                                                                                                                                     
+                          {                                                                                                                                                                                      
+                           $params[':default'] = false;                                                                                                                                                          
+                           $params[':type'] = 1;                                                                                                                                                     
+                           }                                                                                                                                                                                     
+                        $params[':id_conns'] = $cont->max + 1;
+                        $qry = new AwlQuery( "select id_city from phpgw_cc_city where city_name = :idcity", array(':idcity' => $address[3]));
+                        if ( @$qry->Exec() && $qry->rows() > 0 )
+                           { $city=$qry->fetch(); 
+                             $params[':locality'] = $city->id_city;      
+                           } 
+                        else
+                            $params[':locality'] = "";
+                        $qry = new AwlQuery( "select id_state from phpgw_cc_state where state_name = :idstate OR state_symbol = :idstate ", array(':idstate' => $address[4]));
+                        if ( @$qry->Exec() && $qry->rows() > 0 )
+                           { $state=$qry->fetch(); 
+                             $params[':region'] = $state->id_state;      
+                           } 
+                         else
+                            $params[':region'] = "";
+                        dbg_log_array( "PUTTTTTT", 'ADDRESSSSSSS', $params, true );
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_addresses ( id_address,id_city,id_state,id_country,address1,postal_code,address_is_default) VALUES( :id_conns, :locality, :region, :country, :street_address, :postcode, :default   )', $params );
+                        $qry->Exec();                                                                                                                                                                              
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_addrs (id_contact, id_address, id_typeof_contact_address ) VALUES( :id_contact, :id_conns, :type)', array(':id_contact' => $id_contactt , ':id_conns' => $cont->max + 1,':type' =>  $params[':type']));                                                                                                                                                                                                                                    
+                        $qry->Exec();                                                                                                                                                                                                        
+                     }                                                                                                                                                                                                                       
+                       
+            }          
+          else       
+            {         
+                    foreach( $addresses AS $adr ) {                                                                                                                                                                  
+                         $params[':type'] = $adr->GetParameterValue('TYPE');                                                                                                                                     
+                         $address = explode(';',$adr->Value());                                                                                                                                                  
+
+                         // We use @ to suppress the warnings here, because the NULL in the database suits us well.
+                         @$params[':box_no']   = $address[0];                                                      
+                         @$params[':unit_no']  = $address[1];
+                         @$params[':street_address'] = $address[2];
+                         @$params[':locality'] = $address[3];
+                         @$params[':region']   = $address[4];
+                         @$params[':postcode'] = $address[5];
+                         @$params[':country']  = substr($address[6],0,2);
+                         $qry = new AwlQuery( "select max(id_address) from phpgw_cc_addresses");
+                         $qry->Exec();
+                         $cont=$qry->fetch();
+                         if ( strtoupper($params[':type']) == 'WORK')
+                           {$params[':type'] = 2;
+                            $params[':default'] = true;
+                           }
+                         else
+                          {
+                           $params[':default'] = false;
+                           $params[':type'] = 1;
+                           }
+                        $params[':id_conns'] = $cont->max + 1;
+                        $qry = new AwlQuery( "select id_city from phpgw_cc_city where city_name = :idcity", array(':idcity' => $address[3]));
+                        if ( @$qry->Exec() && $qry->rows() > 0 )
+                           { $city=$qry->fetch();
+                             $params[':locality'] = $city->id_city;
+                           }
+                        else
+                            $params[':locality'] = "";
+                        $qry = new AwlQuery( "select id_state from phpgw_cc_state where state_name = :idstate OR state_symbol = :idstate ", array(':idstate' => $address[4]));
+                        if ( @$qry->Exec() && $qry->rows() > 0 )
+                           { $state=$qry->fetch();
+                             $params[':region'] = $state->id_state;
+                           }
+                         else
+                            $params[':region'] = "";
+                         dbg_log_array( "PUTTTTTT", 'ADDRESSSSSSSSSS INSERT', $params, true ); 
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_addresses ( id_address,id_city,id_state,id_country,address1,postal_code,address_is_default) VALUES( :id_conns, :locality, :region, :country, :street_address, :postcode, :default   )', $params );
+                        $qry->Exec();
+                        $qry = new AwlQuery( 'INSERT INTO phpgw_cc_contact_addrs (id_contact, id_address, id_typeof_contact_address ) VALUES( :id_contact, :id_conns, :type)', array(':id_contact' => $id_contactt , ':id_conns' => $cont->max + 1,':type' =>  $params[':type']));                                                                                                                                                                                                        
+                        $qry->Exec();
+                   }
+            }
+      }
+
+   return $response_code;  
+  }
+  
+
+
+}
Index: contrib/davical/inc/caldav-REPORT-cardquery.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-cardquery.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-cardquery.php	(revision 3733)
@@ -0,0 +1,233 @@
+<?php
+
+require_once('vcard.php');
+
+$address_data_properties = array();
+function get_address_properties( $address_data_xml ) {
+  global $address_data_properties;
+  $expansion = $address_data_xml->GetElements();
+  foreach( $expansion AS $k => $v ) {
+    $address_data_properties[strtoupper($v->GetAttribute('name'))] = true;
+  }
+}
+
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$qry_content = $xmltree->GetContent('urn:ietf:params:xml:ns:carddav:addressbook-query');
+$proptype = $qry_content[0]->GetTag();
+$properties = array();
+switch( $proptype ) {
+  case 'DAV::prop':
+    $qry_props = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/'.$proptype.'/*');
+    foreach( $qry_content[0]->GetElements() AS $k => $v ) {
+      $propertyname = preg_replace( '/^.*:/', '', $v->GetTag() );
+      $properties[$propertyname] = 1;
+      if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+    }
+    break;
+
+  case 'DAV::allprop':
+    $properties['allprop'] = 1;
+    if ( $qry_content[1]->GetTag() == 'DAV::include' ) {
+      foreach( $qry_content[1]->GetElements() AS $k => $v ) {
+        $include_properties[] = $v->GetTag(); /** $include_properties is referenced in DAVResource where allprop is expanded */
+        if ( $v->GetTag() == 'urn:ietf:params:xml:ns:carddav:address-data' ) get_address_properties($v);
+      }
+    }
+    break;
+
+  default:
+    $propertyname = preg_replace( '/^.*:/', '', $proptype );
+    $properties[$propertyname] = 1;
+}
+
+/**
+ * There can only be *one* FILTER element.
+ */
+$qry_filters = $xmltree->GetPath('/urn:ietf:params:xml:ns:carddav:addressbook-query/urn:ietf:params:xml:ns:carddav:filter/*');
+if ( count($qry_filters) != 1 ) {
+/*  $qry_filters = $qry_filters[0];  // There can only be one FILTER element
+}
+else { */
+  $qry_filters = false;
+}
+
+
+/**
+* While we can construct our SQL to apply some filters in the query, other filters
+* need to be checked against the retrieved record.  This is for handling those ones.
+*
+* @param array $filter An array of XMLElement which is the filter definition
+* @param string $item The database row retrieved for this calendar item
+*
+* @return boolean True if the check succeeded, false otherwise.
+*/
+function apply_filter( $filters, $item ) {
+  global $session, $c, $request;
+
+  if ( count($filters) == 0 ) return true;
+
+  dbg_error_log("cardquery","Applying filter for item '%s'", $item->dav_name );
+  $vcard = new vComponent( $item->caldav_data );
+  return $vcard->TestFilter($filters);
+}
+
+
+/**
+ * Process a filter fragment returning an SQL fragment
+ */
+$need_post_filter = false;
+$matchnum = 0;
+function SqlFilterCardDAV( $filter, $components, $property = null, $parameter = null ) {
+  global $need_post_filter, $target_collection, $matchnum;
+  $sql = "";
+  $params = array();
+  if ( !is_array($filter) ) {
+    dbg_error_log( "cardquery", "Filter is of type '%s', but should be an array of XML Tags.", gettype($filter) );
+  }
+
+  foreach( $filter AS $k => $v ) {
+    $tag = $v->GetTag();
+    dbg_error_log("cardquery", "Processing $tag into SQL - %d, '%s', %d\n", count($components), $property, isset($parameter) );
+
+    $not_defined = "";
+    switch( $tag ) {
+      case 'urn:ietf:params:xml:ns:carddav:text-match':
+        $search = $v->GetContent();
+        $negate = $v->GetAttribute("negate-condition");
+        $collation = $v->GetAttribute("collation");
+        switch( strtolower($collation) ) {
+          case 'i;octet':
+            $comparison = 'LIKE';
+            break;
+          case 'i;ascii-casemap':
+          case 'i;unicode-casemap':
+          default:
+            $comparison = 'ILIKE';
+            break;
+        }
+        $pname = ':text_match_'.$matchnum++;
+        $params[$pname] = '%'.$search.'%';
+        dbg_error_log("cardquery", " text-match: (%s%s %s '%s') ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+                                          $property, $comparison, $params[$pname] );
+        $sql .= sprintf( "AND (%s%s %s $pname) ", (isset($negate) && strtolower($negate) == "yes" ? "NOT ": ""),
+                                          $property, $comparison );
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:prop-filter':
+        $propertyname = $v->GetAttribute("name");
+        switch( $propertyname ) {
+          case 'VERSION':
+          case 'UID':
+          case 'NICKNAME':
+          case 'FN':
+          case 'NOTE':
+          case 'ORG':
+          case 'URL':
+          case 'FBURL':
+          case 'CALADRURI':
+          case 'CALURI':
+            $property = strtolower($propertyname);
+            break;
+
+          case 'N':
+            $property = 'name';
+            break;
+
+          default:
+            $need_post_filter = true;
+            dbg_error_log("cardquery", "Could not handle 'prop-filter' on %s in SQL", $propertyname );
+            continue;
+        }
+        $subfilter = $v->GetContent();
+        $success = SqlFilterCardDAV( $subfilter, $components, $property, $parameter );
+        if ( $success === false ) continue; else {
+          $sql .= $success['sql'];
+          $params = array_merge( $params, $success['params'] );
+        }
+        break;
+
+      case 'urn:ietf:params:xml:ns:carddav:param-filter':
+        $need_post_filter = true;
+        return false; /** Figure out how to handle PARAM-FILTER conditions in the SQL */
+        /*
+        $parameter = $v->GetAttribute("name");
+        $subfilter = $v->GetContent();
+        $success = SqlFilterCardDAV( $subfilter, $components, $property, $parameter );
+        if ( $success === false ) continue; else {
+          $sql .= $success['sql'];
+          $params = array_merge( $params, $success['params'] );
+        }
+        break;
+        */
+
+      default:
+        dbg_error_log("cardquery", "Could not handle unknown tag '%s' in calendar query report", $tag );
+        break;
+    }
+  }
+  dbg_error_log("cardquery", "Generated SQL was '%s'", $sql );
+  return array( 'sql' => $sql, 'params' => $params );
+}
+
+
+/**
+* Something that we can handle, at least roughly correctly.
+*/
+
+$responses = array();
+$target_collection = new DAVResource($request->path);
+$bound_from = $target_collection->bound_from();
+if ( !$target_collection->Exists() ) {
+  $request->DoResponse( 404 );
+}
+if ( ! ($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection()) ) {
+  $request->DoResponse( 403, translate('The addressbook-query report must be run against an addressbook collection') );
+}
+
+/**
+* @todo Once we are past DB version 1.2.1 we can change this query more radically.  The best performance to
+* date seems to be:
+*   SELECT caldav_data.*,address_item.* FROM collection JOIN address_item USING (collection_id,user_no)
+*         JOIN caldav_data USING (dav_id) WHERE collection.dav_name = '/user1/home/'
+*              AND caldav_data.caldav_type = 'VEVENT' ORDER BY caldav_data.user_no, caldav_data.dav_name;
+*/
+
+$params = array();
+$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
+if ( is_array($qry_filters) ) {
+  dbg_log_array( 'cardquery', 'qry_filters', $qry_filters, true );
+  $components = array();
+  $filter_fragment =  SqlFilterCardDAV( $qry_filters, $components );
+  if ( $filter_fragment !== false ) {
+    $where .= ' '.$filter_fragment['sql'];
+    $params = $filter_fragment['params'];
+  }
+}
+else {
+  dbg_error_log( 'cardquery', 'No query filters' );
+}
+
+$sql = 'SELECT * FROM caldav_data INNER JOIN addressbook_resource USING(dav_id)'. $where;
+if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY dav_id";
+$qry = new AwlQuery( $sql, $params );
+if ( $qry->Exec("cardquery",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+  while( $address_object = $qry->Fetch() ) {
+    if ( !$need_post_filter || apply_filter( $qry_filters, $address_object ) ) {
+      if ( $bound_from != $target_collection->dav_name() ) {
+        $address_object->dav_name = str_replace( $bound_from, $target_collection->dav_name(), $address_object->dav_name);
+      }
+      if ( count($address_data_properties) > 0 ) {
+        $vcard = new VCard($address_object->caldav_data);
+        $vcard->MaskProperties($address_data_properties);
+        $address_object->caldav_data = $vcard->Render();
+      }
+      $responses[] = component_to_xml( $properties, $address_object );
+    }
+  }
+}
+$multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/caldav-DELTICKET.php
===================================================================
--- contrib/davical/inc/caldav-DELTICKET.php	(revision 3733)
+++ contrib/davical/inc/caldav-DELTICKET.php	(revision 3733)
@@ -0,0 +1,45 @@
+<?php
+/**
+* CalDAV Server - handle DELTICKET method in line with defunct proposed RFC
+*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+*
+* Why are we using a defunct RFC?  Well, we want to support some kind of system
+* for providing a URI to people to give out for granting privileged access
+* without requiring logins.  Using a defunct proposed spec seems better than
+* inventing our own.  As well as Xythos, Cosmo follows this specification,
+* with some documented variations, which we will also follow.  In particular
+* we use the xmlns="http://www.xythos.com/namespaces/StorageServer" rather
+* than the DAV: namespace.
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log('DELTICKET', 'method handler');
+require_once('DAVResource.php');
+
+if ( ! $request->HavePrivilegeTo('DAV::unbind') && $request->ticket->owner() != $session->principal_id ) {
+  $request->NeedPrivilege('DAV::unbind');
+}
+
+if ( ! isset($request->ticket) ) {
+  if ( isset($_GET['ticket']) || isset($_SERVER['HTTP_TICKET']) ) {
+    $r = new DAVResource($request->path);
+    if ( ! $r->Exists() ) {
+      $request->PreconditionFailed(404,'not-found');
+    }
+    else {
+      $request->PreconditionFailed(412,'ticket-does-not-exist','The specified ticket does not exist');
+    }
+  }
+  else
+    $request->MalformedRequest('No ticket specified');
+}
+
+$qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=:ticket_id', array( ':ticket_id'   => $request->ticket->id() ) );
+if ( $qry->Exec('DELTICKET', __LINE__, __FILE__) ) {
+  $request->DoResponse( 204 );
+}
+$request->DoResponse( 500 );
Index: contrib/davical/inc/pubsub.php
===================================================================
--- contrib/davical/inc/pubsub.php	(revision 3733)
+++ contrib/davical/inc/pubsub.php	(revision 3733)
@@ -0,0 +1,723 @@
+<?
+
+/**********************************************************************
+ *                       XMPP PubSub for DAViCal
+ *           Copyright 2009 Rob Ostensen rob@boxacle.net
+ *         Licenced http://gnu.org/copyleft/gpl.html GNU GPL v2
+ *
+ *********************************************************************/
+
+
+class xmpp
+{
+	private $connection,$streamTagBegin,$streamTagEnd,$mesgcount=0,$ready,$moredata=false,$username,$stream,$xmlparser,$xquery;
+	private $namespaces = Array();
+	private $recvTags = Array();
+	private $recvHandlers = Array();
+	private $sendHandlers = Array();
+	private $finishedCommands = Array();
+	private $sendQueue = Array();
+	private $recvQueue = '';
+	private $pubsubNext = Array();
+	private $depth = 0,$processDepth=0;
+	public $server,$port,$jid,$resource,$password,$tls,$idle,$status,$pubsubLayout='hometree';
+
+	// constructor
+	public function __construct ( )
+	{
+		$this->status = "online";
+		$this->setupXmlParser ();
+	}
+
+	// figure out what server to connect to and make the connection, returns true if successful, false otherwise
+	private function connect ()
+	{
+		if ( ! isset ( $this->jid ) )
+			return $this->connection = false;
+		if ( ! isset ( $this->idle ) )
+			$this->idle = true;
+		if ( ! isset ( $this->resource ) )
+			$this->resource = 'caldav' . getmypid();
+		if ( ! preg_match ( '/^\//', $this->resource ) )
+			$this->resource = '/' . $this->resource;
+		$temp = explode ( '@', $this->jid );
+		$this->username = $temp[0];
+		if ( ! isset ( $this->server ) )
+		{
+			$this->server = $temp[1];
+		}
+		$r = dns_get_record("_xmpp-client._tcp.". $this->server , DNS_SRV);
+		if ( 0 < count ( $r ) )
+		{
+			$this->original_server   = $this->server;
+			$this->server            = $r[0]['target'];
+			$this->original_port     = $this->port;
+			$this->port              = $r[0]['port'];
+		}
+		if ( ! isset ( $this->port ) )
+			$this->port = 5222;
+		if ( 'ssl' == $this->tls || ( ! isset ( $this->tls ) && 5223 == $this->port ) )
+			$url = 'ssl://' . $this->server;
+		elseif ( 'tls' == $this->tls || ( ! isset ( $this->tls ) && 5222 == $this->port ) )
+			$url = 'tcp://' . $this->server;
+		else
+			$url = 'tcp://' . $this->server;
+		if ( isset ( $this->original_server ) )
+			$this->server = $this->original_server;
+		$this->connection = stream_socket_client ( $url . ':' . $this->port, $errno, $errstring, 10, STREAM_CLIENT_ASYNC_CONNECT );
+		if ( false === $this->connection )
+		{
+			if ( $errno != 0 )
+				$log = $errstring;
+			return false;
+		}
+		$this->initializeQueue ( );
+		socket_set_blocking ( $this->connection, false );
+		return true;
+	}
+
+	// handles the features tag, mostly related to authentication
+	private function handleFeatures ( &$node )
+	{
+		if ( $this->debug ) $this->log ( 'handling features' );
+		if ( 'STARTTLS' == $node->firstChild->nodeName )
+		{
+			$this->sendQueue[] = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
+			return;
+		}
+		$elements = $this->query ( '*/MECHANISM', $node );
+		if ( ! is_null ( $elements )  && $elements !== false )
+		{
+			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching MECHANISM nodes ");
+			$auth_mech = array ();
+			foreach ( $elements as $e )
+				$auth_mech[] = $e->nodeValue;
+			if ( in_array ( 'PLAIN', $auth_mech ) )
+				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" . base64_encode("\x00" . preg_replace('/@.*$/','',$this->jid) . "\x00" . $this->password) . "</auth>";
+			elseif ( in_array ( 'DIGEST-MD5', $auth_mech ) ) // this code and the associated function are UNTESTED
+			{
+				$this->sendQueue[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
+				$this->recvHandlers['challenge'] = 'digestAuth' ;
+			}
+			$this->recvHandlers['success'] = 'handleSuccess' ;
+		}
+		$elements = $this->query ( '*/BIND', $node );
+		if ( ! is_null ( $elements ) && $elements->length > 0 )
+		{
+			// failure if we don't hit this, not sure how we can detect that failure yet.
+			if ( $this->debug ) $this->log ( " found " . $elements->length . " matching BIND nodes ");
+			$this->ready = true;
+		}
+	}
+
+	// handle proceed tag/enable tls
+	private function enableTLS ( $node )
+	{
+		stream_set_blocking ( $this->connection, true );
+		stream_socket_enable_crypto ( $this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
+		stream_set_blocking ( $this->connection, false );
+		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+	}
+
+	// do digest auth
+	private function digestAuth ( &$node )
+	{
+		// this code is based solely on the description found @ http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-crash.html
+		// UNTESTED please shoot me an email if you get this to work !!
+		$contents = $node->nodeValue;
+		if ( ! is_null ( $elements ) )
+		{
+			$challlenge = array ();
+			$parts = explode ( ',', base64_decode ( $contents ) );
+			foreach ( $parts as $text )
+			{
+				$temp = explode ( '=', $text );
+				$challenge[$temp[0]] = $temp[1];
+			}
+			if ( $challenge['realm'] == $this->server ) // might fail need to handle a response with multiple realms
+			{
+				$cnonce =  md5((mt_rand() * time() / mt_rand())+$challenge['nonce']);
+				$X =  md5 ( preg_replace('/@.*$/','',$this->jid) . ':' . $this->server . ':' .  $this->password, true );
+				$HA1 = md5 ( $X . ':' . $challenge['nonce'] . ':' . $cnonce . ':' . $this->jid . $this->resource );
+				$HA2 = md5 ( "AUTHENTICATE:xmpp/" . $this->server );
+				$resp = md5 ( $HA1 . ':' . $challenge['nonce'] . ':00000001:' . $cnonce . ':auth' . $HA2 );
+				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" .
+					base64_encode("username=\"" . preg_replace('/@.*$/','',$this->jid) . "\"," .
+					"realm=\"" . $this->server . "\",nonce=\"" . $challenge['nonce'] . "\",cnonce=\"". $cnonce . "\"," .
+					"nc=00000001,qop=auth,digest-uri=\"xmpp/" . $this->server . "\",response=" . $resp .
+					",charset=utf-8,authzid=\"". $this->jid . $this->resource . "\"" ) . "</response>" // note the PID component to the resource, just incase
+					;
+			}
+			elseif ( $challenge['rspauth'] )
+				$this->sendQueue[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>" ;
+		}
+	}
+
+	// do basic setup to get the connection logged in and going
+	private function handleSuccess ( &$node )
+	{
+		$this->loggedIn = true;
+		$this->sendQueue[] = "<"."?xml version=\"1.0\"?".">\n\n<stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+		$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='1'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>" . preg_replace('/^\//','',$this->resource) . "</resource></bind></iq>";
+		$this->recvHandlers['stream:error'] = 'handleError' ;
+		$this->recvHandlers['iq'] = 'handleIq' ;
+		$this->recvHandlers['message'] = 'handleMessage' ;
+		$this->mesgcount = 1;
+	}
+
+	// do something with standard iq messages also does some standard setup like setting presence
+	private function handleIq ( &$node )
+	{
+		if ( $this->debug ) $this->log ( "Handle IQ id:" . $node->getAttribute ( 'id' ) . ' type:' . $node->getAttribute ( 'type' ) . "");
+		if ( $node->getAttribute ( 'type' ) == 'result' || $node->getAttribute ( 'type' ) == 'error' )
+		{
+			$commandId = $node->getAttribute ( 'id' );
+			$this->command[$commandId] = true;
+			if ( isset ( $this->handleCommand[$commandId] ) )
+			{
+				$this->finishedCommands[$commandId] = true;
+				if ( method_exists ( $this, $this->handleCommand[$commandId] ) )
+					call_user_func_array ( array ( $this, $this->handleCommand[$commandId] ),  array ( &$node ) );
+				else
+					call_user_func_array ( $this->handleCommand[$commandId], array ( &$node ) );
+			}
+		}
+		if ( $node->getAttribute ( 'id' ) == $this->mesgcount && $this->mesgcount < 3 )
+		{
+			$this->sendQueue[] = "<iq xmlns='jabber:client' type='set' id='" . ( $this->mesgcount++ ) . "'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>";
+			$this->sendQueue[] = "<iq xmlns='jabber:client' type='get' id='" . ( $this->mesgcount++ ) . "'><query xmlns='jabber:iq:roster' /></iq>";
+		}
+		if ( $node->getAttribute ( 'id' ) == '2' && $this->command['2'] == true )
+		{
+			$this->nextreply = $this->mesgcount++;
+			$this->sendQueue[] = "<presence id='" . $this->nextreply . "' ><status>" . $this->status . '</status></presence>';
+			$this->ready = true;
+		}
+	}
+
+	// do something with standard messages
+	private function handleMessage ( &$node )
+	{
+		if ( $node->getAttribute ( 'type' ) == 'chat' )
+		{
+			$this->command[$node->getAttribute ( 'id' )] = true;
+			$elements = $this->query ( '//*/body', $node );
+			if ( 0 < $elements->length )
+			{
+				$temp = $elements->items(0);
+				if ( $this->debug ) $this->log ( "received message " . $temp->nodeValue );
+			}
+		}
+	}
+
+	// handle stream errors by logging a message and closing the connection
+	private function handleError ( &$node )
+	{
+		$this->log ( 'STREAM ERROR OCCURRED! XMPP closing connection, this is probably a bug' );
+		$this->idle = false;
+		$this->close ();
+	}
+
+	//  disco a pubsub collection
+	private function disco ( $to, $type, $name )
+	{
+		$msg = $this->mesgcount++;
+		$send  = "<iq type='get' from='" . $this->jid . $this->resource . "' to='$to' id='" . $msg . "'>";
+		$send .= "	<query xmlns='http://jabber.org/protocol/disco#$type' node='$name'/>";
+		$send .= "</iq>";
+		$this->handleCommand[$msg] = 'discoResult';
+		$this->sendQueue[] = $send;
+		$this->go();
+	}
+
+	//  result from disco
+	private function discoResult ( &$node )
+	{
+		if ( $this->debug ) $this->log ( $node->ownerDocument->saveXML($node) );
+		$id = $node->getAttribute ( 'id' );
+		$identity = $this->query ( '*/IDENTITY', $node );
+		if ( @is_array ( $this->pubsub [ 'create' ] [ $id ] ) && 0 == $identity->length )
+		{
+			$this->pubsubCreateNode( $this->pubsub [ 'create' ] [ $id ] [ 0 ],
+				$this->pubsub [ 'create' ] [ $id ] [ 1 ],
+				$this->pubsub [ 'create' ] [ $id ] [ 2 ],
+				$this->pubsub [ 'create' ] [ $id ] [ 3 ] );
+		}
+	}
+
+	//  send a message to a jid
+	public function sendMessage ( $to, $message )
+	{
+		$msg = $this->mesgcount++;
+		$out .= "<message id='" . $msg . "' from='" . $this->jid . $this->resource . "' to='" . $to. "' >";
+		$out .= "<body>" . $message . "</body></message>";
+		$this->sendQueue[] = $out;
+		$this->go();
+	}
+
+	//	get a pubsub collection/leaf node and create if it doesn't exist
+	public function pubsubCreate ( $to, $type, $name, $configure = null )
+	{
+		if ( 1 > strlen ( $to ) )
+			$to = 'pubsub.' . $this->server;
+		if ( 1 > strlen ( $type ) )
+			$type = 'set';
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$this->pubsub['create'][$this->mesgcount+1] = array ( $to, $type, $name, $configure );
+		$this->disco ( $to, 'info', $node );
+	}
+
+	//	create a pubsub collection/leaf node
+	private function pubsubCreateNode ( $to, $type, $name, $configure = null )
+	{
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$msg = $this->mesgcount++;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub" ><create node="' . $node . '"/>';
+		if ( $configure )
+			$out .= '<configure>' . $configure .' </configure>';
+		else
+			$out .= '<configure/>';
+		$out .= '</pubsub>';
+		$out .= '</iq>';
+		$this->sendQueue[] = $out;
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->go();
+	}
+
+	//	configure a pubsub collection or leaf
+	public function pubsubConfig ( $to, $type, $name )
+	{
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$msg = $this->mesgcount++;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="get" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner" ><configure node="' . $node . '"/>';
+		$out .= '</pubsub>';
+		$out .= '</iq>';
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->sendQueue[] = $out;
+		$this->go();
+	}
+
+	//	delete a pubsub collection or leaf
+	public function pubsubDelete ( $to, $type, $name )
+	{
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$msg = $this->mesgcount++;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><delete node="' . $node . '"/>';
+		$out .= '</pubsub>';
+		$out .= '</iq>';
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->sendQueue[] = $out;
+		$this->go();
+	}
+
+	//	purge a pubsub collection or leaf
+	public function pubsubPurge ( $to, $type, $name )
+	{
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$msg = $this->mesgcount++;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"><purge node="' . $node . '"/>';
+		$out .= '</pubsub>';
+		$out .= '</iq>';
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->sendQueue[] = $out;
+		$this->go();
+	}
+
+	//	publish to a pubsub collection
+	public function pubsubPublish ( $to, $type, $name, $contents, $nodeId )
+	{
+		if ( 1 > strlen ( $to ) )
+			$to = 'pubsub.' . $this->server;
+		if ( 1 > strlen ( $type ) )
+			$type = 'set';
+		if ( 1 > strlen ( $nodeId ) )
+			$id = "id='$nodeId'";
+		else
+			$id = '';
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$msg = $this->mesgcount++;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="' . $node . '">';
+		if ( preg_match ( '/^<item/', $contents ) )
+			$out .= $contents;
+		else
+			$out .= '<item ' . $id . '>' . $contents . '</item>';
+		$out .= '</publish></pubsub>';
+		$out .= '</iq>';
+		$this->sendQueue[] = $out;
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->go();
+	}
+
+	// subscribe to a pubsub collection,leaf or item
+	private function pubsubSubscribe ( $to, $type, $name )
+	{
+		$msg = $this->mesgcount++;
+		if ( 'hometree' == $this->pubsubLayout )
+			$node = '/home/' . $this->server . '/' . $this->username . $name;
+		else
+			$node= $name;
+		$out = '<iq from="' . $this->jid . $this->resource . '" to="' . $to . '" type="' . $type . '" id="' . $msg . '">';
+		$out .= '<pubsub xmlns="http://jabber.org/protocol/pubsub"><subscribe node="' . $name . '" jid="' . $this->jid . $this->resource . '"/>';
+		$out .= '</pubsub>';
+		$out .= '</iq>';
+		$this->sendQueue[] = $out;
+		$this->handleCommand[ $msg ] = 'pubsubResult';
+		$this->go();
+	}
+
+	private function pubsubResult ( &$node )
+	{
+		if ( $this->debug ) $this->log ( "pubsub RESULT   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+		if ( $node->getAttribute ( 'type' ) == 'error' )
+		{
+			$errnode = $this->query ( 'ERROR', $node );
+			if ( $errnode->length > 0 && (  '403' == $errnode->item( 0 )->getAttribute ( 'code' ) || '500' == $errnode->item( 0 )->getAttribute ( 'code' ) ) )
+			{
+				if ( 'CREATE' == $node->firstChild->firstChild->tagName )
+				{
+					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+					if ( $this->debug ) $this->log ( "403 error during CREATE for node '" . $pubnode . "' ");
+					$name =  preg_replace ( '/^.*?\/' . $this->username . '\//','', $pubnode );
+					$newnode = '';
+					if ( ! in_array ( 'create', $this->pubsubNext ) )
+					{
+						$a = array ( );
+						foreach ( explode ( '/', $name ) as $v )
+						{
+							$newnode .= '/' . $v;
+							$a[] = array (
+								'call' => 'create',
+								'to' => $node->getAttribute ( 'from' ),
+								'name' => $newnode );
+						}
+						foreach ( array_reverse ( $a ) as $v )
+							array_unshift ( $this->pubsubNext, $v );
+						$this->pubsubDoNext ( );
+					}
+				}
+			}
+			elseif ( $errnode->length > 0 && '404' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+			{
+				if ( 'PUBLISH' == $node->firstChild->firstChild->tagName )
+				{
+					$pubnode = $node->firstChild->firstChild->getAttribute ( 'node' );
+					if ( $this->debug ) $this->log ( "404 error during PUBLISH for node '" . $pubnode . "' ");
+					$publish = $this->query ( '//*/PUBLISH', $node );
+					$this->pubsubNext[] = array (
+						'call' => 'publish',
+						'to' => $node->getAttribute ( 'from' ),
+						'name' => preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) ,
+						'contents' => $publish->item( 0 )->firstChild->nodeValue );
+					if ( $this->debug ) $this->log ( "attempting to create node '" . $this->pubsubNext[0]['name'] . "' ");
+					$this->pubsubCreateNode ( $node->getAttribute ( 'from' ) ,'set', preg_replace ( '/^.*?\/' . $this->username . '/','', $pubnode ) );
+				}
+			}
+			elseif ( $errnode->length > 0 && '409' == $errnode->item( 0 )->getAttribute ( 'code' ) )
+			{
+				if ( 'CANCEL' == $errnode->item( 0 )->firstChild->tagName || 'CONFLICT' == $errnode->item( 0 )->firstChild->tagName )
+					$this->pubsubDoNext ( );
+			}
+		}
+		elseif ( 0 < count ( $this->pubsubNext ) )
+			$this->pubsubDoNext ( );
+	}
+
+	// do next pubsub request
+	private function pubsubDoNext ( )
+	{
+		if ( 0 < count ( $this->pubsubNext ) )
+		{
+			$pub = array_shift ( $this->pubsubNext );
+			if ( 'publish' == $pub['call'] )
+			{
+				if ( $this->debug ) $this->log ( "attempting to publish to node '" . $pub['name'] . "'  contents '" . $pub['contents'] . "'");
+				$this->pubsubPublish ( $pub[$to], 'set', $pub['name'], $pub['contents'] );
+			}
+			if ( 'create' == $pub['call'] )
+			{
+				if ( $this->debug ) $this->log ( "attempting to create node '" . $pub['name'] . "' ");
+				$this->pubsubCreateNode ( $pub[$to], 'set', $pub['name'] );
+			}
+		}
+	}
+
+	// do basic setup to get the connection logged in and going
+	private function initializeQueue ( )
+	{
+		$this->loggedIn = false;
+		$this->streamTagBegin = '<'."?xml version='1.0'?"."><stream:stream to='" . $this->server . "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' version='1.0'>";
+		$this->streamTagEnd = '</stream:stream>';
+		$this->sendQueue[] = $this->streamTagBegin;
+		$this->recvHandlers['stream:features'] = 'handleFeatures' ;
+		$this->recvHandlers['features'] = 'handleFeatures' ;
+		$this->recvHandlers['proceed'] = 'enableTLS' ;
+	}
+
+	// send data out the socket
+	private function send ( $data )
+	{
+		$len = strlen ( $data );
+		if ( $this->debug ) $this->log ( "SEND: $data");
+		if ( false !== $this->connection )
+		{
+			if ( fwrite ( $this->connection, $data, $len) === $len )
+				return true;
+			else
+				return false;
+		}
+		return false;
+	}
+
+	// receive any data waiting on the socket
+	private function recv ()
+	{
+		if ( false !== $this->connection )
+		{
+			$data = '';
+			$data = fgets ( $this->connection, 4096 );
+			if ( 4094 < strlen ( $data ) )
+			{
+				$count = 0;
+				while ( 0 != strlen ( $moredata = fgets ( $this->connection, 1024 ) ) && 20 < $count++ )
+				{
+					$data .= $moredata;
+					usleep ( 10 );
+				}
+			}
+			if ( 0 < strlen ( $data ) )
+			{
+				$data = preg_replace ( '/^<\?xml version=\'1.0\'\?'.'>/', '', $data );
+				$this->stream .= $data;
+				if ( $this->debug ) $this->log ( "RECV: $data" );
+				return $data;
+			}
+			else
+				return false;
+		}
+		return false;
+	}
+
+	private function go ()
+	{
+		$this->recvQueue = implode ( '', $this->sendQueue );
+		$count = 0;
+		$this->moredata = false;
+		while ( false !== $this->connection )
+		{
+			if ( 0 < count ( $this->sendQueue ) )
+			{
+				$count = 0;
+				while ( $data = array_shift ( $this->sendQueue ) )
+					$this->send ( $data );
+			}
+			$data = $this->recv ( );
+			xml_parse ( $this->xmlparser, $data, false );
+			while ( $rnode = array_shift ( $this->recvTags ) )
+			{
+				$rname = strtolower ( $rnode->localName );
+				if ( $this->debug ) $this->log ( " processing $rname ");
+				if ( isset ( $this->recvHandlers[$rname] ) ) //&& is_callable ( $this->recvHandlers[$r->name] ) )
+				{
+					if ( method_exists ( $this, $this->recvHandlers[$rname] ) )
+						call_user_func_array ( array ( $this, $this->recvHandlers[$rname] ),  array ( &$rnode ) );
+					else
+						call_user_func_array ( $this->recvHandlers[$rname], array ( &$rnode ) );
+				}
+			}
+			$count++;
+			if ( $count > 20 )
+			{
+				if ( $this->idle === true )
+				{
+					$count = 0;
+					usleep ( 200 );
+				}
+				else
+				{
+					if ( $this->ready == true && count ( $this->handleCommand ) <= count ( $this->command ) )
+					{
+						$count = 0;
+						return ;
+					}
+				}
+			}
+			else
+				usleep ( 20 );
+		}
+	}
+
+
+	// xml parser start element
+	private function startElement ( $parser, $name, $attrs )
+	{
+		$this->depth++;
+		$namespace = '';
+
+		if ( 'STREAM:STREAM' == $name )
+			$this->processDepth++;
+		foreach ( $attrs as $k => $v )
+			if ( preg_match ( '/^xmlns:?(.*)/i', $k, $matches ) )
+			{
+				if ( strlen ( $matches[1] ) > 0 && ! isset ( $this->namespaces [ $matches[1] ] ) )
+				{
+					$this->xquery->registerNamespace ( $matches[1], $v );
+					$this->namespaces [ $matches[1] ] = $v;
+					$namespace = $v;
+					if ( $this->debug ) $this->log ( " adding namespace $k => $v ");
+				}
+			}
+		if ( $namespace != '' )
+			$node = $this->doc->createElementNS ( $namespace, $name );
+		else
+			$node = $this->doc->createElement ( $name );
+		foreach ( $attrs as $k => $v )
+			$node->setAttribute ( strtolower ( $k ), $v );
+		$this->currentXMLNode = $this->currentXMLNode->appendChild ( $node );
+	}
+
+	// xml parser start element
+	private function endElement ( $parser, $name )
+	{
+		$this->depth--;
+		//if ( $this->debug ) $this->log ( "depth: " . $this->depth . " processDepth: " . $this->processDepth . " ");
+		if ( $this->depth == $this->processDepth || 'STREAM:STREAM' == $name || 'STREAM:FEATURES' == $name || 'PROCEED' == $name )
+		{
+			if ( $this->debug ) $this->log ( " adding $name to tags to process ");
+			array_push ( $this->recvTags, $this->currentXMLNode ); // replace with tag
+		}
+		$this->currentXMLNode = $this->currentXMLNode->parentNode;
+	}
+
+	// xml parser start element
+	private function parseData ( $parser, $text )
+	{
+		$this->currentXMLNode->appendChild ( $this->doc->createTextNode ( $text ) );
+	}
+
+	// xml parser start element
+	private function setupXmlParser ( )
+	{
+		$this->depth = 0;
+		$this->xmlparser = xml_parser_create ( );
+		xml_set_object ( $this->xmlparser, $this );
+		xml_set_element_handler ( $this->xmlparser, 'startElement', 'endElement' );
+		xml_set_character_data_handler ( $this->xmlparser, 'parseData' );
+		$this->doc = new DOMDocument ();
+		$this->xquery = new DOMXpath ( $this->doc );
+		$this->xquery->registerNamespace ( 'stream', 'http://etherx.jabber.org/streams' );
+		$this->currentXMLNode = $this->doc->appendChild ( $this->doc->createElement ( 'start' ) );
+	}
+
+	// xml XPath query
+	private function query ( $expression, &$node = '' )
+	{
+		if ( '' == $node )
+			return $this->xquery->query	( $expression );
+		else
+			return $this->xquery->query	( $expression , $node );
+	}
+
+
+	// open xmpp connection, will accept jid and password
+	public function open ( $jid = null, $password = null)
+	{
+		if ( null != $jid )
+			$this->jid = $jid;
+		if ( null != $password )
+			$this->password = $password;
+		$this->ready = false;
+		if ( false !== $this->connect () )
+		{
+			sleep(2);
+			$this->go ();
+		}
+		else
+			return false;
+		return true;
+	}
+
+	public function close ()
+	{
+		if ( false !== $this->connection )
+		{
+			$this->send ( '</stream:stream>');
+			fclose ( $this->connection );
+			$this->connection = false;
+		}
+	}
+
+	// add a send or recv handler, direction = [ send | recv ], command = command to handle, handler = function ref
+	public function addHandler ( $direction, $command, $handler )
+	{
+		if ( 'send' == $direction )
+			$this->sendHandler[$command] = $handler;
+		if ( 'recv' == $direction )
+			$this->recvHandler[$command] = $handler;
+	}
+
+	// handle logging
+	private function log ( $message )
+	{
+		error_log ( 'XMPP: ' . $message );
+		//echo  'XMPP: ' . $message . "\n";
+	}
+}
+
+
+/**
+	* * Log the action
+	* * @param string $action_type INSERT / UPDATE or DELETE
+	* * @param string $uid The UID of the modified item
+	* * @param integer $user_no The user owning the containing collection.
+	* * @param integer $collection_id The ID of the containing collection.
+	* * @param string $dav_name The DAV path of the item, relative to the DAViCal base path
+	* */
+function log_caldav_action( $action_type, $uid, $user_no, $collection_id, $dav_name )
+{
+  global $c;
+	$t = new xmpp();
+	$t->tls =  'none';
+	$t->idle =  false;
+	if ( 1 == $c->dbg["ALL"] || 1 == $c->dbg["push"] )
+		$t->debug = true ;
+	else
+		$t->debug = false ;
+	// for now use a flat node tree layout
+	$t->pubsubLayout = 'flat';
+	// get the principal_id for this collection, that's what the client will be looking for
+	$qry = new AwlQuery ('SELECT principal_id FROM principal JOIN collection USING (user_no) WHERE collection_id= :collection_id',
+                           array( ':collection_id' => $collection_id ) );
+	$qry->Exec('pubsub');
+	$row = $qry->Fetch();
+
+	$t->open ( $c->notifications_server['jid'], $c->notifications_server['password'] );
+	if ( isset ( $c->notifications_server['debug_jid'] ) )
+		$t->sendMessage ( $c->notifications_server['debug_jid'], "ACTION: $action_type\nUSER: $user_no\nDAV NAME: $dav_name\nPRINCIPAL ID: " . $row->principal_id );
+	$t->pubsubCreate ( '', 'set', '/davical-' . $row->principal_id, '<x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#node_config</value></field><field var="pubsub#access_model"><value>open</value></field><field var=\'pubsub#type\'>plist-apple<value></value></field></x>' );
+	$t->pubsubPublish ( '', 'set', '/davical-' . $row->principal_id , '<item xmlns="plist-apple" id="' . $uid . ' " ><plistfrag xmlns="plist-apple"><key>davical</key><string>' . $uid . '</string></plistfrag></item>', $uid );
+	$t->close();
+}
+
Index: contrib/davical/inc/caldav-REPORT-expand-property.php
===================================================================
--- contrib/davical/inc/caldav-REPORT-expand-property.php	(revision 3733)
+++ contrib/davical/inc/caldav-REPORT-expand-property.php	(revision 3733)
@@ -0,0 +1,101 @@
+<?php
+
+/**
+* Given a <response><href>...</href><propstat><prop><someprop/></prop><status>HTTP/1.1 200 OK</status></propstat>...</response>
+* pull out the content of <someprop>content</someprop> and check to see if it has any href elements.  If it *does* then
+* recurse into them, looking for the next deeper nesting of properties.
+*/
+function get_href_containers( &$multistatus_response ) {
+  $propstat_set = $multistatus_response->GetElements('propstat');
+  $propstat_200 = null;
+  foreach( $propstat_set AS $k => $v ) {
+    $status = $v->GetElements('status');
+    if ( preg_match( '{^HTTP/\S+\s+200}', $status[0]->GetContent() ) ) {
+      $propstat_200 = $v;
+      break;
+    }
+  }
+  if ( isset($propstat_200) ) {
+    $props = $propstat_200->GetElements('prop');
+    $properties = array();
+    foreach( $props AS $k => $p ) {
+      $properties = array_merge($properties,$p->GetElements());
+    }
+    $href_containers = array();
+    foreach( $properties AS $k => $property ) {
+      if ( !is_object($property) ) continue;
+//      dbg_error_log('REPORT',' get_href_containers: Checking property "%s" for hrefs.', $property->GetNSTag() );
+      $hrefs = $property->GetElements('href');
+      if ( count($hrefs) > 0 ) {
+        $href_containers[] = $property;
+      }
+    }
+    if ( count($href_containers) > 0 ) {
+      return $href_containers;
+    }
+  }
+  return null;
+}
+
+
+/**
+* Expand the properties, recursing only once
+*/
+function expand_properties( $urls, $ptree, &$reply, $recurse_again = true ) {
+  if ( !is_array($urls) )  $urls = array($urls);
+  if ( !is_array($ptree) ) $ptree = array($ptree);
+
+  $responses = array();
+  foreach( $urls AS $m => $url ) {
+    $resource = new DAVResource($url);
+    $props = array();
+    $subtrees = array();
+    foreach( $ptree AS $n => $property ) {
+      if ( ! is_object($property) ) continue;
+      $pname = $property->GetAttribute('name');
+      $pns = $property->GetAttribute('namespace');
+      if ( !isset($pns) || $pns == '' ) $pns = 'DAV:';  // Not sure if this is the correct way to default this.
+      $pname = $pns .':'. $pname;
+      $props[] = $pname;
+      $subtrees[$pname] = $property->GetElements();
+    }
+    $part_response = $resource->RenderAsXML( $props, $reply );
+    if ( isset($part_response) ) {
+      if ( $recurse_again ) {
+        $href_containers = get_href_containers($part_response);
+        if ( isset($href_containers) ) {
+          foreach( $href_containers AS $h => $property ) {
+            $hrefs = $property->GetElements();
+            $pname = $property->GetTag();
+            $pns = $property->GetAttribute('xmlns');
+            if ( !isset($pns) || $pns == '' ) $pns = 'DAV:';  // Not sure if this is the correct way to default this.
+            $pname = $pns .':'. $pname;
+            $paths = array();
+            foreach( $hrefs AS $k => $v ) {
+              $content = $v->GetContent();
+              $paths[] = $content;
+            }
+//            dbg_error_log('REPORT',' Found property "%s" contains hrefs "%s"', $pname, implode(', ',$paths) );
+            $property->SetContent( expand_properties($paths, $subtrees[$pname], $reply, false) );
+          }
+        }
+//      else {
+//        dbg_error_log('REPORT',' No href containers in response to "%s"', implode(', ', $props ) );
+//      }
+      }
+      $responses[] = $part_response;
+    }
+  }
+
+  return $responses;
+}
+
+
+/**
+ * Build the array of properties to include in the report output
+ */
+$property_tree = $xmltree->GetPath('/DAV::expand-property/DAV::property');
+
+$multistatus = new XMLElement( "multistatus", expand_properties( $request->path, $property_tree, $reply), $reply->GetXmlNsArray() );
+
+$request->XMLResponse( 207, $multistatus );
Index: contrib/davical/inc/page-header.php
===================================================================
--- contrib/davical/inc/page-header.php	(revision 3733)
+++ contrib/davical/inc/page-header.php	(revision 3733)
@@ -0,0 +1,86 @@
+<?php
+
+if ( !isset($c->page_title) ) {
+  $c->page_title = translate('DAViCal CalDAV Server');
+}
+
+function make_help_link($matches)
+{
+  global $c;
+
+  // as usual: $matches[0] is the complete match
+  // $matches[1] the match for the first subpattern
+  // enclosed in '##...##' and so on
+  // Use like: $s = preg_replace_callback('/##([^#]+)##', 'make_help_link', $s);
+//  $help_topic = preg_replace( '/^##(.+)##$/', '$1', $matches[1]);
+  $help_topic = $matches[1];
+  $display_url = $help_topic;
+  if ( $GLOBALS['session']->AllowedTo('Admin') || $GLOBALS['session']->AllowedTo('Support') ) {
+    if ( strlen($display_url) > 30 ) {
+      $display_url = substr( $display_url, 0, 28 ) . '...' ;
+    }
+  }
+  else {
+    $display_url = 'help';
+  }
+  return ' <a class="help" href="'.$c->base_url.'/help.php?h='.$help_topic.'" title="'.translate('Show help on').' &39;'.$help_topic.'&39;" target="_new">['.$display_url.']</a> ';
+}
+
+
+if ( !function_exists('send_page_header') ) {
+function send_page_header() {
+  global $session, $c, $main_menu, $related_menu;
+
+  header( 'Content-type: text/html; charset="utf-8"' );
+
+  echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+  echo <<<EOHDR
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+<title>$c->page_title</title>
+
+EOHDR;
+
+  foreach ( $c->stylesheets AS $stylesheet ) {
+    echo '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'">';
+  }
+  if ( isset($c->local_styles) ) {
+    // Always load local styles last, so they can override prior ones...
+    foreach ( $c->local_styles AS $stylesheet ) {
+      echo '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'">';
+    }
+  }
+
+  if ( isset($c->print_styles) ) {
+    // Finally, load print styles last, so they can override all of the above...
+    foreach ( $c->print_styles AS $stylesheet ) {
+      echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$stylesheet\" media=\"print\">\n";
+    }
+  }
+
+  echo "</head>\n<body>\n";
+  echo "<div id=\"pageheader\">\n";
+
+  if ( isset($main_menu) ) echo $main_menu->RenderAsCSS();
+  if ( isset($related_menu) && $related_menu->Size() > 0 ) {
+    echo $related_menu->Render( true );
+  }
+
+  echo "</div>\n";
+
+  if ( isset($c->messages) && is_array($c->messages) && count($c->messages) > 0 ) {
+    echo "<div id=\"messages\"><ul class=\"messages\">\n";
+    foreach( $c->messages AS $i => $msg ) {
+      // ##HelpTextKey## gets converted to a "/help.phph=HelpTextKey" link
+      $msg = preg_replace_callback("/##([^#]+)##/", "make_help_link", translate($msg));
+      echo "<li class=\"messages\">$msg</li>\n";
+    }
+    echo "</ul>\n</div>\n";
+  }
+
+}
+}
+
+send_page_header();
+
Index: contrib/davical/inc/caldav-MKTICKET.php
===================================================================
--- contrib/davical/inc/caldav-MKTICKET.php	(revision 3733)
+++ contrib/davical/inc/caldav-MKTICKET.php	(revision 3733)
@@ -0,0 +1,115 @@
+<?php
+/**
+* CalDAV Server - handle MKTICKET method in line with defunct proposed RFC
+*   from:  http://tools.ietf.org/html/draft-ito-dav-ticket-00
+*
+* Why are we using a defunct RFC?  Well, we want to support some kind of system
+* for providing a URI to people to give out for granting privileged access
+* without requiring logins.  Using a defunct proposed spec seems better than
+* inventing our own.  As well as Xythos, Cosmo follows this specification,
+* with some documented variations, which we will also follow.  In particular
+* we use the xmlns="http://www.xythos.com/namespaces/StorageServer" rather
+* than the DAV: namespace.
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd - http://www.morphoss.com/
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log('MKTICKET', 'method handler');
+require_once('DAVResource.php');
+
+$request->NeedPrivilege('DAV::bind');
+
+require_once('XMLDocument.php');
+$reply = new XMLDocument(array( 'DAV:' => '', 'http://www.xythos.com/namespaces/StorageServer' => 'T' ));
+
+$target = new DAVResource( $request->path );
+if ( ! $target->Exists() ) {
+  $request->XMLResponse( 404, new XMLElement( 'error', new XMLElement('resource-must-not-be-null'), $reply->GetXmlNsArray() ) );
+}
+
+if ( ! isset($request->xml_tags) ) {
+  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+}
+
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+if ( $xmltree->GetTag() != 'http://www.xythos.com/namespaces/StorageServer:ticketinfo' &&
+     $xmltree->GetTag() != 'DAV::ticketinfo' ) {
+  $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('invalid-xml-for-request'), $reply->GetXmlNsArray() ) );
+}
+
+$ticket_timeout = 'Seconds-3600';
+$ticket_privs_array = array('read-free-busy');
+foreach( $xmltree->GetContent() AS $k => $v ) {
+  // <!ELEMENT ticketinfo (id?, owner?, timeout, visits, privilege)>
+  switch( $v->GetTag() ) {
+    case 'DAV::timeout':
+    case 'http://www.xythos.com/namespaces/StorageServer:timeout':
+      $ticket_timeout = $v->GetContent();
+      break;
+
+    case 'DAV::privilege':
+    case 'http://www.xythos.com/namespaces/StorageServer:privilege':
+      $ticket_privs_array = $v->GetElements(); // Ensure we always get an array back
+      $ticket_privileges = 0;
+      foreach( $ticket_privs_array AS $k1 => $v1 ) {
+        $ticket_privileges |= privilege_to_bits( $v1->GetTag() );
+      }
+      if ( $ticket_privileges & privilege_to_bits('write') )          $ticket_privileges |= privilege_to_bits( 'read' );
+      if ( $ticket_privileges & privilege_to_bits('read') )           $ticket_privileges |= privilege_to_bits( array('read-free-busy', 'read-current-user-privilege-set') );
+      if ( $ticket_privileges & privilege_to_bits('read-free-busy') ) $ticket_privileges |= privilege_to_bits( 'schedule-query-freebusy');
+      break;
+  }
+}
+
+if ( $ticket_timeout == 'infinity' ) {
+  $sql_timeout = null;
+}
+else if ( preg_match( '{^([a-z]+)-(\d+)$}i', $ticket_timeout, $matches ) ) {
+  /** It isn't specified, but timeout seems to be 'unit-number' like 'Seconds-3600', so we make it '3600 Seconds' which PostgreSQL understands */
+  $sql_timeout = $matches[2] . ' ' . $matches[1];
+}
+else {
+  $sql_timeout = $ticket_timeout;
+}
+
+$collection_id = $target->GetProperty('collection_id');
+$resource_id   = $target->GetProperty('dav_id');
+
+$i = 0;
+do {
+  $ticket_id = substr( str_replace('/', '', str_replace('+', '',base64_encode(sha1(date('r') .rand(0,2100000000) . microtime(true),true)))), 7, 8);
+  $qry = new AwlQuery(
+    'INSERT INTO access_ticket ( ticket_id, dav_owner_id, privileges, target_collection_id, target_resource_id, expires )
+                VALUES( :ticket_id, :owner, :privs::INT::BIT(24), :collection, :resource, (current_timestamp + :expires::interval) )',
+    array(
+      ':ticket_id'   => $ticket_id,
+      ':owner'       => $session->principal_id,
+      ':privs'       => $ticket_privileges,
+      ':collection'  => $collection_id,
+      ':resource'    => $resource_id,
+      ':expires'     => $sql_timeout,
+    )
+  );
+  $result = $qry->Exec('MKTICKET', __LINE__, __FILE__);
+} while( !$result && $i++ < 2 );
+
+$privs = new XMLElement('privilege');
+foreach( bits_to_privilege($ticket_privileges) AS $k => $v ) {
+  $reply->NSElement($privs, $v);
+}
+
+$ticketinfo = new XMLElement( 'T:ticketinfo', array(
+      new XMLElement( 'T:id', $ticket_id),
+      new XMLElement( 'owner', $reply->href( ConstructURL('/'.$session->username.'/') ) ),
+      $privs,
+      new XMLElement( 'T:timeout', $ticket_timeout),
+      new XMLElement( 'T:visits', 'infinity')
+  )
+);
+
+$prop = new XMLElement( "prop", new XMLElement('T:ticketdiscovery', $ticketinfo), $reply->GetXmlNsArray() );
+header('Ticket: '.$ticket_id);
+$request->XMLResponse( 200, $prop );
Index: contrib/davical/inc/HTTPAuthSession.php
===================================================================
--- contrib/davical/inc/HTTPAuthSession.php	(revision 3733)
+++ contrib/davical/inc/HTTPAuthSession.php	(revision 3733)
@@ -0,0 +1,299 @@
+<?php
+/**
+* A Class for handling HTTP Authentication
+*
+* @package davical
+* @subpackage HTTPAuthSession
+* @author Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst .Net Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+/**
+* A Class for handling a session using HTTP Basic Authentication
+*
+* @package davical
+*/
+class HTTPAuthSession {
+  /**#@+
+  * @access private
+  */
+
+  /**
+  * User ID number
+  * @var user_no int
+  */
+  public $user_no;
+
+  /**
+  * User e-mail
+  * @var email string
+  */
+  public $email;
+
+  /**
+  * User full name
+  * @var fullname string
+  */
+  public $fullname;
+
+  /**
+  * Group rights
+  * @var groups array
+  */
+  public $groups;
+  /**#@-*/
+
+  /**
+  * The constructor, which just calls the actual type configured
+  */
+  function HTTPAuthSession() {
+    global $c;
+
+    if ( isset($c->http_auth_mode) && $c->http_auth_mode == "Digest" ) {
+      $this->DigestAuthSession();
+    }
+    else {
+      $this->BasicAuthSession();
+    }
+  }
+
+  /**
+  * Authorisation failed, so we send some headers to say so.
+  *
+  * @param string $auth_header The WWW-Authenticate header details.
+  */
+  function AuthFailedResponse( $auth_header = "" ) {
+    global $c;
+    if ( $auth_header == "" ) {
+      $auth_header = sprintf( 'WWW-Authenticate: Basic realm="%s"', $c->system_name);
+    }
+
+    header('HTTP/1.1 401 Unauthorized', true, 401 );
+    header('Content-type: text/plain; ; charset="utf-8"' );
+    header( $auth_header );
+    echo 'Please log in for access to this system.';
+    dbg_error_log( "HTTPAuth", ":Session: User is not authorised" );
+    exit;
+  }
+
+
+  /**
+  * Handle Basic HTTP Authentication (not secure unless https)
+  */
+  function BasicAuthSession() {
+    global $c;
+
+    /**
+    * Get HTTP Auth to work with PHP+FastCGI
+    */
+    if (isset($_SERVER["AUTHORIZATION"]) && !empty($_SERVER["AUTHORIZATION"])) {
+      list ($type, $cred) = split (" ", $_SERVER['AUTHORIZATION']);
+      if ($type == 'Basic') {
+        list ($user, $pass) = explode (":", base64_decode($cred));
+        $_SERVER['PHP_AUTH_USER'] = $user;
+        $_SERVER['PHP_AUTH_PW'] = $pass;
+      }
+    }
+    else if ( isset($c->authenticate_hook['server_auth_type']) && $c->authenticate_hook['server_auth_type'] == $_SERVER['AUTH_TYPE']
+              && isset($_SERVER["REMOTE_USER"]) && !empty($_SERVER["REMOTE_USER"])) {
+      /**
+      * The authentication has happened in the server, and we should accept it.
+      * Perhaps this 'split' is not a good idea though.  People may want to use the
+      * full ID as the username.  A further option may be desirable.
+      *
+      */
+      $_SERVER['PHP_AUTH_USER'] = $_SERVER['REMOTE_USER'];
+      $_SERVER['PHP_AUTH_PW'] = 'Externally Authenticated';
+      if ( ! isset($c->authenticate_hook['call']) ) {
+        /**
+        * Since we still need to get the user's details from somewhere.  We change the default
+        * authentication hook to auth_external which simply retrieves a user row from the DB
+        * and does no password checking.
+        */
+        $c->authenticate_hook['call'] = 'auth_external';
+      }
+    }
+
+
+    /**
+    * Fall through to the normal PHP authentication variables.
+    */
+    if ( isset($_SERVER['PHP_AUTH_USER']) ) {
+      if ( $u = $this->CheckPassword( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
+        $this->AssignSessionDetails($u);
+        return;
+      }
+    }
+
+    if ( isset($c->allow_unauthenticated) && $c->allow_unauthenticated ) {
+      $this->user_no = -1;
+      $this->username = 'guest';
+      $this->fullname = 'Unauthenticated User';
+      $this->email = 'invalid';
+      return;
+    }
+
+    $this->AuthFailedResponse();
+    // Does not return
+  }
+
+
+  /**
+  * Handle Digest HTTP Authentication (no passwords were harmed in this transaction!)
+  *
+  * Note that this will not actually work, unless we can either:
+  *   (A) store the password plain text in the database
+  *   (B) store an md5( username || realm || password ) in the database
+  *
+  * The problem is that potentially means that the administrator can collect the sorts
+  * of things people use as passwords.  I believe this is quite a bad idea.  In scenario (B)
+  * while they cannot see the password itself, they can see a hash which only varies when
+  * the password varies, so can see when two users have the same password, or can use
+  * some of the reverse lookup sites to attempt to reverse the hash.  I think this is a
+  * less bad idea, but not ideal.  Probably better than running Basic auth of HTTP though!
+  */
+  function DigestAuthSession() {
+    global $c;
+
+    if ( ! empty($_SERVER['PHP_AUTH_DIGEST'])) {
+      // analyze the PHP_AUTH_DIGEST variable
+      if ( $data = $this->ParseDigestHeader($_SERVER['PHP_AUTH_DIGEST']) ) {
+        // generate the valid response
+        $user_password = "Don't be silly! Why would a user have a password like this!!?";
+        /**
+        * @todo At this point we need to query the database for something fitting
+        * either strategy (A) or (B) above, in order to set $user_password to
+        * something useful!
+        */
+        $A1 = md5($data['username'] . ':' . $c->system_name . ':' . $user_password);
+        $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
+        $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
+
+        if ( $data['response'] == $valid_response ) {
+          $this->AssignSessionDetails($u);
+          return;
+        }
+      }
+    }
+
+    $nonce = uniqid();
+    $opaque = md5($c->system_name);
+    $this->AuthFailedResponse( sprintf('WWW-Authenticate: Digest realm="%s", qop="auth,auth-int", nonce="%s", opaque="%s"', $c->system_name, $nonce, $opaque ) );
+  }
+
+
+  /**
+  * Parse the HTTP Digest Auth Header
+  *  - largely sourced from the PHP documentation
+  */
+  function ParseDigestHeader($auth_header) {
+    // protect against missing data
+    $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
+    $data = array();
+
+    preg_match_all('@(\w+)=(?:([\'"])([^\2]+)\2|([^\s,]+))@', $auth_header, $matches, PREG_SET_ORDER);
+
+    foreach ($matches as $m) {
+      $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+      unset($needed_parts[$m[1]]);
+    }
+
+    return $needed_parts ? false : $data;
+  }
+
+  /**
+  * CheckPassword does all of the password checking and
+  * returns a user record object, or false if it all ends in tears.
+  */
+  function CheckPassword( $username, $password ) {
+    global $c;
+
+    if ( isset($c->authenticate_hook) && isset($c->authenticate_hook['call']) && function_exists($c->authenticate_hook['call']) ) {
+      /**
+      * The authenticate hook needs to:
+      *   - Accept a username / password
+      *   - Confirm the username / password are correct
+      *   - Create (or update) a 'usr' record in our database
+      *   - Return the 'usr' record as an object
+      *   - Return === false when authentication fails
+      *
+      * It can expect that:
+      *   - Configuration data will be in $c->authenticate_hook['config'], which might be an array, or whatever is needed.
+      */
+      $hook_response = call_user_func( $c->authenticate_hook['call'], $username, $password );
+      /**
+       * make the authentication hook optional: if the flag is set, ignore a return value of 'false'
+       */
+      if (isset($c->authenticate_hook['optional']) && $c->authenticate_hook['optional']) {
+        if ($hook_response !== false) { return $hook_response; }
+      } else {
+        return $hook_response;
+      }
+    }
+
+    if ( $usr = getUserByName($username) ) {
+      dbg_error_log( "BasicAuth", ":CheckPassword: Name:%s, Pass:%s, File:%s, Active:%s", $username, $password, $usr->password, ($usr->active?'Yes':'No') );
+      if ( $usr->active && session_validate_password( $password, $usr->password ) ) {
+        return $usr;
+      }
+    }
+    return false;
+  }
+
+  /**
+  * Checks whether a user is allowed to do something.
+  *
+  * The check is performed to see if the user has that role.
+  *
+  * @param string $whatever The role we want to know if the user has.
+  * @return boolean Whether or not the user has the specified role.
+  */
+  function AllowedTo ( $whatever ) {
+    return ( isset($this->logged_in) && $this->logged_in && isset($this->roles[$whatever]) && $this->roles[$whatever] );
+  }
+
+
+  /**
+  * Internal function used to get the user's roles from the database.
+  */
+  function GetRoles () {
+    $this->roles = array();
+    $qry = new AwlQuery( 'SELECT role_name FROM role_member m join roles r ON r.role_no = m.role_no WHERE user_no = :user_no ',
+                                array( ':user_no' => $this->user_no) );
+    if ( $qry->Exec('BasicAuth') && $qry->rows() > 0 ) {
+      while( $role = $qry->Fetch() ) {
+        $this->roles[$role->role_name] = true;
+      }
+    }
+  }
+
+
+  /**
+  * Internal function used to assign the session details to a user's new session.
+  * @param object $u The user+session object we (probably) read from the database.
+  */
+  function AssignSessionDetails( $u ) {
+    if ( !isset($u->principal_id) ) {
+      // If they don't have a principal_id set then we should re-read from our local database
+      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+      if ( $qry->Exec() && $qry->rows() == 1 ) {
+        $u = $qry->Fetch();
+      }
+    }
+
+    // Assign each field in the selected record to the object
+    foreach( $u AS $k => $v ) {
+      $this->{$k} = $v;
+    }
+
+    $this->GetRoles();
+    $this->logged_in = true;
+    if ( function_exists("awl_set_locale") && isset($this->locale) && $this->locale != "" ) {
+      awl_set_locale($this->locale);
+    }
+  }
+
+
+}
+
Index: contrib/davical/inc/always.php.in
===================================================================
--- contrib/davical/inc/always.php.in	(revision 3733)
+++ contrib/davical/inc/always.php.in	(revision 3733)
@@ -0,0 +1,545 @@
+<?php
+/**
+* @package davical
+* @author Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+if ( preg_match('{/always.php$}', $_SERVER['SCRIPT_NAME'] ) ) header('Location: index.php');
+
+// Ensure the configuration starts out as an empty object.
+$c = (object) array();
+$c->script_start_time = microtime(true);
+
+// Ditto for a few other global things
+unset($session); unset($request); unset($dbconn); unset($_awl_dbconn); unset($include_properties);
+
+// An ultra-simple exception handler to catch errors that occur
+// before we get a more functional exception handler in place...
+function early_exception_handler($e) {
+  echo "Uncaught early exception: ", $e->getMessage(), "\nAt line ", $e->getLine(), " of ", $e->getFile(), "\n";
+
+  $trace = array_reverse($e->getTrace());
+  foreach( $trace AS $k => $v ) {
+    printf( "=====================================================\n%s[%d] %s%s%s()\n", $v['file'], $v['line'], (isset($v['class'])?$v['class']:''), (isset($v['type'])?$v['type']:''), (isset($v['function'])?$v['function']:'') );
+  }
+}
+set_exception_handler('early_exception_handler');
+
+// Default some of the configurable values
+$c->sysabbr     = 'davical';
+$c->admin_email = 'admin@davical.example.com';
+$c->system_name = 'DAViCal CalDAV Server';
+$c->domain_name = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+$c->save_time_zone_defs = true;
+$c->collections_always_exist = false;
+$c->allow_get_email_visibility = false;
+$c->permission_scan_depth = 2;
+$c->expand_pdo_parameters = true;
+$c->home_calendar_name = 'home';
+$c->enable_row_linking = true;
+$c->enable_scheduling = false;
+$c->http_auth_mode = 'Basic';
+// $c->default_locale = array('es_MX', 'es_AR', 'es', 'pt');  // An array of locales to try, or just a single locale
+// $c->local_tzid = 'Pacific/Auckland';  // Perhaps we should read from /etc/timezone - I wonder how standard that is?
+$c->default_locale = 'en';
+$c->locale_path = '../locale';
+$c->base_url = preg_replace('#/[^/]+\.php.*$#', '', $_SERVER['SCRIPT_NAME']);
+$c->base_directory = preg_replace('#/[^/]*$#', '', $_SERVER['DOCUMENT_ROOT']);
+$c->default_privileges = array('read-free-busy', 'schedule-deliver');
+
+$c->stylesheets = array( $c->base_url.'/davical.css' );
+$c->images      = $c->base_url . '/images';
+
+// Add a default for newly created users
+$c->template_usr = array( 'active' => true,
+                          'locale' => 'en_GB',
+                          'date_format_type' => 'E',
+                          'email_ok' => date('Y-m-d')
+                        );
+
+$c->hide_TODO = true;                      // VTODO only visible to collection owner
+$c->readonly_webdav_collections = true;    // WebDAV access is readonly
+
+// Kind of private configuration values
+$c->total_query_time = 0;
+
+$c->dbg = array();
+
+
+// Utilities
+if ( ! @include_once('AWLUtilities.php') ) {
+  $try_paths = array(
+        '../../awl/inc'
+      , '/usr/share/awl/inc'
+      , '/usr/local/share/awl/inc'
+  );
+  foreach( $try_paths AS $awl_include_path ) {
+    if ( @file_exists($awl_include_path.'/AWLUtilities.php') ) {
+      set_include_path( $awl_include_path. PATH_SEPARATOR. get_include_path());
+      break;
+    }
+  }
+  if ( ! @include_once('AWLUtilities.php') ) {
+    echo "Could not find the AWL libraries. Are they installed? Check your include_path in php.ini!\n";
+    exit;
+  }
+}
+
+// Ensure that ../inc is in our included paths as early as possible
+set_include_path( '../inc'. PATH_SEPARATOR. get_include_path());
+
+
+/** We actually discovered this and worked around it earlier, but we can't log it until the utilties are loaded */
+if ( !isset($_SERVER['SERVER_NAME']) ) {
+  @dbg_error_log( 'WARN', "Your webserver is not setting the SERVER_NAME parameter. You may need to set \$c->domain_name in your configuration.  Using IP address meanhwhile..." );
+}
+
+/**
+* Calculate the simplest form of reference to this page, excluding the PATH_INFO following the script name.
+*/
+$c->protocol_server_port = sprintf( '%s://%s%s',
+                 (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'? 'https' : 'http'),
+                 $_SERVER['SERVER_NAME'],
+                 (
+                   ( (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on') && $_SERVER['SERVER_PORT'] == 80 )
+                           || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' && $_SERVER['SERVER_PORT'] == 443 )
+                   ? ''
+                   : ':'.$_SERVER['SERVER_PORT']
+                 ) );
+$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);
+
+
+/**
+* We use @file_exists because things like open_basedir might noisily deny
+* access which could break DAViCal completely by causing output to start
+* too early.
+*/
+ob_start( );
+if ( @file_exists('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+  include('/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+}
+else if ( @file_exists('/etc/davical/config.php') ) {
+  include('/etc/davical/config.php');
+}
+else if ( @file_exists('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php') ) {
+  include('/usr/local/etc/davical/'.$_SERVER['SERVER_NAME'].'-conf.php');
+}
+else if ( @file_exists('/usr/local/etc/davical/config.php') ) {
+  include('/usr/local/etc/davical/config.php');
+}
+else if ( @file_exists('../config/config.php') ) {
+  include('../config/config.php');
+}
+else if ( @file_exists('config/config.php') ) {
+  include('config/config.php');
+}
+else {
+  include('davical_configuration_missing.php');
+  exit;
+}
+$config_warnings = trim(ob_get_contents());
+ob_end_clean();
+
+if ( !isset($c->page_title) ) $c->page_title = $c->system_name;
+
+if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) {
+  @dbg_error_log( 'LOG', '==========> Test case =%s=', $_SERVER['HTTP_X_DAVICAL_TESTCASE'] );
+}
+else if ( isset($c->dbg['script_start']) && $c->dbg['script_start'] ) {
+  // Only log this if more than a little debugging of some sort is turned on, somewhere
+  @dbg_error_log( 'LOG', '==========> method =%s= =%s= =%s= =%s= =%s=',
+         $_SERVER['REQUEST_METHOD'], $c->protocol_server_port_script, $_SERVER['PATH_INFO'], $c->base_url, $c->base_directory );
+}
+
+/**
+* Now that we have loaded the configuration file we can switch to a
+* default site locale.  This may be overridden by each user.
+*/
+putenv("LANG=". $c->default_locale);
+awl_set_locale($c->default_locale);
+init_gettext( 'davical', $c->locale_path );
+
+/**
+* Work out our version
+*
+*/
+$c->code_version = 0;
+$c->version_string = '0.9.8.3'; // The actual version # is replaced into that during the build /release process
+if ( isset($c->version_string) && preg_match( '/(\d+)\.(\d+)\.(\d+)(.*)/', $c->version_string, $matches) ) {
+  $c->code_major = $matches[1];
+  $c->code_minor = $matches[2];
+  $c->code_patch = $matches[3];
+  $c->code_version = (($c->code_major * 1000) + $c->code_minor).'.'.$c->code_patch;
+  dbg_error_log('caldav', 'Version (%d.%d.%d) == %s', $c->code_major, $c->code_minor, $c->code_patch, $c->code_version);
+  header( sprintf('Server: %d.%d', $c->code_major, $c->code_minor) );
+}
+
+/**
+* Force the domain name to what was in the configuration file
+*/
+$_SERVER['SERVER_NAME'] = $c->domain_name;
+
+require_once('AwlQuery.php');
+
+$c->want_dbversion = array(1,2,8);
+$c->schema_version = 0;
+$qry = new AwlQuery( 'SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1;' );
+if ( $qry->Exec('always',__LINE__,__FILE__) && $row = $qry->Fetch() ) {
+  $c->schema_version = doubleval( sprintf( '%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch) );
+  $c->wanted_version = doubleval( sprintf( '%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]) );
+  $c->schema_major = $row->schema_major;
+  $c->schema_minor = $row->schema_minor;
+  $c->schema_patch = $row->schema_patch;
+  if ( $c->schema_version < $c->wanted_version ) {
+    $c->messages[] = sprintf( 'Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d',
+             $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
+  }
+  if ( isset($_SERVER['HTTP_X_DAVICAL_TESTCASE']) ) $qry->QDo('SET TIMEZONE TO \'Pacific/Auckland\'');
+}
+
+
+$_known_users_name = array();
+$_known_users_id   = array();
+$_known_users_pid  = array();
+
+function _davical_get_principal_query_cached( $where, $parameter ) {
+  global $c, $session, $_known_users_name, $_known_users_id, $_known_users_pid;
+
+  $sql = 'SELECT *, to_char(updated at time zone \'GMT\',\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS modified, principal.*, ';
+  if ( isset($session->principal_id) ) {
+    $sql .= 'pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) AS privileges ';
+    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+  }
+  else {
+    $sql .= '0::BIT(24) AS privileges ';
+    $params = array( );
+  }
+  $sql .= 'FROM usr LEFT JOIN principal USING(user_no) WHERE '. $where;
+  $params[':param'] = $parameter;
+
+  $qry = new AwlQuery( $sql, $params );
+  if ( $qry->Exec('always',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
+    if ( isset($session->principal_id) ) {
+      $_known_users_name[$row->username]      = $row;
+      $_known_users_id[$row->user_no]         = $row;
+      $_known_users_pid[$row->principal_id]   = $row;
+    }
+    return $row;
+  }
+
+  return false;
+}
+
+/**
+* Return a user record identified by a username, caching it for any subsequent lookup
+* @param string $username The username of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByName( $username, $use_cache = true ) {
+  global $_known_users_name;
+
+  if ( $use_cache && isset( $_known_users_name[$username] ) ) return $_known_users_name[$username];
+  return _davical_get_principal_query_cached( 'lower(username) = lower(:param)', $username );
+}
+
+
+/**
+* Return a user record identified by e-mail address, caching it for any subsequent lookup
+* @param string $email The email address of the user record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByEMail( $email, $use_cache = true ) {
+  global $_known_users_name;
+
+  if ( $use_cache ) {
+    /** We don't actually maintain a cache-access by e-mail, since it's rare */
+    foreach( $_known_users_name AS $k => $v ) {
+      if ( strtolower($email) == strtolower($v->email) ) return $v;
+    }
+  }
+  return _davical_get_principal_query_cached( 'lower(email) = lower(:param)', $email );
+}
+
+
+/**
+* Return a user record identified by a user_no, caching it for any subsequent lookup
+* @param int $user_no The ID of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getUserByID( $user_no, $use_cache = true ) {
+  global $c, $session, $_known_users_id;
+
+  if ( $use_cache && isset( $_known_users_id[$user_no] ) ) return $_known_users_id[$user_no];
+  return _davical_get_principal_query_cached( 'user_no = :param', $user_no );
+}
+
+
+/**
+* Return a user record identified by a user_no, caching it for any subsequent lookup
+* @param int $user_no The ID of the record to retrieve
+* @param boolean $use_cache Whether or not to use the cache (default: yes)
+*/
+function getPrincipalByID( $principal_id, $use_cache = true ) {
+  global $c, $session, $_known_users_pid;
+
+  if ( $use_cache && isset( $_known_users_pid[$principal_id] ) ) return $_known_users_pid[$principal_id];
+  return _davical_get_principal_query_cached( 'principal_id = :param', $principal_id );
+}
+
+
+/**
+ * Return the HTTP status code description for a given code. Hopefully
+ * this is an efficient way to code this.
+ * @return string The text for a give HTTP status code, in english
+ */
+function getStatusMessage($status) {
+  switch( $status ) {
+    case 100:  $ans = 'Continue';                             break;
+    case 101:  $ans = 'Switching Protocols';                  break;
+    case 200:  $ans = 'OK';                                   break;
+    case 201:  $ans = 'Created';                              break;
+    case 202:  $ans = 'Accepted';                             break;
+    case 203:  $ans = 'Non-Authoritative Information';        break;
+    case 204:  $ans = 'No Content';                           break;
+    case 205:  $ans = 'Reset Content';                        break;
+    case 206:  $ans = 'Partial Content';                      break;
+    case 207:  $ans = 'Multi-Status';                         break;
+    case 300:  $ans = 'Multiple Choices';                     break;
+    case 301:  $ans = 'Moved Permanently';                    break;
+    case 302:  $ans = 'Found';                                break;
+    case 303:  $ans = 'See Other';                            break;
+    case 304:  $ans = 'Not Modified';                         break;
+    case 305:  $ans = 'Use Proxy';                            break;
+    case 307:  $ans = 'Temporary Redirect';                   break;
+    case 400:  $ans = 'Bad Request';                          break;
+    case 401:  $ans = 'Unauthorized';                         break;
+    case 402:  $ans = 'Payment Required';                     break;
+    case 403:  $ans = 'Forbidden';                            break;
+    case 404:  $ans = 'Not Found';                            break;
+    case 405:  $ans = 'Method Not Allowed';                   break;
+    case 406:  $ans = 'Not Acceptable';                       break;
+    case 407:  $ans = 'Proxy Authentication Required';        break;
+    case 408:  $ans = 'Request Timeout';                      break;
+    case 409:  $ans = 'Conflict';                             break;
+    case 410:  $ans = 'Gone';                                 break;
+    case 411:  $ans = 'Length Required';                      break;
+    case 412:  $ans = 'Precondition Failed';                  break;
+    case 413:  $ans = 'Request Entity Too Large';             break;
+    case 414:  $ans = 'Request-URI Too Long';                 break;
+    case 415:  $ans = 'Unsupported Media Type';               break;
+    case 416:  $ans = 'Requested Range Not Satisfiable';      break;
+    case 417:  $ans = 'Expectation Failed';                   break;
+    case 422:  $ans = 'Unprocessable Entity';                 break;
+    case 423:  $ans = 'Locked';                               break;
+    case 424:  $ans = 'Failed Dependency';                    break;
+    case 500:  $ans = 'Internal Server Error';                break;
+    case 501:  $ans = 'Not Implemented';                      break;
+    case 502:  $ans = 'Bad Gateway';                          break;
+    case 503:  $ans = 'Service Unavailable';                  break;
+    case 504:  $ans = 'Gateway Timeout';                      break;
+    case 505:  $ans = 'HTTP Version Not Supported';           break;
+    default:   $ans = 'Unknown HTTP Status Code '.$status;
+  }
+  return $ans;
+}
+
+
+/**
+* Construct a URL from the supplied dav_name.  The URL will be urlencoded,
+* except for any '/' characters in it.
+* @param string $partial_path  The part of the path after the script name
+*/
+function ConstructURL( $partial_path, $force_script = false ) {
+  global $c;
+
+  $partial_path = rawurlencode($partial_path);
+  $partial_path = str_replace( '%2F', '/', $partial_path);
+
+  if ( ! isset($c->_url_script_path) ) {
+    $c->_url_script_path = (preg_match('#/$#', $c->protocol_server_port_script) ? 'caldav.php' : '');
+    $c->_url_script_path = $c->protocol_server_port_script . $c->_url_script_path;
+  }
+
+  $url = $c->_url_script_path;
+  if ( $force_script ) {
+    if ( ! preg_match( '#/caldav\.php$#', $url ) ) $url .= '/caldav.php';
+  }
+  $url .= $partial_path;
+  $url = preg_replace( '#^(https?://.+)//#', '$1/', $url );  // Ensure we don't double any '/'
+  $url = preg_replace('#^https?://[^/]+#', '', $url );       // Remove any protocol + hostname portion
+
+  return $url;
+}
+
+
+/**
+* Deconstruct a dav_name from the supplied URL.  The dav_name will be urldecoded.
+*
+* @param string $partial_path  The part of the path after the script name
+*/
+function DeconstructURL( $url, $force_script = false ) {
+  global $c;
+
+  $dav_name = rawurldecode($url);
+
+  /** Allow a path like .../username/calendar.ics to translate into the calendar URL */
+  if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $dav_name, $matches ) ) {
+    $dav_name = $matches[1]. '/';
+  }
+
+  /** remove any leading protocol/server/port/prefix... */
+  if ( !isset($c->deconstruction_base_path) ) $c->deconstruction_base_path = ConstructURL('/');
+  if ( preg_match( '%^(.*?)'.str_replace('%', '\\%',$c->deconstruction_base_path).'(.*)$%', $dav_name, $matches ) ) {
+    if ( $matches[1] == '' || $matches[1] == $c->protocol_server_port ) {
+      $dav_name = $matches[2];
+    }
+  }
+
+  /** strip doubled slashes */
+  if ( strstr($dav_name,'//') ) $dav_name = preg_replace( '#//+#', '/', $dav_name);
+
+  if ( substr($dav_name,0,1) != '/' ) $dav_name = '/'.$dav_name;
+
+  return $dav_name;
+}
+
+
+/**
+* Convert a date from ISO format into the sad old HTTP format.
+* @param string $isodate The date to convert
+*/
+function ISODateToHTTPDate( $isodate ) {
+  // Use strtotime since strptime is not available on Windows platform.
+  return( gmstrftime('%a, %d %b %Y %T GMT', strtotime($isodate)) );
+}
+
+/**
+* Convert a date into ISO format into the sparkly new ISO format.
+* @param string $indate The date to convert
+*/
+function DateToISODate( $indate ) {
+  // Use strtotime since strptime is not available on Windows platform.
+  return( date('c', strtotime($indate)) );
+}
+
+/**
+* Given a privilege string, or an array of privilege strings, return a bit mask
+* of the privileges.
+* @param mixed $raw_privs The string (or array of strings) of privilege names
+* @return integer A bit mask of the privileges.
+*/
+define("DAVICAL_MAXPRIV", "65535");
+define("DAVICAL_ADDRESSBOOK_MAXPRIV", "1023");
+function privilege_to_bits( $raw_privs ) {
+  $out_priv = 0;
+
+  if ( gettype($raw_privs) == 'string' ) $raw_privs = array( $raw_privs );
+
+  if ( ! is_array($raw_privs) ) $raw_privs = array($raw_privs);
+
+  foreach( $raw_privs AS $priv ) {
+    $trim_priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv)));
+    switch( $trim_priv ) {
+      case 'read'                            : $out_priv |=     1;  break;
+      case 'write-properties'                : $out_priv |=     2;  break;
+      case 'write-content'                   : $out_priv |=     4;  break;
+      case 'unlock'                          : $out_priv |=     8;  break;
+      case 'read-acl'                        : $out_priv |=    16;  break;
+      case 'read-current-user-privilege-set' : $out_priv |=    32;  break;
+      case 'bind'                            : $out_priv |=    64;  break;
+      case 'unbind'                          : $out_priv |=   128;  break;
+      case 'write-acl'                       : $out_priv |=   256;  break;
+      case 'read-free-busy'                  : $out_priv |=   512;  break;
+      case 'schedule-deliver-invite'         : $out_priv |=  1024;  break;
+      case 'schedule-deliver-reply'          : $out_priv |=  2048;  break;
+      case 'schedule-query-freebusy'         : $out_priv |=  4096;  break;
+      case 'schedule-send-invite'            : $out_priv |=  8192;  break;
+      case 'schedule-send-reply'             : $out_priv |= 16384;  break;
+      case 'schedule-send-freebusy'          : $out_priv |= 32768;  break;
+
+      /** Aggregates of Privileges */
+      case 'write'                           : $out_priv |=   198;  break; // 2 + 4 + 64 + 128
+      case 'schedule-deliver'                : $out_priv |=  7168;  break; // 1024 + 2048 + 4096
+      case 'schedule-send'                   : $out_priv |= 57344;  break; // 8192 + 16384 + 32768
+      case 'all'                             : $out_priv  = DAVICAL_MAXPRIV;  break;
+      default:
+        dbg_error_log( 'ERROR', 'Cannot convert privilege of "%s" into bits.', $priv );
+
+    }
+  }
+
+  // 'all' will include future privileges
+  if ( ($out_priv & DAVICAL_MAXPRIV) >= DAVICAL_MAXPRIV ) $out_priv = pow(2,24) - 1;
+  return $out_priv;
+}
+
+
+/**
+* Given a bit mask of the privileges, will return an array of the
+* text values of privileges.
+* @param integer $raw_bits A bit mask of the privileges.
+* @return mixed The string (or array of strings) of privilege names
+*/
+function bits_to_privilege( $raw_bits, $resourcetype = 'resource' ) {
+  $out_priv = array();
+
+  if ( is_string($raw_bits) ) {
+    $raw_bits = bindec($raw_bits);
+  }
+
+  if ( ($raw_bits & DAVICAL_MAXPRIV) == DAVICAL_MAXPRIV ) $out_priv[] = 'all';
+
+  if ( ($raw_bits &   1) != 0 ) $out_priv[] = 'DAV::read';
+  if ( ($raw_bits &   8) != 0 ) $out_priv[] = 'DAV::unlock';
+  if ( ($raw_bits &  16) != 0 ) $out_priv[] = 'DAV::read-acl';
+  if ( ($raw_bits &  32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
+  if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
+  if ( ($resourcetype == 'calendar' || $resourcetype == 'proxy') && ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
+
+  if ( ($raw_bits & 198) != 0 ) {
+    if ( ($raw_bits & 198) == 198 ) $out_priv[] = 'DAV::write';
+    if ( ($raw_bits &   2) != 0 ) $out_priv[] = 'DAV::write-properties';
+    if ( ($raw_bits &   4) != 0 ) $out_priv[] = 'DAV::write-content';
+    if ( ($raw_bits &  64) != 0 ) $out_priv[] = 'DAV::bind';
+    if ( ($raw_bits & 128) != 0 ) $out_priv[] = 'DAV::unbind';
+  }
+
+  if ( $resourcetype == 'schedule-inbox' && ($raw_bits & 7168) != 0 ) {
+    if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
+    if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
+    if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
+    if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
+  }
+
+  if ( $resourcetype == 'schedule-outbox' && ($raw_bits & 57344) != 0 ) {
+    if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
+    if ( ($raw_bits &  8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
+    if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
+    if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
+  }
+
+//  dbg_error_log( 'DAVResource', ' Privilege bit "%s" is "%s".', $raw_bits, implode(', ', $out_priv) );
+
+  return $out_priv;
+}
+
+
+/**
+* Returns the array of privilege names converted into XMLElements
+*/
+function privileges_to_XML( $privilege_names, &$xmldoc=null ) {
+  if ( !isset($xmldoc) && isset($GLOBALS['reply']) ) $xmldoc = $GLOBALS['reply'];
+  $privileges = array();
+  foreach( $privilege_names AS $k ) {
+    $privilege = new XMLElement('privilege');
+    if ( isset($xmldoc) )
+      $xmldoc->NSElement($privilege,$k);
+    else
+      $privilege->NewElement($k);
+    $privileges[] = $privilege;
+  }
+  return $privileges;
+}
+
Index: contrib/davical/inc/iSchedule.php
===================================================================
--- contrib/davical/inc/iSchedule.php	(revision 3733)
+++ contrib/davical/inc/iSchedule.php	(revision 3733)
@@ -0,0 +1,346 @@
+<?PHP
+/**
+* Functions that are needed for iScheduling requests
+*
+*  - verifying Domain Key signatures
+*  - delivering remote scheduling requests to local users inboxes
+*  - Utility functions which we can use to decide whether this
+*    is a permitted activity for this user.
+*
+* @package   davical
+* @subpackage   iSchedule
+* @author    Rob Ostensen <rob@boxacle.net>
+* @copyright Rob Ostensen
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
+*/
+
+require_once("XMLDocument.php");
+
+/**
+* A class for handling iScheduling requests.
+*
+* @package   davical
+* @subpackage   iSchedule
+*/
+class iSchedule
+{
+	public $parsed;
+	public $selector;
+	public $domain;
+	private $dk;
+	private $DKSig;
+	private $try_anyway = false;
+	private $failed = false;
+	private $failOnError = true;
+	private $subdomainsOK = true;
+	private $remote_public_key ;
+
+	function __construct ( )
+	{
+		$this->selector = 'cal';
+		if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
+			$this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
+	}
+
+  /**
+	*	gets the domainkey TXT record from DNS  
+	*/ 
+	function getTxt ()
+	{
+		// TODO handle parents of subdomains and procuration records
+		$dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
+		if ( count ( $dkim ) > 0 )
+			$this->dk = $dkim [ 0 ] [ 'txt' ];
+		else 
+		{
+			$this->failed = true;
+			return false;
+		}
+		return true;	
+	}
+
+  /**
+	* parses DNS TXT record from domainkey lookup
+	*/ 
+	function parseTxt ( )
+	{
+		if ( $this->failed == true )
+			return false;
+		$clean = preg_replace ( '/[\s\t]*([;=])[\s\t]*/', '$1', $this->dk );
+		$pairs = preg_split ( '/;/', $clean );
+		$this->parsed = array();
+		foreach ( $pairs  as $v )
+		{
+			list($key,$value) = preg_split ( '/=/', $v, 2 );
+			if ( preg_match ( '/(g|k|n|p|s|t|v)/', $key ) )
+				$this->parsed [ $key ] = $value;
+			else
+				$this->parsed_ignored [ $key ] = $value;
+		}
+		return true;
+	}
+	
+  /**
+	* validates that domainkey is acceptable for the current request
+	*/ 
+	function validateKey ( )
+	{
+		$this->failed = true;
+		if ( isset ( $this->parsed [ 's' ] ) )
+		{
+			if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) )
+				return 'foo';
+		}
+		if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' )
+			return false;
+		if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) )
+			return false;
+		else
+		{
+			if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
+				$this->failOnError = false;
+			if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
+				$this->subdomainsOK = false;
+		}
+		if ( isset ( $this->parsed [ 'g' ] ) )
+			$this->remote_user_rule = $this->parsed [ 'g' ];
+		if ( isset ( $this->parsed [ 'p' ] ) )
+		{
+			$data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( preg_replace ( '/_/', '', $this->parsed [ 'p' ] ), 64 )) . "\n-----END PUBLIC KEY-----"; 
+			if ( $data === false )
+				return false;
+			$this->remote_public_key = $data;
+		}
+		else 
+			return false;
+		$this->failed = false;
+		return true;
+	}
+
+  /**
+	* finds a remote calender server via DNS SRV records
+	*/ 
+	function getServer ( )
+	{
+		$this->remote_ssl = false;
+		$r = dns_get_record ( '_ischedules._tcp.' . $this->domain , DNS_SRV );
+		if ( 0 < count ( $r ) )
+		{
+			$remote_server            = $r [ 0 ] [ 'target' ];
+			$remote_port              = $r [ 0 ] [ 'port' ];
+			$this->remote_ssl = true;
+		}
+		if ( ! isset ( $remote_server ) )
+		{
+			$r = dns_get_record ( '_ischedule._tcp.' . $this->domain , DNS_SRV );
+			if ( 0 < count ( $r ) )
+			{
+				$remote_server            = $r [ 0 ] [ 'target' ];
+				$remote_port              = $r [ 0 ] [ 'port' ];
+			}
+		}
+		elseif ( $this->try_anyway == true )
+		{
+			if ( ! isset ( $remote_server ) )
+				$remote_server = $this->domain;
+			if ( ! isset ( $remote_port ) )
+				$remote_port = 80;
+		}
+		if ( ! isset ( $remote_server ) )
+			return false;
+		$this->remote_server = $remote_server;
+		$this->remote_port = $remote_port;
+	}
+
+  /**
+	*	get capabilities from remote server 
+	*/ 
+	function getCapabilities ( )
+	{
+		$remote_capabilities = file_get_contents ( 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' . 
+			$this->remote_server . ':' . $this->remote_port . 
+			'/.well-known/ischedule?query=capabilities' );
+		$xmltree = BuildXMLTree( $request->xml_tags, $position);
+		if ( !is_object($xmltree) ) {
+			  $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
+		}
+	}
+
+
+  /**
+  * signs a POST body and headers 
+  *
+	* @param string $body the body of the POST
+	* @param array  $headers the headers to sign as passed to header ();
+  */
+	function signDKIM ( $body, $headers )
+	{
+		$b = '';
+		if ( ! is_array ( $headers ) )
+			return false;
+		foreach ( $headers as $v )
+			$b .= $v . "\n";
+		$dk['s'] = $this->selector;
+		$dk['d'] = $this->domain;
+		$dk['c'] = 'simple-http';
+		$dk['q'] = 'dns/txt';
+		$dk['bh'] = base64_encode ( hash ( 'sha256', $body , true ) );
+		 //a=rsa-sha1; d=caveman.name; s=cal; c=simple-http; q=dns/txt; h=Originator:Recipient:Host:Content-Type; b
+		// XXX finish me
+	}
+
+  /**
+  * parses and validates DK header 
+  *
+	* @param string $sig the value of the DKIM-Signature header
+  */
+	function parseDKIM ( $sig )
+	{
+
+		$this->failed = true;
+		$tags = preg_split ( '/;[\s\t]/', $sig );
+		foreach ( $tags as $v )
+		{
+			list($key,$value) = preg_split ( '/=/', $v, 2 );
+			$dkim[$key] = $value;
+		}
+		// the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
+		// but it does define the value, it should be simple-http.  RFC4871 also defines two methods
+		// simple and relaxed, simple is probably the same as simple http
+		// relaxed allows for header case folding and whitespace folding, see section 3.4.4 or RFC4871
+		if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
+			return 'bad canonicalization:' . $dkim['c'] ;
+		if ( count ( $matches ) > 2 )
+			$this->body_cannon = $matches[2];
+		else
+			$this->body_cannon = $matches[1];
+		$this->header_cannon = $matches[1];
+		// signing algorythm REQUIRED
+		if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) 
+			return 'bad signing algorythm:' . $dkim['a'] ;
+    // query method to retrieve public key, could/should we add https to the spec?  REQUIRED 
+		if ( $dkim['q'] != 'dns/txt' ) 
+			return 'bad query method';
+    // domain of the signing entity REQUIRED
+		if ( ! isset ( $dkim['d'] ) )  
+			return 'missing signing domain';
+		$this->remote_server = $dkim['d'];
+    // identity of signing agent, OPTIONAL
+		if ( isset ( $dkim['i'] ) )    
+      // if present, domain of the signing agent must be a match or a subdomain of the signing domain
+			if ( ! stristr ( $dkim['i'], $dkim['d'] ) ) // RFC4871 does not specify a case match requirement
+				return 'signing domain mismatch';
+		// grab the local part of the signing agent if it's an email address
+		if ( strstr ( $dkim [ 'i' ], '@' ) )
+			$this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
+    // selector used to retrieve public key REQUIRED
+		if ( ! isset ( $dkim['s'] ) )  
+			return 'missing selector';
+		$this->remote_selector = $dkim['s'];
+    // signed header fields, colon seperated  REQUIRED
+		if ( ! isset ( $dkim['h'] ) )  
+			return 'missing list of signed headers';
+		$this->signed_headers = preg_split ( '/:/', $dkim['h'] );
+		foreach ( $this->signed_headers as $h )
+      // signed header fields MUST actually be present in the request
+			// DKIM Signature is NOT allowed in signed header fields per RFC4871
+			if ( ( ! isset ( $_SERVER['HTTP_' . strtr ( strtoupper ( $h ), '-', '_' ) ] ) &&
+             ! isset ( $_SERVER[ strtr ( strtoupper ( $h ), '-', '_' ) ] ) ) 
+				  || strtolower ( $h ) == 'dkim-signature' )
+				return "header $h is signed but missing from request";
+    // body hash REQUIRED
+		if ( ! isset ( $dkim['bh'] ) ) 
+			return 'missing body signature';
+    // signed header hash REQUIRED
+		if ( ! isset ( $dkim['b'] ) )  
+			return 'missing signature in b field';
+		// length of body used for signing
+		if ( isset ( $dkim['l'] ) ) 
+			$this->signed_length = $dkim['l'];
+		$this->failed = false;
+		$this->DKSig = $dkim;
+		return true;
+	}	
+	
+  /**
+	* split up a mailto uri into domain and user components
+	*/ 
+	function parseURI ( $uri )
+	{
+		if ( preg_match ( '/^mailto:([^@]+)@([^\s\t\n]+)/', $uri, $matches ) )
+		{
+			$this->remote_user = $matches[1];
+			$this->domain = $matches[2];
+		}
+		else
+			return false;
+	}
+
+  /**
+	*	verifies parsed DKIM header is valid for current message with a signature from the public key in DNS 
+	*/ 
+	function verifySignature ( )
+	{
+		global $request,$c;
+		$this->failed = true;
+		$signed = '';
+		foreach ( $this->signed_headers as $h )
+			if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
+				$signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+		  else
+				$signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\n";
+		$body = $request->raw_post;
+		if ( ! isset ( $this->signed_length ) )
+			$this->signed_length = strlen ( $body );
+		else
+			$body = substr ( $body, 0, $this->signed_length );
+		if ( isset ( $this->remote_user_rule ) )
+			if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
+				return false;
+		$body_hash = base64_encode ( hash ( preg_replace ( '/^.*(sha[1256]+).*/','$1', $this->DKSig['a'] ), $body , true ) );
+		if ( $this->DKSig['bh'] != $body_hash )
+		  return false;
+		$sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
+		$sig = preg_replace ( '/ b=[^;\s\n\t]+/', ' b=', $sig );
+		$sig = preg_replace ( '/[\r\n]*$/', '', $sig );
+		$signed .= 'DKIM-Signature: ' . $sig;
+		$verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key );
+		if (  $verify != 1 )
+			return false;
+		$this->failed = false;
+		return true;
+	}
+
+  /**
+	* checks that current request has a valid DKIM signature signed by a currently valid key from DNS
+	*/ 
+	function validateRequest ( )
+	{
+		global $request;
+		if ( isset ( $_SERVER['HTTP_DKIM_SIGNATURE'] ) )
+			$sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
+		else
+			$request->DoResponse( 403, translate('DKIM signature missing') );
+		
+    $err = $this->parseDKIM ( $sig );
+		if ( $err !== true || $this->failed )
+			$request->DoResponse( 403, translate('DKIM signature invalid ' ) . "\n" . $err . "\n" . $sig );
+		if ( ! $this->getTxt () || $this->failed )
+			$request->DoResponse( 403, translate('DKIM signature validation failed(DNS ERROR)') );
+		if ( ! $this->parseTxt () || $this->failed )
+			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Parse ERROR)') );
+		if ( ! $this->validateKey () || $this->failed )
+			$request->DoResponse( 403, translate('DKIM signature validation failed(KEY Validation ERROR)') );
+		if ( ! $this->verifySignature () || $this->failed )
+			$request->DoResponse( 403, translate('DKIM signature validation failed(Signature verification ERROR)') . $this->verifySignature() );
+		return true;
+	}
+}
+
+$d = new iSchedule ();
+if ( $d->validateRequest ( ) )
+{
+	include ( 'caldav-POST.php' );
+	// TODO
+	// handle request.
+}
Index: contrib/davical/inc/caldav-client.php
===================================================================
--- contrib/davical/inc/caldav-client.php	(revision 3733)
+++ contrib/davical/inc/caldav-client.php	(revision 3733)
@@ -0,0 +1,549 @@
+<?php
+/**
+* A Class for connecting to a caldav server
+*
+* @package   awl
+* removed curl - now using fsockopen
+* changed 2009 by Andres Obrero - Switzerland andres@obrero.ch
+*
+* @subpackage   caldav
+* @author Andrew McMillan <debian@mcmillan.net.nz>
+* @copyright Andrew McMillan
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+
+/**
+* A class for accessing DAViCal via CalDAV, as a client
+*
+* @package   awl
+*/
+class CalDAVClient {
+  /**
+  * Server, username, password, calendar
+  *
+  * @var string
+  */
+  var $base_url, $user, $pass, $calendar, $entry, $protocol, $server, $port;
+
+  /**
+  * The useragent which is send to the caldav server
+  *
+  * @var string
+  */
+  var $user_agent = 'DAViCalClient';
+
+  var $headers = array();
+  var $body = "";
+  var $requestMethod = "GET";
+  var $httpRequest = ""; // for debugging http headers sent
+  var $xmlRequest = ""; // for debugging xml sent
+  var $httpResponse = ""; // for debugging http headers received
+  var $xmlResponse = ""; // for debugging xml received
+
+  /**
+  * Constructor, initialises the class
+  *
+  * @param string $base_url  The URL for the calendar server
+  * @param string $user      The name of the user logging in
+  * @param string $pass      The password for that user
+  * @param string $calendar  The name of the calendar (not currently used)
+  */
+  function CalDAVClient( $base_url, $user, $pass, $calendar = '' ) {
+    $this->user = $user;
+    $this->pass = $pass;
+    $this->calendar = $calendar;
+    $this->headers = array();
+
+    if ( preg_match( '#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $base_url, $matches ) ) {
+      $this->server = $matches[2];
+      $this->base_url = $matches[5];
+      if ( $matches[1] == 'https' ) {
+        $this->protocol = 'ssl';
+        $this->port = 443;
+      }
+      else {
+        $this->protocol = 'tcp';
+        $this->port = 80;
+      }
+      if ( $matches[4] != '' ) {
+        $this->port = intval($matches[4]);
+      }
+    }
+    else {
+      trigger_error("Invalid URL: '".$base_url."'", E_USER_ERROR);
+    }
+  }
+
+  /**
+  * Adds an If-Match or If-None-Match header
+  *
+  * @param bool $match to Match or Not to Match, that is the question!
+  * @param string $etag The etag to match / not match against.
+  */
+  function SetMatch( $match, $etag = '*' ) {
+    $this->headers[] = sprintf( "%s-Match: %s", ($match ? "If" : "If-None"), $etag);
+  }
+
+ /*
+  * Add a Depth: header.  Valid values are 0, 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetDepth( $depth = '0' ) {
+    $this->headers[] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") );
+  }
+
+  /**
+  * Add a Depth: header.  Valid values are 1 or infinity
+  *
+  * @param int $depth  The depth, default to infinity
+  */
+  function SetUserAgent( $user_agent = null ) {
+    if ( !isset($user_agent) ) $user_agent = $this->user_agent;
+    $this->user_agent = $user_agent;
+  }
+
+  /**
+  * Add a Content-type: header.
+  *
+  * @param int $type  The content type
+  */
+  function SetContentType( $type ) {
+    $this->headers[] = "Content-type: $type";
+  }
+
+  /**
+  * Split response into httpResponse and xmlResponse
+  *
+  * @param string Response from server
+   */
+  function ParseResponse( $response ) {
+      $pos = strpos($response, '<?xml');
+      if ($pos === false) {
+        $this->httpResponse = trim($response);
+      }
+      else {
+        $this->httpResponse = trim(substr($response, 0, $pos));
+        $this->xmlResponse = trim(substr($response, $pos));
+      }
+  }
+
+  /**
+   * Output http request headers
+   *
+   * @return HTTP headers
+   */
+  function GetHttpRequest() {
+      return $this->httpRequest;
+  }
+  /**
+   * Output http response headers
+   *
+   * @return HTTP headers
+   */
+  function GetHttpResponse() {
+      return $this->httpResponse;
+  }
+  /**
+   * Output xml request
+   *
+   * @return raw xml
+   */
+  function GetXmlRequest() {
+      return $this->xmlRequest;
+  }
+  /**
+   * Output xml response
+   *
+   * @return raw xml
+   */
+  function GetXmlResponse() {
+      return $this->xmlResponse;
+  }
+
+  /**
+  * Send a request to the server
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return string The content of the response from the server
+  */
+  function DoRequest( $relative_url = "" ) {
+    if(!defined("_FSOCK_TIMEOUT")){ define("_FSOCK_TIMEOUT", 10); }
+    $headers = array();
+
+    $headers[] = $this->requestMethod." ". $this->base_url . $relative_url . " HTTP/1.1";
+    $headers[] = "Authorization: Basic ".base64_encode($this->user .":". $this->pass );
+    $headers[] = "Host: ".$this->server .":".$this->port;
+
+    foreach( $this->headers as $ii => $head ) {
+      $headers[] = $head;
+    }
+    $headers[] = "Content-Length: " . strlen($this->body);
+    $headers[] = "User-Agent: " . $this->user_agent;
+    $headers[] = 'Connection: close';
+    $this->httpRequest = join("\r\n",$headers);
+    $this->xmlRequest = $this->body;
+
+    $fip = fsockopen( $this->protocol . '://' . $this->server, $this->port, $errno, $errstr, _FSOCK_TIMEOUT); //error handling?
+    if ( !(get_resource_type($fip) == 'stream') ) return false;
+    if ( !fwrite($fip, $this->httpRequest."\r\n\r\n".$this->body) ) { fclose($fip); return false; }
+    $rsp = "";
+    while( !feof($fip) ) { $rsp .= fgets($fip,8192); }
+    fclose($fip);
+
+    $this->headers = array();  // reset the headers array for our next request
+    $this->ParseResponse($rsp);
+    return $rsp;
+  }
+
+
+  /**
+  * Send an OPTIONS request to the server
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return array The allowed options
+  */
+  function DoOptionsRequest( $relative_url = "" ) {
+    $this->requestMethod = "OPTIONS";
+    $this->body = "";
+    $headers = $this->DoRequest($relative_url);
+    $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers );
+    $options = array_flip( preg_split( '/[, ]+/', $options_header ));
+    return $options;
+  }
+
+
+
+  /**
+  * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR)
+  *
+  * @param string $method The method (PROPFIND, REPORT, etc) to use with the request
+  * @param string $xml The XML to send along with the request
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  *
+  * @return array An array of the allowed methods
+  */
+  function DoXMLRequest( $request_method, $xml, $relative_url = '' ) {
+    $this->body = $xml;
+    $this->requestMethod = $request_method;
+    $this->SetContentType("text/xml");
+    return $this->DoRequest($relative_url);
+  }
+
+
+
+  /**
+  * Get a single item from the server.
+  *
+  * @param string $relative_url The part of the URL after the calendar
+  */
+  function DoGETRequest( $relative_url ) {
+    $this->body = "";
+    $this->requestMethod = "GET";
+    return $this->DoRequest( $relative_url );
+  }
+
+
+  /**
+  * PUT a text/icalendar resource, returning the etag
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  * @param string $icalendar The iCalendar resource to send to the server
+  * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource.
+  *
+  * @return string The content of the response from the server
+  */
+  function DoPUTRequest( $relative_url, $icalendar, $etag = null ) {
+    $this->body = $icalendar;
+
+    $this->requestMethod = "PUT";
+    if ( $etag != null ) {
+      $this->SetMatch( ($etag != '*'), $etag );
+    }
+    $this->SetContentType("text/icalendar");
+    $headers = $this->DoRequest($relative_url);
+
+    /**
+    * RSCDS will always return the real etag on PUT.  Other CalDAV servers may need
+    * more work, but we are assuming we are running against RSCDS in this case.
+    */
+    $etag = preg_replace( '/^.*Etag: "?([^"\r\n]+)"?\r?\n.*/is', '$1', $headers );
+    return $etag;
+  }
+
+
+  /**
+  * DELETE a text/icalendar resource
+  *
+  * @param string $relative_url The URL to make the request to, relative to $base_url
+  * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL.
+  *
+  * @return int The HTTP Result Code for the DELETE
+  */
+  function DoDELETERequest( $relative_url, $etag = null ) {
+    $this->body = "";
+
+    $this->requestMethod = "DELETE";
+    if ( $etag != null ) {
+      $this->SetMatch( true, $etag );
+    }
+    $this->DoRequest($relative_url);
+    return $this->resultcode;
+  }
+
+
+  /**
+  * Given XML for a calendar query, return an array of the events (/todos) in the
+  * response.  Each event in the array will have a 'href', 'etag' and '$response_type'
+  * part, where the 'href' is relative to the calendar and the '$response_type' contains the
+  * definition of the calendar data in iCalendar format.
+  *
+  * @param string $filter XML fragment which is the <filter> element of a calendar-query
+  * @param string $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  * @param string $report_type Used as a name for the array element containing the calendar data. @deprecated
+  *
+  * @return array An array of the relative URLs, etags, and events from the server.  Each element of the array will
+  *               be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied
+  *               etag (which only varies when the data changes) and the calendar data in iCalendar format.
+  */
+  function DoCalendarQuery( $filter, $relative_url = '' ) {
+
+    $xml = <<<EOXML
+<?xml version="1.0" encoding="utf-8" ?>
+<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+  <D:prop>
+    <C:calendar-data/>
+    <D:getetag/>
+  </D:prop>$filter
+</C:calendar-query>
+EOXML;
+
+    $this->DoXMLRequest( 'REPORT', $xml, $relative_url );
+    $xml_parser = xml_parser_create_ns('UTF-8');
+    $this->xml_tags = array();
+    xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
+    xml_parse_into_struct( $xml_parser, $this->xmlResponse, $this->xml_tags );
+    xml_parser_free($xml_parser);
+
+    $report = array();
+    foreach( $this->xml_tags as $k => $v ) {
+      switch( $v['tag'] ) {
+        case 'DAV::RESPONSE':
+          if ( $v['type'] == 'open' ) {
+            $response = array();
+          }
+          elseif ( $v['type'] == 'close' ) {
+            $report[] = $response;
+          }
+          break;
+        case 'DAV::HREF':
+          $response['href'] = basename( $v['value'] );
+          break;
+        case 'DAV::GETETAG':
+          $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']);
+          break;
+        case 'URN:IETF:PARAMS:XML:NS:CALDAV:CALENDAR-DATA':
+          $response['data'] = $v['value'];
+          break;
+      }
+    }
+    return $report;
+  }
+
+
+  /**
+  * Get the events in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEvents( $start = null, $finish = null, $relative_url = '' ) {
+    $filter = "";
+    if ( isset($start) && isset($finish) )
+        $range = "<C:time-range start=\"$start\" end=\"$finish\"/>";
+    else
+        $range = '';
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+      <C:comp-filter name="VEVENT">
+        $range
+      </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the todo's in a range from $start to $finish.  The dates should be in the
+  * format yyyymmddThhmmssZ and should be in GMT.  The events are returned as an
+  * array of event arrays.  Each event array will have a 'href', 'etag' and 'event'
+  * part, where the 'href' is relative to the calendar and the event contains the
+  * definition of the event in iCalendar format.
+  *
+  * @param timestamp $start The start time for the period
+  * @param timestamp $finish The finish time for the period
+  * @param boolean   $completed Whether to include completed tasks
+  * @param boolean   $cancelled Whether to include cancelled tasks
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetTodos( $start, $finish, $completed = false, $cancelled = false, $relative_url = "" ) {
+
+    if ( $start && $finish ) {
+$time_range = <<<EOTIME
+                <C:time-range start="$start" end="$finish"/>
+EOTIME;
+    }
+
+    // Warning!  May contain traces of double negatives...
+    $neg_cancelled = ( $cancelled === true ? "no" : "yes" );
+    $neg_completed = ( $cancelled === true ? "no" : "yes" );
+
+    $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VTODO">
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_completed">COMPLETED</C:text-match>
+                </C:prop-filter>
+                <C:prop-filter name="STATUS">
+                        <C:text-match negate-condition="$neg_cancelled">CANCELLED</C:text-match>
+                </C:prop-filter>$time_range
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by UID
+  *
+  * @param uid
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery()
+  */
+  function GetEntryByUid( $uid, $relative_url = '' ) {
+    $filter = "";
+    if ( $uid ) {
+      $filter = <<<EOFILTER
+  <C:filter>
+    <C:comp-filter name="VCALENDAR">
+          <C:comp-filter name="VEVENT">
+                <C:prop-filter name="UID">
+                        <C:text-match icollation="i;octet">$uid</C:text-match>
+                </C:prop-filter>
+          </C:comp-filter>
+    </C:comp-filter>
+  </C:filter>
+EOFILTER;
+    }
+
+    return $this->DoCalendarQuery($filter, $relative_url);
+  }
+
+
+  /**
+  * Get the calendar entry by HREF
+  *
+  * @param string    $href         The href from a call to GetEvents or GetTodos etc.
+  * @param string    $relative_url The URL relative to the base_url specified when the calendar was opened.  Default ''.
+  *
+  * @return string The iCalendar of the calendar entry
+  */
+  function GetEntryByHref( $href, $relative_url = '' ) {
+    return $this->DoGETRequest( $relative_url . $href );
+  }
+
+}
+
+/**
+* Usage example
+*
+* $cal = new CalDAVClient( "http://calendar.example.com/caldav.php/username/calendar/", "username", "password", "calendar" );
+* $options = $cal->DoOptionsRequest();
+* if ( isset($options["PROPFIND"]) ) {
+*   // Fetch some information about the events in that calendar
+*   $cal->SetDepth(1);
+*   $folder_xml = $cal->DoXMLRequest("PROPFIND", '<?xml version="1.0" encoding="utf-8" ?><propfind xmlns="DAV:"><prop><getcontentlength/><getcontenttype/><resourcetype/><getetag/></prop></propfind>' );
+* }
+* // Fetch all events for February
+* $events = $cal->GetEvents("20070101T000000Z","20070201T000000Z");
+* foreach ( $events AS $k => $event ) {
+*   do_something_with_event_data( $event['data'] );
+* }
+* $acc = array();
+* $acc["google"] = array(
+* "user"=>"kunsttherapie@gmail.com",
+* "pass"=>"xxxxx",
+* "server"=>"ssl://www.google.com",
+* "port"=>"443",
+* "uri"=>"https://www.google.com/calendar/dav/kunsttherapie@gmail.com/events/",
+* );
+*
+* $acc["davical"] = array(
+* "user"=>"some_user",
+* "pass"=>"big secret",
+* "server"=>"calendar.foo.bar",
+* "port"=>"80",
+* "uri"=>"http://calendar.foo.bar/caldav.php/some_user/home/",
+* );
+* //*******************************
+*
+* $account = $acc["davical"];
+*
+* //*******************************
+* $cal = new CalDAVClient( $account["uri"], $account["user"], $account["pass"], "", $account["server"], $account["port"] );
+* $options = $cal->DoOptionsRequest();
+* print_r($options);
+*
+* //*******************************
+* //*******************************
+*
+* $xmlC = <<<PROPP
+* <?xml version="1.0" encoding="utf-8" ?>
+* <D:propfind xmlns:D="DAV:" xmlns:C="http://calendarserver.org/ns/">
+*     <D:prop>
+*             <D:displayname />
+*             <C:getctag />
+*             <D:resourcetype />
+*
+*     </D:prop>
+* </D:propfind>
+* PROPP;
+* //if ( isset($options["PROPFIND"]) ) {
+*   // Fetch some information about the events in that calendar
+* //  $cal->SetDepth(1);
+* //  $folder_xml = $cal->DoXMLRequest("PROPFIND", $xmlC);
+* //  print_r( $folder_xml);
+* //}
+*
+* // Fetch all events for February
+* $events = $cal->GetEvents("20090201T000000Z","20090301T000000Z");
+* foreach ( $events as $k => $event ) {
+*     print_r($event['data']);
+*     print "\n---------------------------------------------\n";
+* }
+*
+* //*******************************
+* //*******************************
+*/
Index: contrib/davical/inc/test-RRULE.php
===================================================================
--- contrib/davical/inc/test-RRULE.php	(revision 3733)
+++ contrib/davical/inc/test-RRULE.php	(revision 3733)
@@ -0,0 +1,96 @@
+<?php
+require_once("../htdocs/always.php");
+require_once("RRule.php");
+
+header("Content-type: text/plain");
+
+echo <<<EOTXT
+Testing the RRule Library
+
+EOTXT;
+
+class RRuleTest {
+  var $dtstart;
+  var $recur;
+  var $description;
+  var $result_description;
+
+  function RRuleTest( $description, $start, $recur, $result_description = null ) {
+    $this->description = $description;
+    $this->dtstart = $start;
+    $this->recur = $recur;
+    $this->result_description = $result_description;
+  }
+
+  function PHPTest() {
+    $result = '';
+    $rule = new RRule( new iCalDate($this->dtstart), $this->recur );
+    $i = 0;
+    do {
+      $date = $rule->GetNext();
+      if ( isset($date) ) {
+        if ( ($i++ % 4) == 0 ) $result .= "\n";
+        $result .= "   " . $date->Render();
+      }
+    }
+    while( isset($date) && $i < 30 );
+    return $result;
+  }
+
+  function SQLTest() {
+    $result = '';
+    $sql = "SELECT event_instances::timestamp AS event_date FROM event_instances(?,?) LIMIT 30;";
+    $qry = new AwlQuery($sql, $this->dtstart, $this->recur);
+    // printf( "%s\n", $qry->querystring);
+    if ( $qry->Exec("test") && $qry->rows > 0 ) {
+      $i = 0;
+      while( $row = $qry->Fetch() ) {
+        if ( ($i++ % 4) == 0 ) $result .= "\n";
+        $result .= "   " . $row->event_date;
+      }
+    }
+    return $result;
+  }
+}
+
+
+$tests = array(
+    new RRuleTest( "Daily for 7 days", "20061103T073000", "RRULE:FREQ=DAILY;COUNT=7" )
+  , new RRuleTest( "Weekly for 26 weeks", "20061102T100000", "RRULE:FREQ=WEEKLY;COUNT=26;INTERVAL=1;BYDAY=TH" )
+  , new RRuleTest( "Fortnightly for 28 events", "20061103T160000", "RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20071122T235900" )
+  , new RRuleTest( "3/wk for 5 weeks", "20081101T160000", "RRULE:FREQ=WEEKLY;COUNT=15;INTERVAL=1;BYDAY=MO,WE,FR" )
+  , new RRuleTest( "Monthly forever", "20061104T073000", "RRULE:FREQ=MONTHLY" )
+  , new RRuleTest( "Monthly, on the 1st monday, 2nd wednesday, 3rd friday and last sunday, forever", "20061117T073000", "RRULE:FREQ=MONTHLY;BYDAY=1MO,2WE,3FR,-1SU" )
+  , new RRuleTest( "The last working day of each month", "20061107T113000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" )
+  , new RRuleTest( "Every working day", "20081020T103000", "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR" )
+  , new RRuleTest( "Every working day", "20081020T110000", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR" )
+  , new RRuleTest( "1st Tuesday, 2nd Wednesday, 3rd Thursday & 4th Friday, every March, June, September, October and December", "20081001T133000", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=1TU,2WE,3TH,4FR;BYMONTH=3,6,9,10,12" )
+  , new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=MONTHLY;INTERVAL=1;BYDAY=TU,FR" )
+  , new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,FR" )
+  , new RRuleTest( "Every tuesday and friday", "20081017T084500", "RRULE:FREQ=DAILY;INTERVAL=1;BYDAY=TU,FR" )
+  , new RRuleTest( "Time zone 1", "19700315T030000", "FREQ=YEARLY;INTERVAL=1;BYDAY=3SU;BYMONTH=3" )
+  , new RRuleTest( "Time zone 2", "19700927T020000", "FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=9" )
+  , new RRuleTest( "Time zone 3", "19810329T030000", "FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU" )
+  , new RRuleTest( "Time zone 4", "20000404T020000", "FREQ=YEARLY;BYDAY=1SU;BYMONTH=4" )
+);
+
+foreach( $tests AS $k => $test ) {
+  echo "=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=\n";
+  echo "$test->dtstart - $test->recur\n";
+  echo "$test->description\n";
+  $php_result = $test->PHPTest();
+/*
+  $sql_result = $test->SQLTest();
+  if ( $php_result == $sql_result ) {
+    echo "PHP & SQL results are identical :-)\n";
+  }
+  else {
+    echo "PHP & SQL results differ :-(\n";
+  }
+*/
+  echo "PHP Result:\n$php_result\n\n";
+//  echo "SQL Result:\n$sql_result\n\n";  // Still under development
+}
+
+
+exit(0);
Index: contrib/davical/inc/caldav-GET.php
===================================================================
--- contrib/davical/inc/caldav-GET.php	(revision 3733)
+++ contrib/davical/inc/caldav-GET.php	(revision 3733)
@@ -0,0 +1,189 @@
+<?php
+/**
+* CalDAV Server - handle GET method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("get", "GET method handler");
+
+require_once("iCalendar.php");
+require_once("DAVResource.php");
+
+$dav_resource = new DAVResource($request->path);
+$dav_resource->NeedPrivilege( array('urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read') );
+
+if ( ! $dav_resource->Exists() ) {
+  $request->DoResponse( 404, translate("Resource Not Found.") );
+}
+
+function obfuscated_event( $icalendar ) {
+  // The user is not admin / owner of this calendar looking at his calendar and can not admin the other cal,
+  // or maybe they don't have *read* access but they got here, so they must at least have free/busy access
+  // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
+  $confidential = new iCalComponent();
+  $confidential->SetType($icalendar->GetType());
+  $confidential->AddProperty( 'SUMMARY', translate('Busy') );
+  $confidential->AddProperty( 'CLASS', 'CONFIDENTIAL' );
+  $confidential->SetProperties( $icalendar->GetProperties('DTSTART'), 'DTSTART' );
+  $confidential->SetProperties( $icalendar->GetProperties('RRULE'), 'RRULE' );
+  $confidential->SetProperties( $icalendar->GetProperties('DURATION'), 'DURATION' );
+  $confidential->SetProperties( $icalendar->GetProperties('DTEND'), 'DTEND' );
+  $confidential->SetProperties( $icalendar->GetProperties('UID'), 'UID' );
+  $confidential->SetProperties( $icalendar->GetProperties('CREATED'), 'CREATED' );
+
+  return $confidential;
+}
+
+
+if ( $dav_resource->IsCollection() ) {
+  if ( ! $dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections) ) {
+    /** RFC2616 says we must send an Allow header if we send a 405 */
+    header("Allow: PROPFIND,PROPPATCH,OPTIONS,MKCOL,REPORT,DELETE");
+    $request->DoResponse( 405, translate("GET requests on collections are only supported for calendars.") );
+  }
+
+  /**
+  * The CalDAV specification does not define GET on a collection, but typically this is
+  * used as a .ics download for the whole collection, which is what we do also.
+  */
+  $sql = 'SELECT caldav_data, class, caldav_type, calendar_item.user_no, logged_user ';
+  $sql .= 'FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING ( dav_id ) WHERE ';
+  if ( isset($c->get_includes_subcollections) && $c->get_includes_subcollections ) {
+    $sql .= '(collection.dav_name ~ :path_match ';
+    $sql .= 'OR collection.collection_id IN (SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match)) ';
+    $params = array( ':path_match' => '^'.$request->path );
+  }
+  else {
+    $sql .= 'caldav_data.collection_id = :collection_id ';
+    $params = array( ':collection_id' => $dav_resource->resource_id() );
+  }
+  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= ' ORDER BY dav_id';
+
+  $qry = new AwlQuery( $sql, $params );
+  if ( !$qry->Exec("GET",__LINE__,__FILE__) ) {
+    $request->DoResponse( 500, translate("Database Error") );
+  }
+
+  /**
+  * Here we are constructing a whole calendar response for this collection, including
+  * the timezones that are referred to by the events we have selected.
+  */
+  $vcal = new iCalComponent();
+  $vcal->VCalendar();
+  $displayname = $dav_resource->GetProperty('displayname');
+  if ( isset($displayname) ) {
+    $vcal->AddProperty("X-WR-CALNAME", $displayname);
+  }
+
+  $need_zones = array();
+  $timezones = array();
+  while( $event = $qry->Fetch() ) {
+    $ical = new iCalComponent( $event->caldav_data );
+
+    /** Save the timezone component(s) into a minimal set for inclusion later */
+    $event_zones = $ical->GetComponents('VTIMEZONE',true);
+    foreach( $event_zones AS $k => $tz ) {
+      $tzid = $tz->GetPValue('TZID');
+      if ( !isset($tzid) ) continue ;
+      if ( $tzid != '' && !isset($timezones[$tzid]) ) {
+        $timezones[$tzid] = $tz;
+      }
+    }
+
+    /** Work out which ones are actually used here */
+    $comps = $ical->GetComponents('VTIMEZONE',false);
+    foreach( $comps AS $k => $comp ) {
+      $tzid = $comp->GetPParamValue('DTSTART', 'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+      $tzid = $comp->GetPParamValue('DUE',     'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+      $tzid = $comp->GetPParamValue('DTEND',   'TZID');      if ( isset($tzid) && !isset($need_zones[$tzid]) ) $need_zones[$tzid] = 1;
+
+      if ( $dav_resource->HavePrivilegeTo('all',false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user
+            || ( $c->allow_get_email_visibility && $comp->IsAttendee($session->email) ) ) {
+        /**
+        * These people get to see all of the event, and they should always
+        * get any alarms as well.
+        */
+        $vcal->AddComponent($comp);
+        continue;
+      }
+      /** No visibility even of the existence of these events if they aren't admin/owner/attendee */
+      if ( $event->class == 'PRIVATE' ) continue;
+
+      if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL' ) {
+       $vcal->AddComponent(obfuscated_event($comp));
+      }
+      elseif ( isset($c->hide_alarm) && $c->hide_alarm ) {
+        // Otherwise we hide the alarms (if configured to)
+        $comp->ClearComponents('VALARM');
+        $vcal->AddComponent($comp);
+      }
+      else {
+        $vcal->AddComponent($comp);
+      }
+    }
+  }
+
+  /** Put the timezones on there that we need */
+  foreach( $need_zones AS $tzid => $v ) {
+    if ( isset($timezones[$tzid]) ) $vcal->AddComponent($timezones[$tzid]);
+  }
+
+  $response = $vcal->Render();
+  header( 'Content-Length: '.strlen($response) );
+  header( 'Etag: '.$dav_resource->unique_tag() );
+  $request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $response), 'text/calendar; charset="utf-8"' );
+}
+
+
+// Just a single event then
+
+$resource = $dav_resource->resource();
+$ic = new iCalComponent( $resource->caldav_data );
+
+/** Default deny... */
+$allowed = false;
+if ( $dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $resource->user_no || $session->user_no == $resource->logged_user
+      || ( $c->allow_get_email_visibility && $ic->IsAttendee($session->email) ) ) {
+  /**
+  * These people get to see all of the event, and they should always
+  * get any alarms as well.
+  */
+  $allowed = true;
+}
+else if ( $resource->class != 'PRIVATE' ) {
+  $allowed = true; // but we may well obfuscate it below
+  if ( ! $dav_resource->HavePrivilegeTo('DAV::read') || ( $resource->class == 'CONFIDENTIAL' && ! $request->HavePrivilegeTo('DAV::write-content') ) ) {
+    $ical = new iCalComponent( $resource->caldav_data );
+    $comps = $ical->GetComponents('VTIMEZONE',false);
+    $confidential = obfuscated_event($comps[0]);
+    $ical->SetComponents( array($confidential), $resource->caldav_type );
+    $resource->caldav_data = $ical->Render();
+  }
+}
+// else $resource->class == 'PRIVATE' and this person may not see it.
+
+if ( ! $allowed ) {
+  $request->DoResponse( 403, translate("Forbidden") );
+}
+
+header( 'Etag: "'.$resource->dav_etag.'"' );
+header( 'Content-Length: '.strlen($resource->caldav_data) );
+
+$contenttype = 'text/plain';
+switch( $resource->caldav_type ) {
+  case 'VJOURNAL':
+  case 'VEVENT':
+  case 'VTODO':
+    $contenttype = 'text/calendar';
+    break;
+
+  case 'VCARD':
+    $contenttype = 'text/x-vcard';
+    break;
+}
+
+$request->DoResponse( 200, ($request->method == 'HEAD' ? '' : $resource->caldav_data), $contenttype.'; charset="utf-8"' );
Index: contrib/davical/inc/DAViCalSession.php
===================================================================
--- contrib/davical/inc/DAViCalSession.php	(revision 3733)
+++ contrib/davical/inc/DAViCalSession.php	(revision 3733)
@@ -0,0 +1,155 @@
+<?php
+/**
+* DAViCal extensions to AWL Session handling
+*
+* @package   davical
+* @subpackage   DAViCalSession
+* @author Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+/**
+* @global resource $session
+* @name $session
+* The session object is global.
+*/
+$session = 1;  // Fake initialisation
+
+// The Session object uses some (optional) configurable SQL to load
+// the records related to the logged-on user...  (the where clause gets added).
+// It's very important that someone not be able to externally control this,
+// so we make it a function rather than a variable.
+/**
+* @todo Make this a defined constant
+*/
+function local_session_sql() {
+  $sql = <<<EOSQL
+SELECT session.*, usr.*, principal.*
+        FROM session JOIN usr USING(user_no) JOIN principal USING(user_no)
+EOSQL;
+  return $sql;
+}
+
+/**
+* We extend the AWL Session class.
+*/
+require('Session.php');
+
+
+@Session::_CheckLogout();
+
+/**
+* A class for creating and holding session information.
+*
+* @package   davical
+*/
+class DAViCalSession extends Session
+{
+
+  /**
+  * Create a new DAViCalSession object.
+  *
+  * We create a Session and extend it with some additional useful RSCDS
+  * related information.
+  *
+  * @param string $sid A session identifier.
+  */
+  function __construct( $sid='' ) {
+    $this->Session($sid);
+  }
+
+
+  /**
+  * Internal function used to assign the session details to a user's new session.
+  * @param object $u The user+session object we (probably) read from the database.
+  */
+  function AssignSessionDetails( $u ) {
+    if ( !isset($u->principal_id) ) {
+      // If they don't have a principal_id set then we should re-read from our local database
+      $qry = new AwlQuery('SELECT * FROM dav_principal WHERE username = :username', array(':username' => $u->username) );
+      if ( $qry->Exec() && $qry->rows() == 1 ) {
+        $u = $qry->Fetch();
+      }
+    }
+
+    parent::AssignSessionDetails( $u );
+    $this->GetRoles();
+    if ( function_exists('awl_set_locale') && isset($this->locale) && $this->locale != '' ) {
+      awl_set_locale($this->locale);
+    }
+  }
+
+
+  /**
+  * Method used to get the user's roles
+  */
+  function GetRoles () {
+    $this->roles = array();
+    $sql = 'SELECT role_name FROM roles JOIN role_member ON roles.role_no=role_member.role_no WHERE user_no = '.$this->user_no;
+    $qry = new AwlQuery( $sql );
+    if ( $qry->Exec('DAViCalSession') && $qry->rows() > 0 ) {
+      while( $role = $qry->Fetch() ) {
+        $this->roles[$role->role_name] = 1;
+      }
+    }
+  }
+
+
+  /**
+  * Checks that this user is logged in, and presents a login screen if they aren't.
+  *
+  * The function can optionally confirm whether they are a member of one of a list
+  * of roles, and deny access if they are not a member of any of them.
+  *
+  * @param string $roles The list of roles that the user must be a member of one of to be allowed to proceed.
+  * @return boolean Whether or not the user is logged in and is a member of one of the required roles.
+  */
+  function LoginRequired( $roles = '' ) {
+    global $c, $session, $main_menu, $sub_menu, $tab_menu;
+
+    $current_domain = (isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:$_SERVER['SERVER_ADDR']);
+    if ( (isset($c->restrict_admin_domain) && $c->restrict_admin_domain != $current_domain)
+      || (isset($c->restrict_admin_port) && $c->restrict_admin_port != $_SERVER['SERVER_PORT'] ) ) {
+      header('Location: caldav.php');
+      dbg_error_log( 'LOG WARNING', 'Access to "%s" via "%s:%d" rejected.', $_SERVER['REQUEST_URI'], $current_domain, $_SERVER['SERVER_PORT'] );
+      exit(0);
+    }
+    if ( isset($c->restrict_admin_roles) && $roles == '' ) $roles = $c->restrict_admin_roles;
+    if ( $this->logged_in && $roles == '' ) return;
+    if ( ! $this->logged_in ) {
+      $c->messages[] = i18n('You must log in to use this system.');
+      include_once('page-header.php');
+      if ( function_exists('local_index_not_logged_in') ) {
+        local_index_not_logged_in();
+      }
+      else {
+        if ( $this->login_failed ) {
+          $c->messages[] = i18n('Invalid user name or password.');
+        }
+        echo '<h1>'.translate('Log On Please')."</h1>\n";
+        echo '<p>'.translate('For access to the')
+                  .' '.translate($c->system_name).' '
+                  .translate('you should log on with the username and password that have been issued to you.')
+            ."</p>\n";
+        echo '<p>'.translate('If you would like to request access, please e-mail').' '.$c->admin_email."</p>\n";
+        echo $this->RenderLoginPanel();
+      }
+    }
+    else {
+      $valid_roles = explode(',', $roles);
+      foreach( $valid_roles AS $k => $v ) {
+        if ( $this->AllowedTo($v) ) return;
+      }
+      $c->messages[] = i18n('You are not authorised to use this function.');
+      include_once('page-header.php');
+    }
+
+    include('page-footer.php');
+    exit;
+  }
+}
+
+$session = new DAViCalSession();
+$session->_CheckLogin();
+
Index: contrib/davical/inc/caldav-PROPFIND.php
===================================================================
--- contrib/davical/inc/caldav-PROPFIND.php	(revision 3733)
+++ contrib/davical/inc/caldav-PROPFIND.php	(revision 3733)
@@ -0,0 +1,297 @@
+<?php
+/**
+* CalDAV Server - handle PROPFIND method
+*
+* @package   davical
+* @subpackage   propfind
+* @author    Andrew McMillan <andrew@catalyst.net.nz>
+* @copyright Catalyst .Net Ltd, Andrew McMillan
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log('PROPFIND', 'method handler');
+include_once("drivers_ldap.php");
+$request->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy','DAV::read-current-user-privilege-set') );
+
+require_once('iCalendar.php');
+require_once('XMLDocument.php');
+require_once('DAVResource.php');
+
+$reply = new XMLDocument( array( 'DAV:' => '' ) );
+
+if ( !isset($request->xml_tags) ) {
+  $request->DoResponse( 403, translate("Request body contains no XML data!") );
+}
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+if ( !is_object($xmltree) ) {
+  $request->DoResponse( 403, translate("Request body is not valid XML data!") );
+}
+$allprop    = $xmltree->GetPath('/DAV::propfind/*');
+$property_list = array();
+foreach( $allprop AS $k1 => $propwrap ) {
+  switch ( $propwrap->GetTag() ) {
+    case 'DAV::allprop':
+      $property_list[] = 'DAV::allprop';
+      break;
+    case 'DAV::propname':
+      $property_list[] = 'DAV::propname';
+      break;
+    default:  // prop, include
+      $subprop = $propwrap->GetElements();
+      foreach( $subprop AS $k => $v ) {
+        $property_list[] = $v->GetTag();
+      }
+  }
+}
+
+
+/**
+ * Add the calendar-proxy-read/write pseudocollections
+ * @param responses array of responses to which to add the collections
+ */
+function add_proxy_response( $which, $parent_path ) {
+  global $request, $reply, $c, $session, $property_list;
+
+  if ($parent_path != $request->principal->dav_name()) {
+    dbg_error_log( 'PROPFIND', 'Not returning proxy response since "%s" != "%s"', $parent_path, $request->principal->dav_name() );
+    return null; // Nothing to proxy for
+  }
+
+  $collection = (object) '';
+  if ( $which == 'read' ) {
+    $proxy_group = $request->principal->ReadProxyGroup();
+  } else if ( $which == 'write' ) {
+    $proxy_group = $request->principal->WriteProxyGroup();
+  }
+
+  dbg_error_log( 'PROPFIND', 'Returning proxy response to "%s" for "%s"', $which, $parent_path );
+
+  $collection->parent_container = $parent_path;
+  $collection->dav_name = $parent_path.'calendar-proxy-'.$which.'/';
+  $collection->is_calendar    = 'f';
+  $collection->is_addressbook = 'f';
+  $collection->is_principal   = 't';
+  $collection->is_proxy       = 't';
+  $collection->proxy_type     = $which;
+  $collection->type           = 'proxy';
+  $collection->dav_displayname = $collection->dav_name;
+  $collection->collection_id = 0;
+  $collection->user_no = $session->user_no;
+  $collection->username = $session->username;
+  $collection->email = $session->email;
+  $collection->created = date('Ymd\THis');
+  $collection->dav_etag = md5($c->system_name . $collection->dav_name . implode($proxy_group) );
+  $collection->proxy_for = $proxy_group;
+  $collection->resourcetypes  = sprintf('<DAV::collection/><http://calendarserver.org/ns/:calendar-proxy-%s/>', $which);
+  $collection->in_freebusy_set = 'f';
+  $collection->schedule_transp = 'transp';
+  $collection->timezone        = null;
+  $collection->description     = '';
+
+  $resource = new DAVResource($collection);
+  $resource->FetchPrincipal();
+  return $resource->RenderAsXML($property_list, $reply);
+
+}
+
+
+/**
+* Get XML response for items in the collection
+* If '/' is requested, a list of visible users is given, otherwise
+* a list of calendars for the user which are parented by this path.
+*/
+function get_collection_contents( $depth, $collection, $parent_path = null ) {
+  global $c, $session, $request, $reply, $property_list;
+
+  $bound_from = $collection->bound_from();
+  $bound_to   = $collection->dav_name();
+  if ( !isset($parent_path) ) $parent_path = $collection->dav_name();
+  dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s, Bound from: %s, Bound to: %s',
+                                                              $depth, $collection->dav_name(), $bound_from, $bound_to );
+
+  $date_format = iCalendar::HttpDateFormat();
+  $responses = array();
+  if ( ! $collection->IsCalendar() &&  ! $collection->IsAddressbook() ) {
+    /**
+    * Calendar/Addressbook collections may not contain collections, so we won't look
+    */
+    $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
+    if ( $bound_from == '/' ) {
+      $sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5(username || updated::text) AS dav_etag, ";
+      $sql .= "to_char(joined at time zone 'GMT',$date_format) AS created, ";
+      $sql .= "to_char(updated at time zone 'GMT',$date_format) AS modified, ";
+      $sql .= 'FALSE AS is_calendar, TRUE AS is_principal, FALSE AS is_addressbook, \'principal\' AS type, ';
+      $sql .= 'principal_id AS collection_id, ';
+      $sql .= 'principal.* ';
+      $sql .= 'FROM usr JOIN principal USING (user_no) ';
+      $sql .= "WHERE (pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN ";
+      $sql .= 'ORDER BY usr.user_no';
+    }
+    else {
+      $qry = new AwlQuery('SELECT * FROM dav_binding WHERE dav_binding.parent_container = :this_dav_name ORDER BY bind_id',
+                           array(':this_dav_name' => $bound_from));
+      if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+        while( $binding = $qry->Fetch() ) {
+          $resource = new DAVResource($binding->dav_name);
+          if ( $resource->HavePrivilegeTo('DAV::read', false) ) {
+            //if( true){
+            $resource->set_bind_location( str_replace($bound_from,$bound_to,$binding->dav_name));
+            $responses[] = $resource->RenderAsXML($property_list, $reply);
+            if ( $depth > 0 ) {
+              $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource, $binding->dav_name ) );
+            }
+          }
+        }
+      }
+
+      $sql = 'SELECT principal.*, collection.*, \'collection\' AS type ';
+      $sql .= 'FROM collection LEFT JOIN principal USING (user_no) ';
+      $sql .= 'WHERE parent_container = :this_dav_name ';
+      $sql .= "AND (path_privs(:session_principal::int8,collection.dav_name,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN ";
+      $sql .= ' ORDER BY collection_id';
+      $params[':this_dav_name'] = $bound_from;
+    }
+    $qry = new AwlQuery($sql, $params);
+
+    if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+      while( $subcollection = $qry->Fetch() ) {
+        $resource = new DAVResource($subcollection);
+        $resource->set_bind_location( str_replace($bound_from,$bound_to,$subcollection->dav_name));
+        $responses[] = $resource->RenderAsXML($property_list, $reply);
+        if ( $depth > 0 ) {
+          $responses = array_merge($responses, get_collection_contents( $depth - 1, $resource,
+                                                   str_replace($resource->parent_path(), $parent_path, $resource->dav_name() ) ) );
+        }
+      }
+    }
+
+    if ( $collection->IsPrincipal() ) {
+      // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
+      dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $bound_from );
+      $response = add_proxy_response('read', $bound_from );
+      if ( isset($response) ) $responses[] = $response;
+      $response = add_proxy_response('write', $bound_from );
+      if ( isset($response) ) $responses[] = $response;
+    }
+  }
+
+  /**
+  * freebusy permission is not allowed to see the items in a collection.  Must have at least read permission.
+  */
+  if ( $collection->HavePrivilegeTo('DAV::read', false) ) {
+     dbg_error_log('PROPFIND','Getting collection items: Depth %d, Path: %s', $depth, $bound_from );
+    $privacy_clause = ' ';
+    $time_limit_clause = ' ';
+    if ( $collection->IsCalendar() ) {
+      if ( ! $collection->HavePrivilegeTo('all', false) ) {
+        $privacy_clause = " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
+      }
+
+      if ( isset($c->hide_older_than) && intval($c->hide_older_than > 0) ) {
+        $time_limit_clause = " AND calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') ";
+      }
+    }
+/******************************
+***************** Melhorar as consultas
+*/  
+    $path = preg_replace("/^\//","",$request->path);
+    $nome = $collection->GetProperty('user_no');   
+    if ( $collection->IsCalendar() )
+    {
+      $sqlp = "SELECT cal_id FROM phpgw_cal_user WHERE  cal_type = 'u' AND cal_status != 'R' AND cal_login = :nome";
+      $qryp = new AwlQuery( $sqlp, array( ':nome' => $nome) );
+      if ( $qryp->Exec("PROPFIND",__LINE__,__FILE__) && $qryp->rows() > 0 ) {
+         while( $part = $qryp->Fetch() ) {
+             $sql = "SELECT * FROM phpgw_cal WHERE cal_id = $part->cal_id AND cal_type = 'E'";
+             $qry = new AwlQuery( $sql);
+             if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+                while( $item = $qry->Fetch() ) {
+                         $resource = new DAVResource($item);
+                         //$resource->set_bind_location( str_replace($bound_from,$bound_to,$item->owner));
+                         $resource->set_bind_location( $path."/".$item->cal_id."@".$item->owner.".ics");
+                         $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+                   }
+              }
+             $sql = "SELECT * FROM phpgw_cal INNER JOIN phpgw_cal_repeats USING(cal_id) WHERE cal_id = $part->cal_id";
+             $qry = new AwlQuery( $sql);
+             if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+               while( $item = $qry->Fetch() ) {
+                        $resource = new DAVResource($item);
+                        $resource->set_bind_location( $path."/".$item->cal_id."@".$item->owner.".ics");
+                        $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+                        }
+              }
+          } 
+       }
+    }
+   else if ( $collection->IsAddressbook()) {
+        $sql = "SELECT * FROM phpgw_cc_contact WHERE id_owner = :nome";
+        $qry = new AwlQuery( $sql,array(':nome' => $nome ));
+             if ( $qry->Exec("PROPFIND",__LINE__,__FILE__) && $qry->rows() > 0 ) {
+                while( $item = $qry->Fetch() ) {
+                         $resource = new DAVResource($item);
+                         $resource->set_bind_location(  $path."/".$item->id_contact."@".$item->id_owner.".vcf");
+                         $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+                   }
+              }   
+       
+    }
+    //else
+   //  {
+   //      dbg_error_log( "PROPFIND", "Responding with propfind error:  uUknow collection type");
+   //      $request->DoResponse( 501, 'Collection error');
+   //  }
+    //$sql = 'SELECT collection.*, principal.*, calendar_item.*, caldav_data.*, ';
+    //$sql .= "to_char(coalesce(calendar_item.created, caldav_data.created) at time zone 'GMT',$date_format) AS created, ";
+    //$sql .= "to_char(last_modified at time zone 'GMT',$date_format) AS modified, ";
+    //$sql .= 'summary AS dav_displayname ';
+    //$sql .= 'FROM caldav_data LEFT JOIN calendar_item USING( dav_id, user_no, dav_name, collection_id) ';
+    //$sql .= 'LEFT JOIN collection USING(collection_id,user_no) LEFT JOIN principal USING(user_no) ';
+    //$sql .= 'WHERE collection.dav_name = :collection_dav_name '.$time_limit_clause.' '.$privacy_clause;
+    //if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY caldav_data.dav_id";
+    //$qry = new AwlQuery( $sql, array( ':collection_dav_name' => $bound_from) );
+    //if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
+    //  while( $item = $qry->Fetch() ) {
+    //    $resource = new DAVResource($item);
+    //    $resource->set_bind_location( str_replace($bound_from,$bound_to,$item->dav_name));
+    //    $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
+    //  }
+   // }
+  }
+ 
+  return $responses;
+}
+
+
+
+/**
+* Something that we can handle, at least roughly correctly.
+*/
+$responses = array();
+if ( $request->IsProxyRequest() ) {
+  $response = add_proxy_response($request->proxy_type, $request->principal->dav_name() );
+  if ( isset($response) ) $responses[] = $response;
+}
+else {
+  $resource = new DAVResource($request->path);
+  if ( ! $resource->Exists() ) {
+    $request->PreconditionFailed( 404, 'must-exist', translate('That resource is not present on this server.') );
+  }
+  $resource->NeedPrivilege('DAV::read');
+  if ( $resource->IsCollection() ) {
+    dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name() );
+    $responses[] = $resource->RenderAsXML($property_list, $reply);
+    if ( $request->depth > 0 ) {
+      $responses = array_merge($responses, get_collection_contents( $request->depth - 1, $resource ) );
+    }
+  }
+  elseif ( $request->HavePrivilegeTo('DAV::read',false) ){ 
+    $responses[] = $resource->RenderAsXML($property_list, $reply);
+  }
+ 
+}
+$xmldoc = $reply->Render('multistatus', $responses);
+$etag = md5($xmldoc);
+header('ETag: "'.$etag.'"');
+$request->DoResponse( 207, $xmldoc, 'text/xml; charset="utf-8"' );
+
Index: contrib/davical/inc/RRule-v2.php
===================================================================
--- contrib/davical/inc/RRule-v2.php	(revision 3733)
+++ contrib/davical/inc/RRule-v2.php	(revision 3733)
@@ -0,0 +1,834 @@
+<?php
+/**
+* Class for parsing RRule and getting us the dates
+*
+* @package   awl
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+if ( !class_exists('DateTime') ) return;
+
+$rrule_expand_limit = array(
+  'YEARLY'  => array( 'bymonth' => 'expand', 'byweekno' => 'expand', 'byyearday' => 'expand', 'bymonthday' => 'expand',
+                      'byday' => 'expand', 'byhour' => 'expand', 'byminute' => 'expand', 'bysecond' => 'expand' ),
+  'MONTHLY' => array( 'bymonth' => 'limit', 'bymonthday' => 'expand',
+                      'byday' => 'expand', 'byhour' => 'expand', 'byminute' => 'expand', 'bysecond' => 'expand' ),
+  'WEEKLY'  => array( 'bymonth' => 'limit',
+                      'byday' => 'expand', 'byhour' => 'expand', 'byminute' => 'expand', 'bysecond' => 'expand' ),
+  'DAILY'   => array( 'bymonth' => 'limit', 'bymonthday' => 'limit',
+                      'byday' => 'limit', 'byhour' => 'expand', 'byminute' => 'expand', 'bysecond' => 'expand' ),
+  'HOURLY'  => array( 'bymonth' => 'limit', 'bymonthday' => 'limit',
+                      'byday' => 'limit', 'byhour' => 'limit', 'byminute' => 'expand', 'bysecond' => 'expand' ),
+  'MINUTELY'=> array( 'bymonth' => 'limit', 'bymonthday' => 'limit',
+                      'byday' => 'limit', 'byhour' => 'limit', 'byminute' => 'limit', 'bysecond' => 'expand' ),
+  'SECONDLY'=> array( 'bymonth' => 'limit', 'bymonthday' => 'limit',
+                      'byday' => 'limit', 'byhour' => 'limit', 'byminute' => 'limit', 'bysecond' => 'limit' ),
+);
+
+$rrule_day_numbers = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 );
+
+$GLOBALS['debug_rrule'] = false;
+// $GLOBALS['debug_rrule'] = true;
+
+/**
+* Wrap the DateTimeZone class to allow parsing some iCalendar TZID strangenesses
+*/
+class RepeatRuleTimeZone extends DateTimeZone {
+  private $tz_defined;
+
+  public function __construct($dtz = null) {
+    $this->tz_defined = false;
+    if ( !isset($dtz) ) return;
+
+    $dtz = olson_from_tzstring($dtz);
+
+    try {
+      parent::__construct($dtz);
+      $this->tz_defined = $dtz;
+    }
+    catch (Exception $e) {
+      $original = $dtz;
+
+      if ( !isset($dtz) ) {
+        dbg_error_log( 'ERROR', 'Could not parse timezone "%s" - will use floating time', $original );
+        $dtz = new DateTimeZone('UTC');
+        $this->tz_defined = false;
+      }
+    }
+  }
+
+  function tzid() {
+    if ( $this->tz_defined === false ) return false;
+    $tzid = $this->getName();
+    if ( $tzid != 'UTC' ) return $tzid;
+    return $this->tz_defined;
+  }
+}
+
+
+/**
+* Wrap the DateTime class to make it friendlier to passing in random strings from iCalendar
+* objects, and especially the random stuff used to identify timezones.  We also add some
+* utility methods and stuff too, in order to simplify some of the operations we need to do
+* with dates.
+*/
+class RepeatRuleDateTime extends DateTime {
+  // public static $Format = 'Y-m-d H:i:s';
+  public static $Format = 'c';
+  private $tzid;
+  private $is_date;
+
+  public function __construct($date = null, $dtz = null) {
+    $this->is_date = false;
+    if ( !isset($date) ) return;
+
+    if ( preg_match('{;?VALUE=DATE[:;]}', $date, $matches) ) $this->is_date = true;
+    elseif ( preg_match('{:([12]\d{3}) (0[1-9]|1[012]) (0[1-9]|[12]\d|3[01]Z?) $}x', $date, $matches) ) $this->is_date = true;
+    if (preg_match('/;?TZID=([^:]+).*:(\d{8}(T\d{6})?)(Z)?/', $date, $matches) ) {
+      if ( isset($matches[4]) && $matches[4] == 'Z' ) {
+        $dtz = new RepeatRuleTimeZone('UTC');
+        $this->tzid = 'UTC';
+      }
+      else if ( isset($matches[1]) && $matches[1] != '' ) {
+        $dtz = new RepeatRuleTimeZone($matches[1]);
+        $this->tzid = $dtz->tzid();
+      }
+      else {
+        $dtz = new RepeatRuleTimeZone('UTC');
+        $this->tzid = null;
+      }
+    }
+    elseif( $dtz === null || $dtz == '' ) {
+      $dtz = new RepeatRuleTimeZone('UTC');
+      if ( preg_match('/(\d{8}(T\d{6})?)Z/', $date, $matches) ) {
+        if ( strlen($matches[1]) == 8 ) $this->is_date = true;
+        $this->tzid = 'UTC';
+      }
+      else {
+        $this->tzid = null;
+      }
+    }
+    elseif ( is_string($dtz) ) {
+      $dtz = new RepeatRuleTimeZone($dtz);
+      $this->tzid = $dtz->tzid();
+    }
+    else {
+      $this->tzid = $dtz->getName();
+    }
+
+    parent::__construct($date, $dtz);
+
+    return $this;
+  }
+
+
+  public function __toString() {
+    return (string)parent::format(self::$Format) . ' ' . parent::getTimeZone()->getName();
+  }
+
+
+  public function AsDate() {
+    return $this->format('Ymd');
+  }
+
+
+  public function modify( $interval ) {
+    if ( preg_match('{^(-)?P((\d+)W)?((\d+)D)?T?((\d+)H)?((\d+)M)?((\d+)S)?$}', $interval, $matches) ) {
+      $minus = $matches[1];
+      $interval = '';
+      if ( isset($matches[2]) && $matches[2] != '' ) $interval .= $minus . $matches[3] . ' weeks ';
+      if ( isset($matches[4]) && $matches[4] != '' ) $interval .= $minus . $matches[5] . ' days ';
+      if ( isset($matches[6]) && $matches[6] != '' ) $interval .= $minus . $matches[7] . ' hours ';
+      if ( isset($matches[8]) && $matches[8] != '' ) $interval .= $minus . $matches[9] . ' minutes ';
+      if (isset($matches[10]) &&$matches[10] != '' ) $interval .= $minus . $matches[11] . ' seconds ';
+    }
+//    printf( "Modify '%s' by: >>%s<<\n", $this->__toString(), $interval );
+//    print_r($this);
+    if ( !isset($interval) || $interval == '' ) $interval = '1 day';
+    parent::modify($interval);
+    return $this->__toString();
+  }
+
+
+  public function UTC() {
+    $gmt = clone($this);
+    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+      $dtz = parent::getTimezone();
+      $offset = 0 - $dtz->getOffset($gmt);
+      $gmt->modify( $offset . ' seconds' );
+    }
+    if ( $this->is_date ) return $gmt->format('Ymd');
+    return $gmt->format('Ymd\THis\Z');
+  }
+
+
+  public function RFC5545() {
+    $result = '';
+    if ( isset($this->tzid) && $this->tzid != 'UTC' ) {
+      $result = ';TZID='.$this->tzid;
+    }
+    if ( $this->is_date ) {
+      $result .= ';VALUE=DATE:' . $this->format('Ymd');
+    }
+    else {
+      $result .= ':' . $this->format('Ymd\THis');
+      if ( isset($this->tzid) && $this->tzid == 'UTC' ) {
+        $result .= 'Z';
+      }
+    }
+    return $result;
+  }
+
+
+  public function RFC5545Duration( $end_stamp ) {
+    return sprintf( 'PT%dM', intval(($end_stamp->epoch() - $this->epoch()) / 60) );
+  }
+
+
+  public function setTimeZone( $tz ) {
+    if ( is_string($tz) ) {
+      $tz = new RepeatRuleTimeZone($tz);
+      $this->tzid = $tz->tzid();
+    }
+    parent::setTimeZone( $tz );
+    return $this;
+  }
+
+
+  function setDate( $year=null, $month=null, $day=null ) {
+    if ( !isset($year) )  $year  = parent::format('Y');
+    if ( !isset($month) ) $month = parent::format('m');
+    if ( !isset($day) )   $day   = parent::format('d');
+    parent::setDate( $year , $month , $day );
+    return $this;
+  }
+
+  function year() {
+    return parent::format('Y');
+  }
+
+  function month() {
+    return parent::format('m');
+  }
+
+  function day() {
+    return parent::format('d');
+  }
+
+  function hour() {
+    return parent::format('H');
+  }
+
+  function minute() {
+    return parent::format('i');
+  }
+
+  function second() {
+    return parent::format('s');
+  }
+
+  function epoch() {
+    return parent::format('U');
+  }
+}
+
+
+class RepeatRule {
+
+  private $base;
+  private $until;
+  private $freq;
+  private $count;
+  private $interval;
+  private $bysecond;
+  private $byminute;
+  private $byhour;
+  private $bymonthday;
+  private $byyearday;
+  private $byweekno;
+  private $byday;
+  private $bymonth;
+  private $bysetpos;
+  private $wkst;
+
+  private $instances;
+  private $position;
+  private $finished;
+  private $current_base;
+
+
+  public function __construct( $basedate, $rrule ) {
+    $this->base = ( is_object($basedate) ? $basedate : new RepeatRuleDateTime($basedate) );
+
+    if ( $GLOBALS['debug_rrule'] ) {
+      printf( "Constructing RRULE based on: '%s', rrule: '%s'\n", $basedate, $rrule );
+    }
+
+    if ( preg_match('{FREQ=([A-Z]+)(;|$)}', $rrule, $m) ) $this->freq = $m[1];
+    if ( preg_match('{UNTIL=([0-9TZ]+)(;|$)}', $rrule, $m) ) $this->until = new RepeatRuleDateTime($m[1]);
+    if ( preg_match('{COUNT=([0-9]+)(;|$)}', $rrule, $m) ) $this->count = $m[1];
+    if ( preg_match('{INTERVAL=([0-9]+)(;|$)}', $rrule, $m) ) $this->interval = $m[1];
+    if ( preg_match('{WKST=(MO|TU|WE|TH|FR|SA|SU)(;|$)}', $rrule, $m) ) $this->wkst = $m[1];
+
+    if ( preg_match('{BYDAY=(([+-]?[0-9]{0,2}(MO|TU|WE|TH|FR|SA|SU),?)+)(;|$)}', $rrule, $m) )  $this->byday = explode(',',$m[1]);
+
+    if ( preg_match('{BYYEARDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byyearday = explode(',',$m[1]);
+    if ( preg_match('{BYWEEKNO=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->byweekno = explode(',',$m[1]);
+    if ( preg_match('{BYMONTHDAY=([0-9,+-]+)(;|$)}', $rrule, $m) ) $this->bymonthday = explode(',',$m[1]);
+    if ( preg_match('{BYMONTH=(([+-]?[0-1]?[0-9],?)+)(;|$)}', $rrule, $m) ) $this->bymonth = explode(',',$m[1]);
+    if ( preg_match('{BYSETPOS=(([+-]?[0-9]{1,3},?)+)(;|$)}', $rrule, $m) ) $this->bysetpos = explode(',',$m[1]);
+
+    if ( preg_match('{BYSECOND=([0-9,]+)(;|$)}', $rrule, $m) ) $this->bysecond = explode(',',$m[1]);
+    if ( preg_match('{BYMINUTE=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byminute = explode(',',$m[1]);
+    if ( preg_match('{BYHOUR=([0-9,]+)(;|$)}', $rrule, $m) ) $this->byhour = explode(',',$m[1]);
+
+    if ( !isset($this->interval) ) $this->interval = 1;
+    switch( $this->freq ) {
+      case 'SECONDLY': $this->freq_name = 'second'; break;
+      case 'MINUTELY': $this->freq_name = 'minute'; break;
+      case 'HOURLY':   $this->freq_name = 'hour';   break;
+      case 'DAILY':    $this->freq_name = 'day';    break;
+      case 'WEEKLY':   $this->freq_name = 'week';   break;
+      case 'MONTHLY':  $this->freq_name = 'month';  break;
+      case 'YEARLY':   $this->freq_name = 'year';   break;
+      default:
+        /** need to handle the error, but FREQ is mandatory so unlikely */
+    }
+    $this->frequency_string = sprintf('+%d %s', $this->interval, $this->freq_name );
+    if ( $GLOBALS['debug_rrule'] ) printf( "Frequency modify string is: '%s', base is: '%s'\n", $this->frequency_string, $this->base->format('c') );
+    $this->Start();
+  }
+
+
+  public function set_timezone( $tzstring ) {
+    $this->base->setTimezone(new DateTimeZone($tzstring));
+  }
+
+
+  public function Start() {
+    $this->instances = array();
+    $this->GetMoreInstances();
+    $this->rewind();
+    $this->finished = false;
+  }
+
+
+  public function rewind() {
+    $this->position = -1;
+  }
+
+
+  public function next() {
+    $this->position++;
+    return $this->current();
+  }
+
+
+  public function current() {
+    if ( !$this->valid() ) return null;
+    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+    if ( !$this->valid() ) return null;
+    if ( $GLOBALS['debug_rrule'] ) printf( "Returning date from position %d: %s (%s)\n", $this->position, $this->instances[$this->position]->format('c'), $this->instances[$this->position]->UTC() );
+    return $this->instances[$this->position];
+  }
+
+
+  public function key() {
+    if ( !$this->valid() ) return null;
+    if ( !isset($this->instances[$this->position]) ) $this->GetMoreInstances();
+    if ( !isset($this->keys[$this->position]) ) {
+      $this->keys[$this->position] = $this->instances[$this->position];
+    }
+    return $this->keys[$this->position];
+  }
+
+
+  public function valid() {
+    if ( isset($this->instances[$this->position]) || !$this->finished ) return true;
+    return false;
+  }
+
+
+  private function GetMoreInstances() {
+    global $rrule_expand_limit;
+
+    if ( $this->finished ) return;
+    $got_more = false;
+    $loop_limit = 10;
+    $loops = 0;
+    while( !$this->finished && !$got_more && $loops++ < $loop_limit ) {
+      if ( !isset($this->current_base) ) {
+        $this->current_base = clone($this->base);
+      }
+      else {
+        $this->current_base->modify( $this->frequency_string );
+      }
+      if ( $GLOBALS['debug_rrule'] ) printf( "Getting more instances from: '%s' - %d\n", $this->current_base->format('c'), count($this->instances) );
+      $this->current_set = array( clone($this->current_base) );
+      foreach( $rrule_expand_limit[$this->freq] AS $bytype => $action ) {
+        if ( isset($this->{$bytype}) ) $this->{$action.'_'.$bytype}();
+        if ( !isset($this->current_set[0]) ) break;
+      }
+      sort($this->current_set);
+      if ( isset($this->bysetpos) ) $this->limit_bysetpos();
+
+      $position = count($this->instances) - 1;
+      if ( $GLOBALS['debug_rrule'] ) printf( "Inserting %d from current_set into position %d\n", count($this->current_set), $position + 1 );
+      foreach( $this->current_set AS $k => $instance ) {
+        if ( $instance < $this->base ) continue;
+        if ( isset($this->until) && $instance > $this->until ) {
+          $this->finished = true;
+          return;
+        }
+        if ( !isset($this->instances[$position]) || $instance != $this->instances[$position] ) {
+          $got_more = true;
+          $position++;
+          $this->instances[$position] = $instance;
+          if ( $GLOBALS['debug_rrule'] ) printf( "Added date %s into position %d in current set\n", $instance->format('c'), $position );
+          if ( isset($this->count) && ($position + 1) >= $this->count ) $this->finished = true;
+        }
+      }
+    }
+  }
+
+
+  static public function date_mask( $date, $y, $mo, $d, $h, $mi, $s ) {
+    $date_parts = explode(',',$date->format('Y,m,d,H,i,s'));
+
+    $tz = $date->getTimezone();
+    if ( isset($y) || isset($mo) || isset($d) ) {
+      if ( isset($y) ) $date_parts[0] = $y;
+      if ( isset($mo) ) $date_parts[1] = $mo;
+      if ( isset($d) ) $date_parts[2] = $d;
+      $date->setDate( $date_parts[0], $date_parts[1], $date_parts[2] );
+    }
+    if ( isset($h) || isset($mi) || isset($s) ) {
+      if ( isset($h) ) $date_parts[3] = $h;
+      if ( isset($mi) ) $date_parts[4] = $mi;
+      if ( isset($s) ) $date_parts[5] = $s;
+      $date->setTime( $date_parts[3], $date_parts[4], $date_parts[5] );
+    }
+    return $date;
+  }
+
+
+  private function expand_bymonth() {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->bymonth AS $k => $month ) {
+        $expanded = $this->date_mask( clone($instance), null, $month, null, null, null, null);
+        if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYMONTH $month into date %s\n", $expanded->format('c') );
+        $this->current_set[] = $expanded;
+      }
+    }
+  }
+
+  private function expand_bymonthday() {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->bymonthday AS $k => $monthday ) {
+        $this->current_set[] = $this->date_mask( clone($instance), null, null, $monthday, null, null, null);
+      }
+    }
+  }
+
+
+  private function expand_byday_in_week( $day_in_week ) {
+    global $rrule_day_numbers;
+
+    /**
+    * @TODO: This should really allow for WKST, since if we start a series
+    * on (eg.) TH and interval > 1, a MO, TU, FR repeat will not be in the
+    * same week with this code.
+    */
+    $dow_of_instance = $day_in_week->format('w'); // 0 == Sunday
+    foreach( $this->byday AS $k => $weekday ) {
+      $dow = $rrule_day_numbers[$weekday];
+      $offset = $dow - $dow_of_instance;
+      if ( $offset < 0 ) $offset += 7;
+      $expanded = clone($day_in_week);
+      $expanded->modify( sprintf('+%d day', $offset) );
+      $this->current_set[] = $expanded;
+      if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(W) $weekday into date %s\n", $expanded->format('c') );
+    }
+  }
+
+
+  private function expand_byday_in_month( $day_in_month ) {
+    global $rrule_day_numbers;
+
+    $first_of_month = $this->date_mask( clone($day_in_month), null, null, 1, null, null, null);
+    $dow_of_first = $first_of_month->format('w'); // 0 == Sunday
+    $days_in_month = cal_days_in_month(CAL_GREGORIAN, $first_of_month->format('m'), $first_of_month->format('Y'));
+    foreach( $this->byday AS $k => $weekday ) {
+      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+        $dow = $rrule_day_numbers[$matches[3]];
+        $first_dom = 1 + $dow - $dow_of_first;  if ( $first_dom < 1 ) $first_dom +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+        $whichweek = intval($matches[2]);
+        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(M) $weekday in month of %s\n", $instance->format('c') );
+        if ( $whichweek > 0 ) {
+          $whichweek--;
+          $monthday = $first_dom;
+          if ( $matches[1] == '-' ) {
+            $monthday += 35;
+            while( $monthday > $days_in_month ) $monthday -= 7;
+            $monthday -= (7 * $whichweek);
+          }
+          else {
+            $monthday += (7 * $whichweek);
+          }
+          if ( $monthday > 0 && $monthday <= $days_in_month ) {
+            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+            $this->current_set[] = $expanded;
+          }
+        }
+        else {
+          for( $monthday = $first_dom; $monthday <= $days_in_month; $monthday += 7 ) {
+            $expanded = $this->date_mask( clone($day_in_month), null, null, $monthday, null, null, null);
+            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(M) $weekday now $monthday into date %s\n", $expanded->format('c') );
+            $this->current_set[] = $expanded;
+          }
+        }
+      }
+    }
+  }
+
+
+  private function expand_byday_in_year( $day_in_year ) {
+    global $rrule_day_numbers;
+
+    $first_of_year = $this->date_mask( clone($day_in_year), null, 1, 1, null, null, null);
+    $dow_of_first = $first_of_year->format('w'); // 0 == Sunday
+    $days_in_year = 337 + cal_days_in_month(CAL_GREGORIAN, 2, $first_of_year->format('Y'));
+    foreach( $this->byday AS $k => $weekday ) {
+      if ( preg_match('{([+-])?(\d)?(MO|TU|WE|TH|FR|SA|SU)}', $weekday, $matches ) ) {
+        $expanded = clone($first_of_year);
+        $dow = $rrule_day_numbers[$matches[3]];
+        $first_doy = 1 + $dow - $dow_of_first;  if ( $first_doy < 1 ) $first_doy +=7;  // e.g. 1st=WE, dow=MO => 1+1-3=-1 => MO is 6th, etc.
+        $whichweek = intval($matches[2]);
+        if ( $GLOBALS['debug_rrule'] ) printf( "Expanding BYDAY(Y) $weekday from date %s\n", $instance->format('c') );
+        if ( $whichweek > 0 ) {
+          $whichweek--;
+          $yearday = $first_doy;
+          if ( $matches[1] == '-' ) {
+            $yearday += 371;
+            while( $yearday > $days_in_year ) $yearday -= 7;
+            $yearday -= (7 * $whichweek);
+          }
+          else {
+            $yearday += (7 * $whichweek);
+          }
+          if ( $yearday > 0 && $yearday <= $days_in_year ) {
+            $expanded->modify(sprintf('+%d day', $yearday - 1));
+            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+            $this->current_set[] = $expanded;
+          }
+        }
+        else {
+          $expanded->modify(sprintf('+%d day', $first_doy - 1));
+          for( $yearday = $first_doy; $yearday <= $days_in_year; $yearday += 7 ) {
+            if ( $GLOBALS['debug_rrule'] ) printf( "Expanded BYDAY(Y) $weekday now $yearday into date %s\n", $expanded->format('c') );
+            $this->current_set[] = clone($expanded);
+            $expanded->modify('+1 week');
+          }
+        }
+      }
+    }
+  }
+
+
+  private function expand_byday() {
+    if ( !isset($this->current_set[0]) ) return;
+    if ( $this->freq == 'MONTHLY' || $this->freq == 'YEARLY' ) {
+      if ( isset($this->bymonthday) || isset($this->byyearday) ) {
+        $this->limit_byday();  /** Per RFC5545 3.3.10 from note 1&2 to table */
+        return;
+      }
+    }
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      if ( $this->freq == 'MONTHLY' ) {
+        $this->expand_byday_in_month($instance);
+      }
+      else if ( $this->freq == 'WEEKLY' ) {
+        $this->expand_byday_in_week($instance);
+      }
+      else { // YEARLY
+        if ( isset($this->bymonth) ) {
+          $this->expand_byday_in_month($instance);
+        }
+        else if ( isset($this->byweekno) ) {
+          $this->expand_byday_in_week($instance);
+        }
+        else {
+          $this->expand_byday_in_year($instance);
+        }
+      }
+
+    }
+  }
+
+  private function expand_byhour() {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->bymonth AS $k => $month ) {
+        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, $hour, null, null);
+      }
+    }
+  }
+
+  private function expand_byminute() {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->bymonth AS $k => $month ) {
+        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, $minute, null);
+      }
+    }
+  }
+
+  private function expand_bysecond() {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->bymonth AS $k => $second ) {
+        $this->current_set[] = $this->date_mask( clone($instance), null, null, null, null, null, $second);
+      }
+    }
+  }
+
+
+  private function limit_generally( $fmt_char, $element_name ) {
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $instances AS $k => $instance ) {
+      foreach( $this->{$element_name} AS $k => $element_value ) {
+        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' ? %s\n", $instance->format('c'), $instance->format($fmt_char), $element_value, ($instance->format($fmt_char) == $element_value ? 'Yes' : 'No') );
+        if ( $instance->format($fmt_char) == $element_value ) $this->current_set[] = $instance;
+      }
+    }
+  }
+
+  private function limit_byday() {
+    global $rrule_day_numbers;
+
+    $fmt_char = 'w';
+    $instances = $this->current_set;
+    $this->current_set = array();
+    foreach( $this->byday AS $k => $weekday ) {
+      $dow = $rrule_day_numbers[$weekday];
+      foreach( $instances AS $k => $instance ) {
+        if ( $GLOBALS['debug_rrule'] ) printf( "Limiting '$fmt_char' on '%s' => '%s' ?=? '%s' (%d) ? %s\n", $instance->format('c'), $instance->format($fmt_char), $weekday, $dow, ($instance->format($fmt_char) == $dow ? 'Yes' : 'No') );
+        if ( $instance->format($fmt_char) == $dow ) $this->current_set[] = $instance;
+      }
+    }
+  }
+
+  private function limit_bymonth()    {   $this->limit_generally( 'm', 'bymonth' );     }
+  private function limit_byyearday()  {   $this->limit_generally( 'z', 'byyearday' );   }
+  private function limit_bymonthday() {   $this->limit_generally( 'd', 'bymonthday' );  }
+  private function limit_byhour()     {   $this->limit_generally( 'H', 'byhour' );      }
+  private function limit_byminute()   {   $this->limit_generally( 'i', 'byminute' );    }
+  private function limit_bysecond()   {   $this->limit_generally( 's', 'bysecond' );    }
+
+
+  private function limit_bysetpos( ) {
+    $instances = $this->current_set;
+    $count = count($instances);
+    $this->current_set = array();
+    foreach( $this->bysetpos AS $k => $element_value ) {
+      if ( $GLOBALS['debug_rrule'] ) printf( "Limiting bysetpos %s of %d instances\n", $element_value, $count );
+      if ( $element_value > 0 ) {
+        $this->current_set[] = $instances[$element_value - 1];
+      }
+      else if ( $element_value < 0 ) {
+        $this->current_set[] = $instances[$count + $element_value];
+      }
+    }
+  }
+
+
+}
+
+
+require_once("vComponent.php");
+
+/**
+* Expand the event instances for an RDATE or EXDATE property
+*
+* @param string $property RDATE or EXDATE, depending...
+* @param array $component A vComponent which is a VEVENT, VTODO or VJOURNAL
+* @param array $range_end A date after which we care less about expansion
+*
+* @return array An array keyed on the UTC dates, referring to the component
+*/
+function rdate_expand( $dtstart, $property, $component, $range_end = null ) {
+  $properties = $component->GetProperties($property);
+  $expansion = array();
+  foreach( $properties AS $p ) {
+    $timezone = $p->GetParameterValue('TZID');
+    $rdate = $p->Value();
+    $rdates = explode( ',', $rdate );
+    foreach( $rdates AS $k => $v ) {
+      $rdate = new RepeatRuleDateTime( $v, $timezone);
+      $expansion[$rdate->UTC()] = $component;
+      if ( $rdate > $range_end ) break;
+    }
+  }
+  return $expansion;
+}
+
+
+/**
+* Expand the event instances for an RRULE property
+*
+* @param object $dtstart A RepeatRuleDateTime which is the master dtstart
+* @param string $property RDATE or EXDATE, depending...
+* @param array $component A vComponent which is a VEVENT, VTODO or VJOURNAL
+* @param array $range_end A date after which we care less about expansion
+*
+* @return array An array keyed on the UTC dates, referring to the component
+*/
+function rrule_expand( $dtstart, $property, $component, $range_end ) {
+  $expansion = array();
+
+  $recur = $component->GetProperty($property);
+  if ( !isset($recur) ) return $expansion;
+  $recur = $recur->Value();
+
+  $this_start = $component->GetProperty('DTSTART');
+  if ( isset($this_start) ) {
+    $timezone = $this_start->GetParameterValue('TZID');
+    $this_start = new RepeatRuleDateTime($this_start->Value(),$timezone);
+  }
+  else {
+    $this_start = clone($dtstart);
+  }
+
+//  print_r( $this_start );
+//  printf( "RRULE: %s\n", $recur );
+  $rule = new RepeatRule( $this_start, $recur );
+  $i = 0;
+  $result_limit = 1000;
+  while( $date = $rule->next() ) {
+//    printf( "[%3d] %s\n", $i, $date->UTC() );
+    $expansion[$date->UTC()] = $component;
+    if ( $i++ >= $result_limit || $date > $range_end ) break;
+  }
+//  print_r( $expansion );
+  return $expansion;
+}
+
+
+/**
+* Expand the event instances for an iCalendar VEVENT (or VTODO)
+*
+* @param object $vResource A vComponent which is a VCALENDAR containing components needing expansion
+* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
+* @param object $range_end A RepeatRuleDateTime which is the end of the range for events, default +6 weeks
+*
+* @return vComponent The original vComponent, with the instances of the internal components expanded.
+*/
+function expand_event_instances( $vResource, $range_start = null, $range_end = null ) {
+  $components = $vResource->GetComponents();
+
+  if ( !isset($range_start) ) { $range_start = new RepeatRuleDateTime(); $range_start->modify('-6 weeks'); }
+  if ( !isset($range_end) )   { $range_end   = clone($range_start);      $range_end->modify('+6 months');  }
+
+  $new_components = array();
+  $result_limit = 1000;
+  $instances = array();
+  $expand = false;
+  $dtstart = null;
+  foreach( $components AS $k => $comp ) {
+    if ( $comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL' ) {
+      $new_components[] = $comp;
+      continue;
+    }
+    if ( !isset($dtstart) ) {
+      $dtstart = $comp->GetProperty('DTSTART');
+      $tzid = $dtstart->GetParameterValue('TZID');
+      $dtstart = new RepeatRuleDateTime( $dtstart->Value(), $tzid );
+      $instances[$dtstart->UTC()] = $comp;
+    }
+    $p = $comp->GetProperty('RECURRENCE-ID');
+    if ( isset($p) && $p->Value() != '' ) {
+      $range = $p->GetParameterValue('RANGE');
+      $recur_tzid = $p->GetParameterValue('TZID');
+      $recur_utc = new RepeatRuleDateTime($p->Value(),$recur_tzid);
+      $recur_utc = $recur_utc->UTC();
+      if ( isset($range) && $range == 'THISANDFUTURE' ) {
+        foreach( $instances AS $k => $v ) {
+          if ( $k >= $recur_utc ) unset($instances[$k]);
+        }
+      }
+      else {
+        unset($instances[$recur_utc]);
+      }
+      $instances[] = $comp;
+    }
+    $instances = array_merge( $instances, rrule_expand($dtstart, 'RRULE', $comp, $range_end) );
+    $instances = array_merge( $instances, rdate_expand($dtstart, 'RDATE', $comp, $range_end) );
+    foreach ( rdate_expand($dtstart, 'EXDATE', $comp, $range_end) AS $k => $v ) {
+      unset($instances[$k]);
+    }
+  }
+
+  $last_duration = null;
+  $in_range = false;
+  $new_components = array();
+  $start_utc = $range_start->UTC();
+  $end_utc = $range_end->UTC();
+  foreach( $instances AS $utc => $comp ) {
+    if ( $utc > $end_utc ) break;
+
+    $end_type = ($comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND');
+    $duration = $comp->GetProperty('DURATION');
+    if ( !isset($duration) || $duration->Value() == '' ) {
+      $instance_start = $comp->GetProperty('DTSTART');
+      $dtsrt = new RepeatRuleDateTime( $instance_start->Value(), $instance_start->GetParameterValue('TZID'));
+      $instance_end = $comp->GetProperty($end_type);
+      $dtend = new RepeatRuleDateTime( $instance_end->Value(), $instance_end->GetParameterValue('TZID'));
+      $duration = $dtstart->RFC5545Duration( $dtend );
+    }
+    else {
+      $duration = $duration->Value();
+    }
+
+    if ( $utc < $start_utc ) {
+      if ( isset($last_duration) && $duration == $last_duration) {
+        if ( $utc < $early_start ) continue;
+      }
+      else {
+        $latest_start = clone($range_start);
+        $latest_start->modify('-'.$duration);
+        $early_start = $latest_start->UTC();
+        $last_duration = $duration;
+        if ( $utc < $early_start ) continue;
+      }
+    }
+    $component = clone($comp);
+    $component->ClearProperties( array('DTSTART'=> true, 'DUE' => true, 'DTEND' => true) );
+    $component->AddProperty('DTSTART', $utc );
+    $component->AddProperty('DURATION', $duration );
+    $new_components[] = $component;
+    $in_range = true;
+  }
+
+  if ( $in_range ) {
+    $vResource->SetComponents($new_components);
+  }
+  else {
+    $vResource->SetComponents(array());
+  }
+
+  return $vResource;
+}
Index: contrib/davical/inc/drivers_ldap.php
===================================================================
--- contrib/davical/inc/drivers_ldap.php	(revision 3733)
+++ contrib/davical/inc/drivers_ldap.php	(revision 3733)
@@ -0,0 +1,603 @@
+<?php
+/**
+* Manages LDAP repository connection
+*
+* @package   davical
+* @category Technical
+* @subpackage   ldap
+* @author    Maxime Delorme <mdelorme@tennaxia.net>
+* @copyright Maxime Delorme
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+
+require_once("auth-functions.php");
+
+class ldapDrivers
+{
+  /**#@+
+  * @access private
+  */
+
+  /**
+  * Holds the LDAP connection parameters
+  */
+  var $connect;
+
+  /**#@-*/
+
+
+  /**
+  * Constructor.
+  * @param array $config The configuration data
+  */
+  function ldapDrivers($config){
+      $this->__construct($config);
+  }
+
+
+  /**
+  * Initializes the LDAP connection
+  *
+  * @param array $config The configuration data
+  */
+  function __construct($config)
+  {
+      global $c;
+      $host=$config['host'];
+      $port=$config['port'];
+      if(!function_exists('ldap_connect')){
+          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+          $this->valid=false;
+          return ;
+      }
+
+      //Set LDAP protocol version
+      if (isset($config['protocolVersion']))
+          ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
+      if (isset($config['optReferrals']))
+          ldap_set_option($this->connect, LDAP_OPT_REFERRALS, $config['optReferrals']);
+
+      if ($port)
+          $this->connect=ldap_connect($host, $port);
+      else
+          $this->connect=ldap_connect($host);
+
+      if (! $this->connect){
+          $c->messages[] = sprintf(translate( 'drivers_ldap : Unable to connect to LDAP with port %s on host %s'), $port, $host );
+          $this->valid=false;
+          return ;
+      }
+
+      dbg_error_log( "LDAP", "drivers_ldap : Connected to LDAP server %s",$host );
+
+      // Start TLS if desired (requires protocol version 3)
+      if (isset($config['startTLS'])) {
+        if (!ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+          $c->messages[] = i18n('drivers_ldap : Failed to set LDAP to use protocol version 3, TLS not supported');
+          $this->valid=false;
+          return;
+        }
+        if (!ldap_start_tls($this->connect)) {
+          $c->messages[] = i18n('drivers_ldap : Could not start TLS: ldap_start_tls() failed');
+          $this->valid=false;
+          return;
+        }
+      }
+
+      //Set the search scope to be used, default to subtree.  This sets the functions to be called later.
+      if (!isset($config['scope'])) $config['scope'] = 'subtree';
+      switch (strtolower($config['scope'])) {
+      case "base":
+        $this->ldap_query_one = 'ldap_read';
+        $this->ldap_query_all = 'ldap_read';
+        break;
+      case "onelevel":
+        $this->ldap_query_one = 'ldap_list';
+        $this->ldap_query_all = 'ldap_search';
+        break;
+      default:
+        $this->ldap_query_one = 'ldap_search';
+        $this->ldap_query_all = 'ldap_search';
+        break;
+      }
+
+      //connect as root
+      if (!ldap_bind($this->connect, (isset($config['bindDN']) ? $config['bindDN'] : null), (isset($config['passDN']) ? $config['passDN'] : null) ) ){
+          $bindDN = isset($config['bindDN']) ? $config['bindDN'] : 'anonymous';
+          $passDN = isset($config['passDN']) ? $config['passDN'] : 'anonymous';
+          dbg_error_log( "LDAP", i18n('drivers_ldap : Failed to bind to host %1$s on port %2$s with bindDN of %3$s'), $host, $port, $bindDN );
+          $c->messages[] = i18n( 'drivers_ldap : Unable to bind to LDAP - check your configuration for bindDN and passDN, and that your LDAP server is reachable');
+          $this->valid=false;
+          return ;
+      }
+      $this->valid = true;
+      //root to start search
+      $this->baseDNUsers  = is_string($config['baseDNUsers']) ? array($config['baseDNUsers']) : $config['baseDNUsers'];
+      $this->filterUsers  = (isset($config['filterUsers'])  ? $config['filterUsers']  : null);
+      $this->baseDNGroups = is_string($config['baseDNGroups']) ? array($config['baseDNGroups']) : $config['baseDNGroups'];
+      $this->filterGroups = (isset($config['filterGroups']) ? $config['filterGroups'] : null);
+  }
+
+/**                                                                                                                                                                       
+ * retorna atributo especifico utilizando determinado filtro                                                                                                              
+ *                                                                                                                                                                        
+ * @param string $filter The filter used to search entries                                                                                                                
+ * @param array $attributes Attributes to be returned                                                                                                                     
+ *                                                                                                                                                                        
+ * feito por Gabriel Gomes Malheiros                                                                                                                                      
+ * 15/07/2008                                                                                                                                                             
+ *                                                                                                                                                                        
+*/                                                                                                                                                                        
+
+function requestAtributo( $filter, $attributes=NULL) {
+                                                      
+    global $c;                                        
+    $entry=NULL;                                      
+    // We get the DN of the USER                      
+    $host=$c->authenticate_hook['config']['host'];    
+    $port=$c->authenticate_hook['config']['port'];    
+      if(!function_exists('ldap_connect')){           
+          $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
+          $this->valid=false;                                                                                   
+          return ;                                                                                              
+      }                                                                                                         
+      if ($port)                                                                                                
+          $connect=ldap_connect($host, $port);                                                                  
+      else                                                                                                      
+          $connect=ldap_connect($host);                                                                         
+
+      if (! $connect){
+          $c->messages[] = sprintf(i18n( "drivers_ldap : Unable to connect to LDAP with port %s on host %s"), $port,$host );
+          $this->valid=false;                                                                                               
+          return ;                                                                                                          
+      }                                                                                                                     
+    $baseDNUsers  = $c->authenticate_hook['config']['baseDNUsers'];                                                         
+                                                                                                                            
+    $entry = ldap_search($connect, $baseDNUsers, $filter,$attributes);                                                      
+    if ( !ldap_first_entry($connect, $entry) ){                                                                             
+      dbg_error_log( "ERROR", "drivers_ldap : Unable to find the user with filter %s",$filter );                            
+      return false;                                                                                                         
+    } else {                                                                                                                
+      dbg_error_log( "LDAP", "drivers_ldap : Found a user using filter %s",$filter );                                       
+      $i = ldap_first_entry($connect,$entry);                                                                               
+     $arr = ldap_get_attributes($connect,$i);                                                                               
+      for( $i=0; $i<$arr['count']; $i++ ) {                                                                                 
+        $ret[$arr[$i]]=$arr[$arr[$i]][0];                                                                                   
+     }                                                                                                                      
+    return $ret;                                                                                                            
+   }                                                                                                                        
+                                                                                                                            
+}                
+  /**
+  * Retrieve all groups from the LDAP directory
+  */
+  function getAllGroups($attributes){
+    global $c;
+
+    $query = $this->ldap_query_all;
+
+    foreach($this->baseDNGroups as $baseDNGroups) {
+      $entry = $query($this->connect,$baseDNGroups,$this->filterGroups,$attributes);
+
+      if (!ldap_first_entry($this->connect,$entry)) {
+        $c->messages[] = sprintf(translate('Error NoGroupFound with filter >%s<, attributes >%s< , dn >%s<'),
+                                 $this->filterGroups,
+                                 join(', ', $attributes),
+                                 $baseDNGroups);
+      }
+      $row = array();
+      for($i = ldap_first_entry($this->connect,$entry);
+          $i && $arr = ldap_get_attributes($this->connect,$i);
+          $i = ldap_next_entry($this->connect,$i) ) {
+        for ($j=0; $j < $arr['count']; $j++) {
+          $row[$arr[$j]] = count($arr[$arr[$j]])>2?$arr[$arr[$j]]:$arr[$arr[$j]][0];
+        }
+        $ret[]=$row;
+      }
+    }
+    return $ret;
+  }
+
+  /**
+    * Returns the result of the LDAP query
+    *
+    * @param string $filter The filter used to search entries
+    * @param array $attributes Attributes to be returned
+    * @param string $passwd password to check
+    * @return array Contains selected attributes from all entries corresponding to the given filter
+    */
+  function requestUser( $filter, $attributes=NULL, $username, $passwd) {
+    global $c;
+
+    $entry=NULL;
+    // We get the DN of the USER
+    $query = $this->ldap_query_one;
+
+    foreach($this->baseDNUsers as $baseDNUsers) {
+      $entry = $query($this->connect, $baseDNUsers, $filter, $attributes);
+
+      if (ldap_first_entry($this->connect,$entry) )
+        break;
+
+      dbg_error_log( "LDAP", "drivers_ldap : Failed to find user with baseDN: %s", $baseDNUsers );
+    }
+
+    if ( !ldap_first_entry($this->connect, $entry) ){
+      dbg_error_log( "ERROR", "drivers_ldap : Unable to find the user with filter %s",$filter );
+      return false;
+    } else {
+      dbg_error_log( "LDAP", "drivers_ldap : Found a user using filter %s",$filter );
+    }
+
+    $dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
+
+    if ( isset($c->authenticate_hook['config']['i_use_mode_kerberos']) && $c->authenticate_hook['config']['i_use_mode_kerberos'] == "i_know_what_i_am_doing") {
+    	dbg_error_log( "LOG", "drivers_ldap : Skipping password Check for user %s which should be the same as %s",$username , $_SERVER["REMOTE_USER"]);
+      if ($username != $_SERVER["REMOTE_USER"]) {
+        return false;
+      }
+    } else {
+      if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
+        dbg_error_log( "LDAP", "drivers_ldap : Failed to bind to user %s using password %s", $dnUser, $passwd );
+        return false;
+      }
+    }
+
+
+    dbg_error_log( "LDAP", "drivers_ldap : Bound to user %s using password %s", $dnUser, $passwd );
+
+    $i = ldap_first_entry($this->connect,$entry);
+    $arr = ldap_get_attributes($this->connect,$i);
+    for( $i=0; $i<$arr['count']; $i++ ) {
+      $ret[$arr[$i]]=$arr[$arr[$i]][0];
+    }
+    return $ret;
+
+  }
+}
+
+
+/**
+* A generic function to create and fetch static objects
+*/
+function getStaticLdap() {
+  global $c;
+  // Declare a static variable to hold the object instance
+  static $instance;
+
+  // If the instance is not there, create one
+  if(!isset($instance)) {
+    $ldapDrivers =& new ldapDrivers($c->authenticate_hook['config']);
+  }
+  return $ldapDrivers;
+}
+
+
+/**
+* Synchronise a cached user with one from LDAP
+* @param object $usr A user record to be updated (or created)
+*/
+function sync_user_from_LDAP( &$usr, $mapping, $ldap_values ) {
+  global $c;
+
+  dbg_error_log( "LDAP", "Going to sync the user from LDAP" );
+  $validUserFields = get_fields('usr');
+
+  if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+    foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+      if ( isset($validUserFields[$field]) ) {
+        $usr->{$field} =  $value;
+        dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+      }
+    }
+  }
+
+  foreach ( $mapping as $field => $value ) {
+    dbg_error_log( "LDAP", "Considering copying %s", $field );
+    if ( isset($validUserFields[$field]) ) {
+      $usr->{$field} =  $ldap_values[$value];
+      dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+    }
+  }
+
+  UpdateUserFromExternal( $usr );
+}
+
+
+/**
+* Check the username / password against the LDAP server
+*/
+function LDAP_check($username, $password ){
+  global $c;
+
+  $ldapDriver = getStaticLdap();
+  if ( !$ldapDriver->valid ) {
+    dbg_error_log( "ERROR", "Couldn't contact LDAP server for authentication" );
+    return false;
+  }
+
+  $mapping = $c->authenticate_hook['config']['mapping_field'];
+  $attributes = array_values($mapping);
+
+  /**
+  * If the config contains a filter that starts with a ( then believe
+  * them and don't modify it, otherwise wrap the filter.
+  */
+  $filter_munge = "";
+  if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
+    $filter_munge = $ldapDriver->filterUsers;
+  }
+  else if ( isset($ldapDriver->filterUsers) && $ldapDriver->filterUsers != '' ) {
+    $filter_munge = "($ldapDriver->filterUsers)";
+  }
+
+  $filter = "(&$filter_munge(".$mapping["username"]."=$username))";
+  $valid = $ldapDriver->requestUser( $filter, $attributes, $username, $password );
+
+  // is a valid user or not
+  if ( !$valid ) {
+    dbg_error_log( "LDAP", "user %s is not a valid user",$username );
+    return false;
+  }
+
+  $ldap_timestamp = $valid[$mapping["updated"]];
+
+  /**
+  * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
+  */
+  foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+    $$k = substr($ldap_timestamp,$v[0],$v[1]);
+
+  $ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
+  $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+
+  if ( $usr = getUserByName($username) ) {
+    // should we update it ?
+    $db_timestamp = $usr->updated;
+    $db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
+    if($ldap_timestamp <= $db_timestamp) {
+        return $usr; // no need to update
+    }
+    // we will need to update the user record
+  }
+  else {
+    dbg_error_log( "LDAP", "user %s doesn't exist in local DB, we need to create it",$username );
+    $usr = (object) array( 'user_no' => 0 );
+  }
+
+  // The local cached user doesn't exist, or is older, so we create/update their details
+  sync_user_from_LDAP($usr, $mapping, $valid );
+
+  return $usr;
+
+}
+
+/**
+* sync LDAP Groups against the DB
+*/
+function sync_LDAP_groups(){
+  global $c;
+  $ldapDriver = getStaticLdap();
+  if($ldapDriver->valid){
+    $mapping = $c->authenticate_hook['config']['group_mapping_field'];
+    //$attributes = array('cn','modifyTimestamp','memberUid');
+    $attributes = array_values($mapping);
+    $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes);
+
+    if ( sizeof($ldap_groups_tmp) == 0 )
+      return;
+
+    foreach($ldap_groups_tmp as $key => $ldap_group){
+      $ldap_groups_info[$ldap_group[$mapping['username']]] = $ldap_group;
+      if (is_array($ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']])) {
+      unset ( $ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']]['count'] );
+      }
+      else {
+          $ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']] = array($ldap_groups_info[$ldap_group[$mapping['username']]][$mapping['members']]);
+      }
+      unset($ldap_groups_tmp[$key]);
+    }
+    $db_groups = array ();
+    $db_group_members = array ();
+    $qry = new AwlQuery( "SELECT g.username AS group_name, member.username AS member_name FROM dav_principal g JOIN group_member ON (g.principal_id=group_member.group_id) JOIN dav_principal member  ON (member.principal_id=group_member.member_id) WHERE g.type_id = 3");
+    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+    while($db_group = $qry->Fetch()) {
+      $db_groups[$db_group->group_name] = $db_group->group_name;
+      $db_group_members[$db_group->group_name][] = $db_group->member_name;
+    }
+
+    $ldap_groups = array_keys($ldap_groups_info);
+    // users only in ldap
+    $groups_to_create = array_diff($ldap_groups,$db_groups);
+    // users only in db
+    $groups_to_deactivate = array_diff($db_groups,$ldap_groups);
+    // users present in ldap and in the db
+    $groups_to_update = array_intersect($db_groups,$ldap_groups);
+
+    if ( sizeof ( $groups_to_create ) ){
+      $c->messages[] = sprintf(i18n('- creating groups : %s'),join(', ',$groups_to_create));
+      $validUserFields = get_fields('usr');
+      foreach ( $groups_to_create as $k => $group ){
+        $user = (object) array( 'user_no' => 0, 'username' => '' );
+
+        if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
+          foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
+            if ( isset($validUserFields[$field]) ) {
+              $usr->{$field} =  $value;
+              dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
+            }
+          }
+        }
+        $user->user_no = 0;
+        $ldap_values = $ldap_groups_info[$group];
+        foreach ( $mapping as $field => $value ) {
+          dbg_error_log( "LDAP", "Considering copying %s", $field );
+          if ( isset($validUserFields[$field]) ) {
+            $user->{$field} =  $ldap_values[$value];
+            dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
+          }
+        }
+        if ($user->fullname=="") {
+          $user->fullname = $group;
+        }
+        if ($user->displayname=="") {
+          $user->displayname = $group;
+        }
+        $user->username = $group;
+        $user->updated = "now";  /** @todo Use the 'updated' timestamp from LDAP for groups too */
+
+        UpdateUserFromExternal( $user );
+        $qry = new AwlQuery( "UPDATE dav_principal set type_id = 3 WHERE username=:group ",array(':group'=>$group) );
+        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+        $c->messages[] = sprintf(i18n('- adding users %s to group : %s'),join(',',$ldap_groups_info[$group][$mapping['members']]),$group);
+        foreach ( $ldap_groups_info[$group][$mapping['members']] as $member ){
+          $qry = new AwlQuery( "INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member;",array (':group'=>$group,':member'=>$member) );
+          $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+        }
+      }
+    }
+
+    if ( sizeof ( $groups_to_update ) ){
+      $c->messages[] = sprintf(i18n('- updating groups : %s'),join(', ',$groups_to_update));
+      foreach ( $groups_to_update as $group ){
+        $db_members = array_values ( $db_group_members[$group] );
+        $ldap_members = array_values ( $ldap_groups_info[$group][$mapping['members']] );
+        $add_users = array_diff ( $ldap_members, $db_members );
+        if ( sizeof ( $add_users ) ){
+          $c->messages[] = sprintf(i18n('- adding %s to group : %s'),join(', ', $add_users ), $group);
+          foreach ( $add_users as $member ){
+            $qry = new AwlQuery( "INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member",array (':group'=>$group,':member'=>$member) );
+            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+          }
+        }
+        $remove_users = array_diff ( $db_members, $ldap_members );
+        if ( sizeof ( $remove_users ) ){
+          $c->messages[] = sprintf(i18n('- removing %s from group : %s'),join(', ', $remove_users ), $group);
+          foreach ( $remove_users as $member ){
+            $qry = new AwlQuery( "DELETE FROM group_member USING dav_principal g,dav_principal m WHERE group_id=g.principal_id AND member_id=m.principal_id AND g.username=:group AND m.username=:member",array (':group'=>$group,':member'=>$member) );
+            $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
+          }
+        }
+      }
+    }
+
+    if ( sizeof ( $groups_to_deactivate ) ){
+      $c->messages[] = sprintf(i18n('- deactivate groups : %s'),join(', ',$groups_to_deactivate));
+      foreach ( $groups_to_deactivate as $group ){
+        $qry = new AwlQuery( "UPDATE dav_principal set active='f'::bool WHERE username=:group AND type_id = 3",array(':group'=>$group) );
+        $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+      }
+    }
+  }
+}
+
+/**
+* sync LDAP against the DB
+*/
+function sync_LDAP(){
+  global $c;
+  $ldapDriver = getStaticLdap();
+  if($ldapDriver->valid){
+    $mapping = $c->authenticate_hook['config']['mapping_field'];
+    $attributes = array_values($mapping);
+    $ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
+
+    if ( sizeof($ldap_users_tmp) == 0 )
+      return;
+
+    foreach($ldap_users_tmp as $key => $ldap_user){
+      $ldap_users_info[$ldap_user[$mapping["username"]]] = $ldap_user;
+      unset($ldap_users_tmp[$key]);
+    }
+    $qry = new AwlQuery( "SELECT username, user_no, modified as updated FROM dav_principal where type_id=1");
+    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+    while($db_user = $qry->Fetch()) {
+      $db_users[] = $db_user->username;
+      $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated);
+    }
+
+    $ldap_users = array_keys($ldap_users_info);
+    // users only in ldap
+    $users_to_create = array_diff($ldap_users,$db_users);
+    // users only in db
+    $users_to_deactivate = array_diff($db_users,$ldap_users);
+    // users present in ldap and in the db
+    $users_to_update = array_intersect($db_users,$ldap_users);
+
+    // creation of all users;
+    if ( sizeof($users_to_create) ) {
+      $c->messages[] = sprintf(i18n('- creating record for users :  %s'),join(', ',$users_to_create));
+
+      foreach( $users_to_create as $username ) {
+        $user = (object) array( 'user_no' => 0, 'username' => $username );
+        $valid = $ldap_users_info[$username];
+        $ldap_timestamp = $valid[$mapping["updated"]];
+
+        /**
+        * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
+        */
+        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+            $$k = substr($ldap_timestamp,$v[0],$v[1]);
+        $ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
+        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+
+        sync_user_from_LDAP( $user, $mapping, $valid );
+      }
+    }
+
+    // deactivating all users
+    $params = array();
+    $i = 0;
+    foreach( $users_to_deactivate AS $v ) {
+      if ( isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v]) ) continue;
+      $params[':u'.$i++] = strtolower($v);
+    }
+    if ( count($params) > 0 ) {
+      $c->messages[] = sprintf(i18n('- deactivating users : %s'),join(', ',$users_to_deactivate));
+      $qry = new AwlQuery( 'UPDATE usr SET active = FALSE WHERE lower(username) IN ('.implode(',',array_keys($params)).')', $params);
+      $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+    }
+
+    // updating all users
+    if ( sizeof($users_to_update) ) {
+      foreach ( $users_to_update as $key=> $username ) {
+        $valid=$ldap_users_info[$username];
+        $ldap_timestamp = $valid[$mapping["updated"]];
+
+        $valid["user_no"] = $db_users_info[$username]["user_no"];
+        $mapping["user_no"] = "user_no";
+
+        /**
+        * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
+        */
+        foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
+            $$k = substr($ldap_timestamp,$v[0],$v[1]);
+        $ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
+        $valid[$mapping["updated"]] = "$Y-$m-$d $H:$M:$S";
+
+        $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
+        if ( $ldap_timestamp > $db_timestamp ) {
+          sync_user_from_LDAP($usr, $mapping, $valid );
+        }
+        else {
+          unset($users_to_update[$key]);
+          $users_nothing_done[] = $username;
+        }
+      }
+      if ( sizeof($users_to_update) )
+        $c->messages[] = sprintf(i18n('- updating user records : %s'),join(', ',$users_to_update));
+      if ( sizeof($users_nothing_done) )
+        $c->messages[] = sprintf(i18n('- nothing done on : %s'),join(', ', $users_nothing_done));
+		}
+
+		$admins = 0;
+    $qry = new AwlQuery( "select count(*) as admins from usr join role_member using ( user_no ) join roles using (role_no) where usr.active = true and role_name='Admin'");
+    $qry->Exec('sync_LDAP',__LINE__,__FILE__);
+    while($db_user = $qry->Fetch()) {
+      $admins = $db_user->admins;
+		}
+		if ( $admins == 0 ) {
+      $c->messages[] = sprintf(i18n('Warning: there are no active admin users, you should fix this before logging out.'));
+		}
+  }
+}
Index: contrib/davical/inc/caldav-LOCK.php
===================================================================
--- contrib/davical/inc/caldav-LOCK.php	(revision 3733)
+++ contrib/davical/inc/caldav-LOCK.php	(revision 3733)
@@ -0,0 +1,163 @@
+<?php
+/**
+* We support both LOCK and UNLOCK methods in this function
+*/
+
+require_once('XMLDocument.php');
+$reply = new XMLDocument(array( 'DAV:' => '' ));
+
+if ( ! $request->AllowedTo('write') ) {
+  $request->NeedPrivilege( 'write', $request->path );
+}
+
+if ( ! isset($request->xml_tags) ) {
+  if ( isset($request->lock_token) ) {
+    // It's OK for LOCK refresh requests to be empty.
+    $request->xml_tags = array();
+  }
+  else {
+    $request->XMLResponse( 400, new XMLElement( 'error', new XMLElement('missing-xml-for-request'), $reply->GetXmlNsArray() ) );
+  }
+}
+
+
+$unsupported = array();
+$lockinfo = array();
+$inside = array();
+
+foreach( $request->xml_tags AS $k => $v ) {
+
+  $tag = $v['tag'];
+  dbg_error_log( "LOCK", " Handling Tag '%s' => '%s' ", $k, $v );
+  switch ( $tag ) {
+    case 'DAV::lockinfo':
+      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+      if ( $v['type'] == "open" ) {
+        $lockscope = "";
+        $locktype = "";
+        $lockowner = "";
+        $inside[$tag] = true;
+      }
+      else if ( $inside[$tag] && $v['type'] == "close" ) {
+        $lockinfo['scope']  = $lockscope;   unset($lockscope);
+        $lockinfo['type']   = $locktype;    unset($locktype);
+        $lockinfo['owner']  = $lockowner;   unset($lockowner);
+        $inside[$tag] = false;
+      }
+      break;
+
+    case 'DAV::owner':
+    case 'DAV::locktype':
+    case 'DAV::lockscope':
+      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+      if ( $inside['DAV::lockinfo'] ) {
+        if ( $v['type'] == "open" ) {
+          $inside[$tag] = true;
+        }
+        else if ( $inside[$tag] && $v['type'] == "close" ) {
+          $inside[$tag] = false;
+        }
+      }
+      break;
+
+    /*case 'DAV::SHARED': */ /** Shared lock is not supported yet */
+    case 'DAV::exclusive':
+      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+      if ( $inside['DAV::lockscope'] && $v['type'] == "complete" ) {
+        $lockscope = strtolower(substr($tag,5));
+      }
+      break;
+
+    /* case 'DAV::READ': */ /** RFC2518 is pretty vague about read locks */
+    case 'DAV::write':
+      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+      if ( $inside['DAV::locktype'] && $v['type'] == "complete" ) {
+        $locktype = strtolower(substr($tag,5));
+      }
+      break;
+
+    case 'DAV::href':
+      dbg_error_log( "LOCK", ":Request: %s -> %s", $v['type'], $tag );
+      dbg_log_array( "LOCK", "DAV:href", $v, true );
+      if ( $inside['DAV::owner'] && $v['type'] == "complete" ) {
+        $lockowner = $v['value'];
+      }
+      break;
+
+    default:
+      if ( preg_match('/^(.*):([^:]+)$/', $tag, $matches) ) {
+        $unsupported[$matches[2]] = $matches[1];
+      }
+      else {
+        $unsupported[$tag] = "";
+      }
+      dbg_error_log( "LOCK", "Unhandled tag >>%s<<", $tag);
+  }
+}
+
+
+
+
+$request->UnsupportedRequest($unsupported); // Won't return if there was unsupported stuff.
+
+$lock_opener = $request->FailIfLocked();
+
+
+if ( $request->method == "LOCK" ) {
+  dbg_error_log( "LOCK", "Attempting to lock resource '%s'", $request->path);
+  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+    $sql = 'UPDATE locks SET start = current_timestamp WHERE opaquelocktoken = :lock_token';
+    $params = array( ':lock_token' => $lock_token);
+  }
+  else {
+    /**
+    * A fresh lock
+    */
+    $lock_token = uuid();
+    $sql = 'INSERT INTO locks ( dav_name, opaquelocktoken, type, scope, depth, owner, timeout, start )
+             VALUES( :dav_name, :lock_token, :type, :scope, :request_depth, :owner, :timeout::interval, current_timestamp )';
+    $params = array(
+        ':dav_name'      => $request->path,
+        ':lock_token'    => $lock_token,
+        ':type'          => $lockinfo['type'],
+        ':scope'         => $lockinfo['scope'],
+        ':request_depth' => $request->depth,
+        ':owner'         => $lockinfo['owner'],
+        ':timeout'       => $request->timeout.' seconds'
+    );
+    header( "Lock-Token: <opaquelocktoken:$lock_token>" );
+  }
+  $qry = new AwlQuery($sql, $params  );
+  $qry->Exec("LOCK",__LINE__,__FILE__);
+
+  $lock_row = $request->GetLockRow($lock_token);
+  $activelock = array(
+      new XMLElement( 'locktype',  new XMLElement( $lock_row->type )),
+      new XMLElement( 'lockscope', new XMLElement( $lock_row->scope )),
+      new XMLElement( 'depth',     $request->GetDepthName() ),
+      new XMLElement( 'owner',     new XMLElement( 'href', $lock_row->owner )),
+      new XMLElement( 'timeout',   'Second-'.$request->timeout),
+      new XMLElement( 'locktoken', new XMLElement( 'href', 'opaquelocktoken:'.$lock_token ))
+  );
+  $response = new XMLElement("lockdiscovery", new XMLElement( "activelock", $activelock), array("xmlns" => "DAV:") );
+}
+elseif (  $request->method == "UNLOCK" ) {
+  /**
+  * @TODO: respond with preconditionfailed(409,'lock-token-matches-request-uri') if
+  * there is no lock to be deleted.
+  */
+  dbg_error_log( "LOCK", "Attempting to unlock resource '%s'", $request->path);
+  if ( ($lock_token = $request->IsLocked()) ) { // NOTE Assignment in if() is expected here.
+    $sql = 'DELETE FROM locks WHERE opaquelocktoken = :lock_token';
+    $qry = new AwlQuery($sql, array( ':lock_token' => $lock_token) );
+    $qry->Exec("LOCK",__LINE__,__FILE__);
+  }
+  $request->DoResponse( 204 );
+}
+
+
+$prop = new XMLElement( "prop", $response, array('xmlns'=>'DAV:') );
+// dbg_log_array( "LOCK", "XML", $response, true );
+$xmldoc = $prop->Render(0,'<?xml version="1.0" encoding="utf-8" ?>');
+$request->DoResponse( 200, $xmldoc, 'text/xml; charset="utf-8"' );
+
Index: contrib/davical/inc/drivers_squid_pam.php
===================================================================
--- contrib/davical/inc/drivers_squid_pam.php	(revision 3733)
+++ contrib/davical/inc/drivers_squid_pam.php	(revision 3733)
@@ -0,0 +1,90 @@
+<?php
+/**
+* Manages PAM repository connection with SQUID help
+*
+* @package   davical
+* @category Technical
+* @subpackage   ldap
+* @author    Eric Seigne <eric.seigne@ryxeo.com>
+* @copyright Eric Seigne
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*/
+
+require_once("auth-functions.php");
+
+class squidPamDrivers
+{
+  /**#@+
+  * @access private
+  */
+
+  /**#@-*/
+
+
+  /**
+  * Constructor.
+  * @param string $config path where /usr/lib/squid/pam_auth is
+  */
+  function squidPamDrivers($config){
+      $this->__construct($config);
+  }
+
+
+  /**
+  * The constructor
+  *
+  * @param string $config path where /usr/lib/squid/pam_auth is
+  */
+  function __construct($config)
+  {
+      global $c;
+      if (! file_exists($config)){
+          $c->messages[] = sprintf(i18n( 'drivers_squid_pam : Unable to find %s file'), $config );
+          $this->valid=false;
+          return ;
+      }
+  }
+}
+
+
+/**
+* Check the username / password against the PAM system
+*/
+function SQUID_PAM_check($username, $password ){
+  global $c;
+
+  /**
+  * @todo Think of the children!  This is a horribly insecure use of unvalidated user input!  Probably it should be done with a popen or something, and it seems remarkably dodgy to expect that naively quoted strings will work in any way reliably.
+  * Meanwhile, I've quickly hacked something basic in place to improve the situation.  No quotes/backslashes in passwords for YOU!
+  */
+  $username = str_replace("'","",str_replace('"',"",str_replace('\\',"",$username)));
+  $password = str_replace("'","",str_replace('"',"",str_replace('\\',"",$password)));
+  $cmd = "echo '" . $username . "' '" . $password . "' | " . $c->authenticate_hook['config']['script'] . " -n common-auth";
+  $auth_result = exec($cmd);
+  if ( $auth_result == "OK") {
+    if ( $usr = getUserByName($username) ) {
+      return $usr;
+    }
+    else {
+      dbg_error_log( "PAM", "user %s doesn't exist in local DB, we need to create it",$username );
+      $fullname = exec('getent passwd "'.$username.'"' );
+      $fullname = preg_replace( '{^[^:]+:[^:]+:\d+:\d+:([^:,]+)(,?[^:]*):.*$}', '$1', $fullname );
+      $usr = (object) array(
+              'user_no' => 0,
+              'username' => $username,
+              'active' => 't',
+              'email' => $username . "@" . $c->authenticate_hook['config']['email_base'],
+              'updated' => date(),
+              'fullname' => $fullname
+      );
+
+      UpdateUserFromExternal( $usr );
+      return $usr;
+    }
+  }
+  else {
+    dbg_error_log( "PAM", "User %s is not a valid username (or password was wrong)", $username );
+    return false;
+  }
+
+}
Index: contrib/davical/inc/caldav-PUT-default.php
===================================================================
--- contrib/davical/inc/caldav-PUT-default.php	(revision 3733)
+++ contrib/davical/inc/caldav-PUT-default.php	(revision 3733)
@@ -0,0 +1,96 @@
+<?php
+/**
+* CalDAV Server - handle PUT method on unknown (arbitrary) content-types
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("PUT", "method handler");
+
+require_once('DAVResource.php');
+
+if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['put']) && $c->dbg['put'])) ) {
+  $fh = fopen('/tmp/PUT.txt','w');
+  if ( $fh ) {
+    fwrite($fh,$request->raw_post);
+    fclose($fh);
+  }
+}
+
+$lock_opener = $request->FailIfLocked();
+
+$dest = new DAVResource($request->path);
+
+$container = $dest->FetchParentContainer();
+if ( $container->IsCalendar() ) {
+  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:caldav:supported-calendar-data',
+                  translate('Incorrect content type for calendar: ') . $request->content_type );
+}
+else if ( $container->IsAddressbook() ) {
+  $request->PreconditionFailed(412,'urn:ietf:params:xml:ns:carddav:supported-address-data',
+                  translate('Incorrect content type for addressbook: ') . $request->content_type );
+}
+if ( ! $dest->Exists() ) {
+  if ( $container->IsPrincipal() ) {
+    $request->DoResponse(403,translate('A DAViCal principal collection may only contain collections'));
+  }
+  if ( ! $container->Exists() ) {
+    $request->DoResponse( 409, translate('Destination collection does not exist') );
+  }
+  $container->NeedPrivilege('DAV::bind');
+}
+else {
+  if ( $dest->IsCollection() ) {
+    if ( ! isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections ) {
+      $request->DoResponse(403,translate('You may not PUT to a collection URL'));
+    }
+    $request->DoResponse(403,translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
+  }
+  $dest->NeedPrivilege('DAV::write-content');
+}
+
+if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
+  $request->DoResponse(412);
+}
+
+if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
+  $request->DoResponse(412);
+}
+
+$collection_id = $container->GetProperty('collection_id');
+
+$qry = new AwlQuery();
+$qry->Begin();
+
+$etag = md5($request->raw_post);
+$params = array(
+    ':user_no' => $dest->GetProperty('user_no'),
+    ':dav_name' => $dest->bound_from(),
+    ':etag' => $etag,
+    ':dav_data' => $request->raw_post,
+    ':session_user' => $session->user_no
+);
+if ( $dest->Exists() ) {
+  $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
+          modified=current_timestamp WHERE user_no=:user_no AND dav_name=:dav_name';
+  $response_code = 200;
+}
+else {
+  $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, logged_user, created, modified, collection_id )
+          VALUES( :user_no, :dav_name, :etag, :dav_data, :session_user, current_timestamp, current_timestamp, :collection_id )';
+  $params[':collection_id'] = $collection_id;
+  $response_code = 201;
+}
+$qry->QDo( $sql, $params );
+
+$qry->QDo("SELECT write_sync_change( $collection_id, $response_code, :dav_name)", array(':dav_name' => $dest->bound_from() ) );
+
+$qry = new AwlQuery('COMMIT');
+if ( !$qry->Exec('move') ) rollback(500);
+
+header('ETag: "'. $etag . '"' );
+if ( $response_code == 200 ) $response_code = 204;
+$request->DoResponse( $response_code );
Index: contrib/davical/inc/caldav-OPTIONS.php
===================================================================
--- contrib/davical/inc/caldav-OPTIONS.php	(revision 3733)
+++ contrib/davical/inc/caldav-OPTIONS.php	(revision 3733)
@@ -0,0 +1,26 @@
+<?php
+/**
+* CalDAV Server - handle OPTIONS method
+*
+* @package   davical
+* @subpackage   caldav
+* @author    Andrew McMillan <andrew@mcmillan.net.nz>
+* @copyright Catalyst .Net Ltd, Morphoss Ltd <http://www.morphoss.com/>
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
+*/
+dbg_error_log("OPTIONS", "method handler");
+
+include_once('DAVResource.php');
+$resource = new DAVResource($request->path);
+
+$resource->NeedPrivilege( 'DAV::read', true );
+
+if ( !$resource->Exists() ) {
+  $request->DoResponse( 404, translate("No collection found at that location.") );
+}
+
+$allowed = implode( ', ', array_keys($resource->FetchSupportedMethods()) );
+header( 'Allow: '.$allowed);
+
+$request->DoResponse( 200, "" );
+
Index: contrib/davical/inc/check_UTF8.php
===================================================================
--- contrib/davical/inc/check_UTF8.php	(revision 3733)
+++ contrib/davical/inc/check_UTF8.php	(revision 3733)
@@ -0,0 +1,228 @@
+<?php
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Henri Sivonen, hsivonen@iki.fi
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * For the original C++ code, see
+ * http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+ * http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+ *
+ * The latest version of this file can be obtained from
+ * http://iki.fi/hsivonen/php-utf8/
+ *
+ * Version 1.0, 2003-05-30
+ */
+
+/**
+ * Takes an UTF-8 string and returns an array of ints representing the
+ * Unicode characters. Astral planes are supported ie. the ints in the
+ * output can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+ * are not allowed.
+ *
+ * Returns false if the input string isn't a valid UTF-8 octet sequence.
+ */
+function utf8ToUnicode(&$str)
+{
+  $mState = 0;     // cached expected number of octets after the current octet
+                   // until the beginning of the next UTF8 character sequence
+  $mUcs4  = 0;     // cached Unicode character
+  $mBytes = 1;     // cached expected number of octets in the current sequence
+
+  $out = array();
+
+  $len = strlen($str);
+  for($i = 0; $i < $len; $i++) {
+    $in = ord($str{$i});
+    if (0 == $mState) {
+      // When mState is zero we expect either a US-ASCII character or a
+      // multi-octet sequence.
+      if (0 == (0x80 & ($in))) {
+        // US-ASCII, pass straight through.
+        $out[] = $in;
+        $mBytes = 1;
+      } else if (0xC0 == (0xE0 & ($in))) {
+        // First octet of 2 octet sequence
+        $mUcs4 = ($in);
+        $mUcs4 = ($mUcs4 & 0x1F) << 6;
+        $mState = 1;
+        $mBytes = 2;
+      } else if (0xE0 == (0xF0 & ($in))) {
+        // First octet of 3 octet sequence
+        $mUcs4 = ($in);
+        $mUcs4 = ($mUcs4 & 0x0F) << 12;
+        $mState = 2;
+        $mBytes = 3;
+      } else if (0xF0 == (0xF8 & ($in))) {
+        // First octet of 4 octet sequence
+        $mUcs4 = ($in);
+        $mUcs4 = ($mUcs4 & 0x07) << 18;
+        $mState = 3;
+        $mBytes = 4;
+      } else if (0xF8 == (0xFC & ($in))) {
+        /* First octet of 5 octet sequence.
+         *
+         * This is illegal because the encoded codepoint must be either
+         * (a) not the shortest form or
+         * (b) outside the Unicode range of 0-0x10FFFF.
+         * Rather than trying to resynchronize, we will carry on until the end
+         * of the sequence and let the later error handling code catch it.
+         */
+        $mUcs4 = ($in);
+        $mUcs4 = ($mUcs4 & 0x03) << 24;
+        $mState = 4;
+        $mBytes = 5;
+      } else if (0xFC == (0xFE & ($in))) {
+        // First octet of 6 octet sequence, see comments for 5 octet sequence.
+        $mUcs4 = ($in);
+        $mUcs4 = ($mUcs4 & 1) << 30;
+        $mState = 5;
+        $mBytes = 6;
+      } else {
+        /* Current octet is neither in the US-ASCII range nor a legal first
+         * octet of a multi-octet sequence.
+         */
+        return false;
+      }
+    } else {
+      // When mState is non-zero, we expect a continuation of the multi-octet
+      // sequence
+      if (0x80 == (0xC0 & ($in))) {
+        // Legal continuation.
+        $shift = ($mState - 1) * 6;
+        $tmp = $in;
+        $tmp = ($tmp & 0x0000003F) << $shift;
+        $mUcs4 |= $tmp;
+
+        if (0 == --$mState) {
+          /* End of the multi-octet sequence. mUcs4 now contains the final
+           * Unicode codepoint to be output
+           *
+           * Check for illegal sequences and codepoints.
+           */
+
+          // From Unicode 3.1, non-shortest form is illegal
+          if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+              ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+              ((4 == $mBytes) && ($mUcs4 < 0x10000)) ||
+              (4 < $mBytes) ||
+              // From Unicode 3.2, surrogate characters are illegal
+              (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+              // Codepoints outside the Unicode range are illegal
+              ($mUcs4 > 0x10FFFF)) {
+            return false;
+          }
+          if (0xFEFF != $mUcs4) {
+            // BOM is legal but we don't want to output it
+            $out[] = $mUcs4;
+          }
+          //initialize UTF8 cache
+          $mState = 0;
+          $mUcs4  = 0;
+          $mBytes = 1;
+        }
+      } else {
+        /* ((0xC0 & (*in) != 0x80) && (mState != 0))
+         *
+         * Incomplete multi-octet sequence.
+         */
+        return false;
+      }
+    }
+  }
+  return $out;
+}
+
+/**
+ * Takes an array of ints representing the Unicode characters and returns
+ * a UTF-8 string. Astral planes are supported ie. the ints in the
+ * input can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+ * are not allowed.
+ *
+ * Returns false if the input array contains ints that represent
+ * surrogates or are outside the Unicode range.
+ */
+function unicodeToUtf8(&$arr)
+{
+  $dest = '';
+  foreach ($arr as $src) {
+    if($src < 0) {
+      return false;
+    } else if ( $src <= 0x007f) {
+      $dest .= chr($src);
+    } else if ($src <= 0x07ff) {
+      $dest .= chr(0xc0 | ($src >> 6));
+      $dest .= chr(0x80 | ($src & 0x003f));
+    } else if($src == 0xFEFF) {
+      // nop -- zap the BOM
+    } else if ($src >= 0xD800 && $src <= 0xDFFF) {
+      // found a surrogate
+      return false;
+    } else if ($src <= 0xffff) {
+      $dest .= chr(0xe0 | ($src >> 12));
+      $dest .= chr(0x80 | (($src >> 6) & 0x003f));
+      $dest .= chr(0x80 | ($src & 0x003f));
+    } else if ($src <= 0x10ffff) {
+      $dest .= chr(0xf0 | ($src >> 18));
+      $dest .= chr(0x80 | (($src >> 12) & 0x3f));
+      $dest .= chr(0x80 | (($src >> 6) & 0x3f));
+      $dest .= chr(0x80 | ($src & 0x3f));
+    } else {
+      // out of range
+      return false;
+    }
+  }
+  return $dest;
+}
+function check_string($ics){
+    $ics_file = explode("\n",$ics);
+    foreach($ics_file as $line => $str){
+        if(false === utf8ToUnicode($str)){
+            $error[] = $line;
+        }
+    }
+    if(isset($error) && is_array($error)){
+        foreach($error as $line){
+            dbg_error_log( "LOG check_string","error on lines %  invalid character in string %s" , ($line +1),$ics_file[$line]  );
+            return false;
+        }
+    } else {
+        dbg_error_log( "LOG check_string","the string is UTF8 compliant");
+        return true;
+    }
+}
+?>
Index: contrib/davical/inc/davical_configuration_missing.php
===================================================================
--- contrib/davical/inc/davical_configuration_missing.php	(revision 3733)
+++ contrib/davical/inc/davical_configuration_missing.php	(revision 3733)
@@ -0,0 +1,32 @@
+<?php
+include("page-header.php");
+
+  echo <<<EOBODY
+<h1>DAViCal Not Configured</h1>
+<h2>The Bad News</h2>
+<p>There is no configuration file present in <b>/etc/davical/$_SERVER[SERVER_NAME]-conf.php</b> so
+   your installation is not fully set up.</p>
+<h2>The Good News</h2>
+<p>Well, you're seeing this! At least you have DAViCal <i>installed</i> :-) You also have Apache and PHP working
+   and so really you are well on the road to success!</p>
+<h2>The Dubious News</h2>
+<p>You could try and <a href="http://$_SERVER[SERVER_NAME]/docs/rscds/configuring.html">click here</a> and
+   see if that enlightens you at all.  Odds are it's a fairly broken link, but it might work sooner
+   or later so keep downloading new versions and trying again.  Or make some guesses.  Or bug Andrew :-)</p>
+<h2>The Really Basic Help</h2>
+<p>The configuration file should look something like this:</p>
+<pre>
+&lt;?php
+//  \$c->domain_name  = 'davical.example.com';
+//  \$c->sysabbr     = 'davical';
+//  \$c->system_name = 'DAViCal CalDAV Server';
+
+  \$c->admin_email  = 'admin@example.com';
+  \$c->pg_connect[] = 'dbname=davical user=davical_app';
+
+</pre>
+<p>The only really <em>essential</em> thing there is that connect string for the database, although
+configuring someone for the admin e-mail is a really good idea.</p>
+EOBODY;
+
+include("page-footer.php");
Index: contrib/davical/inc/interactive-page.php
===================================================================
--- contrib/davical/inc/interactive-page.php	(revision 3733)
+++ contrib/davical/inc/interactive-page.php	(revision 3733)
@@ -0,0 +1,47 @@
+<?php
+require_once('MenuSet.php');
+
+
+$home_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
+$home_menu->AddOption(translate('Logout'), $c->base_url.'/index.php?logout', translate('Log out of DAViCal') );
+
+$wiki_help = '';
+if ( isset($_SERVER['SCRIPT_NAME']) ) {
+  $wiki_help = preg_replace('#^.*/#', '', $_SERVER['SCRIPT_NAME']);
+  $wiki_help = preg_replace('#\.php.*$#', '', $wiki_help);
+  if ( $wiki_help == 'admin' ) {
+    $wiki_help .= '/' . $_GET['t'] . '/' . $_GET['action'];
+  }
+  $wiki_help = 'w/Help/'.$wiki_help;
+}
+
+
+$help_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
+$help_menu->AddOption(translate('DAViCal Homepage'),'http://www.davical.org/',translate('The DAViCal Home Page'), false, 6000, true );
+$help_menu->AddOption(translate('DAViCal Wiki'),'http://wiki.davical.org/'.$wiki_help,translate('Visit the DAViCal Wiki'), false, 7000, true );
+$help_menu->AddOption(translate('Request Feature'),'http://davical.uservoice.com/',translate('Go to the DAViCal Feature Requests'), false, 8000, true );
+$help_menu->AddOption(translate('Report Bug'),'http://sourceforge.net/tracker/?func=add&group_id=179845&atid=890785',translate('Report a bug in the system'), false, 9000, true );
+
+$user_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
+$user_menu->AddOption(translate('View My Details'),$c->base_url.'/admin.php?action=edit&t=principal&id='.$session->principal_id,translate('View my own principal record'));
+$user_menu->AddOption(translate('List Users'),$c->base_url.'/admin.php?action=browse&t=principal&type=1');
+$user_menu->AddOption(translate('List Resources'),$c->base_url.'/admin.php?action=browse&t=principal&type=2');
+$user_menu->AddOption(translate('List Groups'),$c->base_url.'/admin.php?action=browse&t=principal&type=3');
+
+$admin_menu = new MenuSet('submenu', 'submenu', 'submenu_active');
+if ( $session->AllowedTo('Admin' )) {
+  $admin_menu->AddOption(translate('Setup'),$c->base_url.'/setup.php',translate('Setup DAViCal') );
+  $admin_menu->AddOption(translate('Upgrade Database'),$c->base_url.'/upgrade.php',translate('Upgrade DAViCal database schema') );
+  $admin_menu->AddOption(translate('Import Calendars'),$c->base_url.'/tools.php',translate('Operations on your calendar') );
+  $user_menu->AddOption(translate('Inactive Principals'),$c->base_url.'/admin.php?action=browse&t=principal&active=f');
+  $user_menu->AddOption(translate('Create Principal'),$c->base_url.'/admin.php?action=edit&t=principal',translate('Create a new principal (i.e. a new user, resource or group)'));
+}
+
+$related_menu = new MenuSet('related', 'menu', 'menu_active');
+
+$main_menu = new MenuSet('menu', 'menu', 'menu_active');
+$main_menu->AddSubMenu($home_menu, translate('Home'), $c->base_url.'/index.php', translate('Home Page'), false, 1000);
+$main_menu->AddSubMenu($user_menu, translate('User Functions'), $c->base_url.'/admin.php?action=browse&t=principal&type=1', translate('Browse all users'), false, 2000);
+$main_menu->AddSubMenu($admin_menu, translate('Administration'), $c->base_url.'/index.php', translate('Administration'), false, 3000);
+$main_menu->AddSubMenu($help_menu, translate('Help'), $c->base_url.'/help.php',translate('Help on the current screen'), false, 9000);
+
Index: contrib/davical/inc/ui/collection-edit.php
===================================================================
--- contrib/davical/inc/ui/collection-edit.php	(revision 3733)
+++ contrib/davical/inc/ui/collection-edit.php	(revision 3733)
@@ -0,0 +1,534 @@
+<?php
+
+// Editor component for company records
+$editor = new Editor(translate('Collection'), 'collection');
+param_to_global('id', 'int', 'old_id', 'collection_id' );
+param_to_global('user_no', 'int' );
+param_to_global('principal_id', 'int' );
+param_to_global('collection_name', '{^.+$}' );
+if ( isset($user_no) ) $usr = getUserByID($user_no);
+if ( isset($principal_id) ) $usr = getPrincipalByID($principal_id);
+$editor->SetLookup( 'timezone', 'SELECT \'\', \'*** Unknown ***\' UNION SELECT tz_id, tz_locn FROM time_zone WHERE tz_id = tz_locn AND length(tz_spec) > 100 ORDER BY 1' );
+$editor->SetLookup( 'schedule_transp', 'SELECT \'opaque\', \'Opaque\' UNION SELECT \'transp\', \'Transparent\'' );
+
+
+$editor->AddAttribute('timezone', 'id', 'fld_timezone' );
+$editor->AddAttribute('schedule_transp', 'id', 'fld_schedule_transp' );
+$editor->AddAttribute('is_calendar', 'id', 'fld_is_calendar');
+$editor->AddAttribute('is_addressbook', 'id', 'fld_is_addressbook');
+$editor->AddAttribute('is_calendar', 'onclick', 'toggle_enabled(\'fld_is_calendar\',\'=fld_timezone\',\'=fld_schedule_transp\',\'!fld_is_addressbook\',\'=fld_ics_file\');');
+$editor->AddAttribute('is_addressbook', 'onclick', 'toggle_enabled(\'fld_is_addressbook\',\'!fld_is_calendar\');');
+
+$editor->AddField('use_default_privs','default_privileges IS NULL');
+$editor->AddAttribute('use_default_privs', 'id', 'fld_use_default_privs');
+$editor->AddAttribute('use_default_privs', 'onclick', 'toggle_visible(\'fld_use_default_privs\',\'!privileges_settings\');');
+
+$editor->AddField('ics_file', "''");
+$editor->AddAttribute('ics_file', 'title', translate('Upload a .ics calendar in iCalendar format to initialise or replace this calendar.'));
+$editor->AddAttribute('ics_file', 'id', 'fld_ics_file');
+
+$editor->SetWhere( 'collection_id='.$id );
+
+$privilege_names = array( 'read', 'write-properties', 'write-content', 'unlock', 'read-acl', 'read-current-user-privilege-set',
+                         'bind', 'unbind', 'write-acl', 'read-free-busy', 'schedule-deliver-invite', 'schedule-deliver-reply',
+                         'schedule-query-freebusy', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );
+
+$params = array(
+  ':session_principal' => $session->principal_id,
+  ':scan_depth'        => $c->permission_scan_depth
+);
+$is_update = ( $_POST['_editor_action'][$editor->Id] == 'update' );
+if ( isset($collection_name) ) $collection_name = trim(str_replace( '/', '', $collection_name));
+if ( !$is_update && isset($collection_name) && $collection_name != '' && is_object($usr) ) {
+  $_POST['dav_name'] = sprintf('/%s/%s/', $usr->username, $collection_name );
+  $_POST['parent_container'] = sprintf('/%s/', $usr->username );
+  $params[':collection_path'] = $_POST['dav_name'];
+  $privsql = 'SELECT path_privs( :session_principal, :collection_path, :scan_depth) AS priv';
+}
+else if ( $id > 0 ) {
+  $params[':collection_id'] = $id;
+  $privsql = 'SELECT path_privs( :session_principal, dav_name, :scan_depth) AS priv FROM collection WHERE collection_id = :collection_id';
+}
+else {
+  if ( $editor->IsSubmit() && !$is_update && isset($collection_name) && $collection_name == '' ) {
+    $c->messages[] =  i18n('The collection name may not be blank.');
+  }
+}
+
+if ( isset($privsql) ) {
+  $privqry = new AwlQuery( $privsql, $params );
+  $privqry->Exec('admin-collection-edit',__LINE__,__FILE__);
+  $permissions = $privqry->Fetch();
+  $can_write_collection = ($session->AllowedTo('Admin') || (bindec($permissions->priv) & privilege_to_bits('DAV::bind')) );
+}
+
+dbg_error_log('collection-edit', "Can write collection: %s", ($can_write_collection? 'yes' : 'no') );
+
+$pwstars = '@@@@@@@@@@';
+if ( $can_write_collection && $editor->IsSubmit() ) {
+  $editor->WhereNewRecord( "collection_id=(SELECT CURRVAL('dav_id_seq'))" );
+  if ( $_POST['use_default_privs'] == 'on' ) {
+    $_POST['default_privileges'] = '';
+  }
+  else if ( isset($_POST['default_privileges']) ) {
+    $privilege_bitpos = array_flip($privilege_names);
+    $priv_names = array_keys($_POST['default_privileges']);
+    $privs = privilege_to_bits($priv_names);
+    $_POST['default_privileges'] = sprintf('%024s',decbin($privs));
+    $editor->Assign('default_privileges', $privs_dec);
+  }
+  $is_update = ( $_POST['_editor_action'][$editor->Id] == 'update' );
+  if ( $_POST['timezone'] == '' ) unset($_POST['timezone']);
+  $resourcetypes = '<DAV::collection/>';
+  if ( isset($_POST['is_calendar'])    && $_POST['is_calendar'] == 'on' )    $resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
+  if ( isset($_POST['is_addressbook']) && $_POST['is_addressbook'] == 'on' ) $resourcetypes .= '<urn:ietf:params:xml:ns:carddav:addressbook/>';
+  $_POST['resourcetypes'] = $resourcetypes;
+  if ( $editor->IsCreate() ) {
+    $c->messages[] = i18n("Creating new Collection.");
+  }
+  else {
+    $c->messages[] = i18n("Updating Collection record.");
+  }
+  if ( !$editor->Write() ) { 
+    $c->messages[] = i18n("Failed to write collection.");
+    if ( $id > 0 ) $editor->GetRecord();
+  }
+  else if ( isset($_FILES['ics_file']['tmp_name']) && $_FILES['ics_file']['tmp_name'] != '' ) {
+    /**
+    * If the user has uploaded a .ics file as a calendar, we fake this out
+    * as if it were a "PUT" request against a collection.  This is something
+    * of a hack.  It works though :-)
+    */
+    $ics = trim(file_get_contents($_FILES['ics_file']['tmp_name']));
+    dbg_error_log('collection-edit',':Write: Loaded %d bytes from %s', strlen($ics), $_FILES['ics_file']['tmp_name'] );
+    include_once('check_UTF8.php');
+    if ( !check_string($ics) ) $ics = force_utf8($ics);
+
+    if ( check_string($ics) ) {
+      $path = $editor->Value('dav_name');
+      $user_no = $editor->Value('user_no');
+      $username = $editor->Value('username');
+      include_once('caldav-PUT-functions.php');
+      controlRequestContainer( $username, $user_no, $path, false, ($publicly_readable == 'on' ? true : false));
+      import_collection( $ics, $user_no, $path, $session->user_no );
+      $c->messages[] = sprintf(translate('Calendar "%s" was loaded from file.'), $path);
+    }
+    else {
+      $c->messages[] =  i18n('The file is not UTF-8 encoded, please check the error for more details.');
+    }
+  }
+}
+else {
+  if ( $id > 0 ) $editor->GetRecord();
+  if ( $editor->IsSubmit() ) {
+    $c->messages[] = i18n('You do not have permission to modify this record.');
+  }
+}
+if ( $editor->Available() ) {
+  $c->page_title = $editor->Title(translate('Collection').': '.$editor->Value('dav_displayname'));
+  $entryqry = new AwlQuery( 'SELECT count(*) as count from caldav_data where collection_id='.$editor->Value('collection_id')  );
+  $entryqry->Exec('admin-collection-edit');
+  $entries = $entryqry->Fetch();  $entries = $entries->count;
+}
+else {
+  $c->page_title = $editor->Title(translate('Create New Collection'));
+  $privs = decbin(privilege_to_bits($c->default_privileges));
+  $editor->Assign('default_privileges', $privs);
+  $editor->Assign('username', $usr->username);
+  $editor->Assign('user_no', $usr->user_no);
+  $editor->Assign('is_calendar', 't' );
+  $editor->Assign('use_default_privs', 't');
+  $entries = 0;
+}
+
+
+$privilege_xlate = array(
+  'read' => translate('Read'),
+  'write-properties' => translate('Write Metadata'),
+  'write-content' => translate('Write Data'),
+  'unlock' => translate('Override a Lock'),
+  'read-acl' => translate('Read Access Controls'),
+  'read-current-user-privilege-set' => translate('Read Current User\'s Access'),
+  'bind' => translate('Create Events/Collections'),
+  'unbind' => translate('Delete Events/Collections'),
+  'write-acl' => translate('Write Access Controls'),
+  'read-free-busy' => translate('Read Free/Busy Information'),
+  'schedule-deliver-invite' => translate('Scheduling: Deliver an Invitation'),
+  'schedule-deliver-reply' => translate('Scheduling: Deliver a Reply'),
+  'schedule-query-freebusy' => translate('Scheduling: Query free/busy'),
+  'schedule-send-invite' => translate('Scheduling: Send an Invitation'),
+  'schedule-send-reply' => translate('Scheduling: Send a Reply'),
+  'schedule-send-freebusy' => translate('Scheduling: Send free/busy'),
+  'write' => translate('Write'),
+  'schedule-deliver' => translate('Scheduling: Delivery'),
+  'schedule-send' => translate('Scheduling: Sending')
+);
+
+/**
+* privilege_format_function is for formatting the binary privileges from the
+* database, including localising them.  This is a hook function for a browser
+* column object, so it takes three parameters:
+* @param mixed $value The value of the column.
+* @param BrowserColumn $column The BrowserColumn object we are hooked into.
+* @param dbrow $row The row object we read from the database.
+* @return string The formatted privileges.
+*/
+function privilege_format_function( $value, $column, $row ) {
+  global $privilege_xlate;
+
+  $privs = bits_to_privilege($value);
+  $formatted = '';
+  foreach( $privs AS $k => $v ) {
+    $formatted .= ($formatted == '' ? '' : ' , ');
+    $v = preg_replace( '{^.*:}', '', $v );
+    $formatted .= (isset($privilege_xlate[$v]) ? $privilege_xlate[$v] : $v );
+  }
+  return $formatted;
+}
+
+$default_privileges = bindec($editor->Value('default_privileges'));
+$privileges_set = '<div id="privileges">';
+for( $i=0; $i<count($privilege_names); $i++ ) {
+  $privilege_set = ( (1 << $i) & $default_privileges ? ' CHECKED' : '');
+  $privileges_set .= '<label class="privilege"><input name="default_privileges['.$privilege_names[$i].']" id="default_privileges_'.$privilege_names[$i].'" type="checkbox"'.$privilege_set.'>'.$privilege_xlate[$privilege_names[$i]].'</label>'."\n";
+}
+$privileges_set .= '</div>';
+
+$prompt_collection_id = translate('Collection ID');
+$value_id = ( $editor->Available() ? '##collection_id.hidden####collection_id.value##' : translate('New Collection'));
+$prompt_dav_name = translate('DAV Path');
+$value_dav_name = $c->base_url.'/caldav.php'. ( $editor->Available() ? '##dav_name.value##' : '/##user_no.hidden####username.value##/ ##collection_name.input.30##' );
+$prompt_load_file = translate('Load From File');
+$prompt_displayname = translate('Displayname');
+$prompt_entries = translate('Items in Collection');
+$prompt_public = translate('Publicly Readable');
+$prompt_calendar = translate('Is a Calendar');
+$prompt_addressbook = translate('Is an Addressbook');
+$prompt_use_default_privs = translate('Specific Privileges');
+$prompt_privileges = translate('Default Privileges');
+$prompt_description = translate('Description');
+$prompt_schedule_transp = translate('Schedule Transparency');
+$prompt_timezone = translate('Calendar Timezone');
+
+$btn_all = htmlspecialchars(translate('All'));             $btn_all_title = htmlspecialchars(translate('Toggle all privileges'));
+$btn_rw  = htmlspecialchars(translate('Read/Write'));      $btn_rw_title = htmlspecialchars(translate('Set read+write privileges'));
+$btn_read = htmlspecialchars(translate('Read'));           $btn_read_title = htmlspecialchars(translate('Set read privileges'));
+$btn_fb = htmlspecialchars(translate('Free/Busy'));        $btn_fb_title = htmlspecialchars(translate('Set free/busy privileges'));
+$btn_sd = htmlspecialchars(translate('Schedule Deliver')); $btn_sd_title = htmlspecialchars(translate('Set schedule-deliver privileges'));
+$btn_ss = htmlspecialchars(translate('Schedule Send'));    $btn_ss_title = htmlspecialchars(translate('Set schedule-deliver privileges'));
+
+
+$id = $editor->Value('collection_id');
+$template = <<<EOTEMPLATE
+##form##
+<script language="javascript">
+function toggle_privileges() {
+  var argv = toggle_privileges.arguments;
+  var argc = argv.length;
+
+  if ( argc < 2 ) {
+    return;
+  }
+  var match_me = argv[0];
+
+  var set_to = -1;
+  if ( argv[1] == 'all' ) {
+    var form = document.getElementById(argv[2]);
+    var fieldcount = form.elements.length;
+    var matching = '/^' + match_me + '/';
+    for (var i = 0; i < fieldcount; i++) {
+      var fieldname = form.elements[i].name;
+      if ( fieldname.match( match_me ) ) {
+        if ( set_to == -1 ) {
+          set_to = ( form.elements[i].checked ? 0 : 1 );
+        }
+        form.elements[i].checked = set_to;
+      }
+    }
+  }
+  else {
+    for (var i = 1; i < argc; i++) {
+      var f = document.getElementById( match_me + '_' + argv[i]);
+      if ( set_to == -1 ) {
+        set_to = ( f.checked ? 0 : 1 );
+      }
+      f.checked = set_to;
+    }
+  }
+}
+
+function toggle_enabled() {
+  var argv = toggle_enabled.arguments;
+  var argc = argv.length;
+
+  var fld_checkbox =  document.getElementById(argv[0]);
+
+  if ( argc < 2 ) {
+    return;
+  }
+
+  for (var i = 1; i < argc; i++) {
+    var fld_id = argv[i].substr(1);
+    var fld_logical = argv[i].substr(0,1);
+    var f = document.getElementById(fld_id);
+    if ( fld_logical == '=' )
+      f.disabled = !fld_checkbox.checked;
+    else
+      f.disabled = fld_checkbox.checked;
+  }
+}
+
+function toggle_visible() {
+  var argv = toggle_visible.arguments;
+  var argc = argv.length;
+
+  var fld_checkbox =  document.getElementById(argv[0]);
+
+  if ( argc < 2 ) {
+    return;
+  }
+
+  for (var i = 1; i < argc; i++) {
+    var block_id = argv[i].substr(1);
+    var block_logical = argv[i].substr(0,1);
+    var b = document.getElementById(block_id);
+    if ( block_logical == '!' )
+      b.style.display = (fld_checkbox.checked ? 'none' : '');
+    else
+      b.style.display = (!fld_checkbox.checked ? 'none' : '');
+  }
+}
+</script>
+<style>
+th.right, label.privilege {
+  white-space:nowrap;
+}
+label.privilege {
+  margin:0.2em 1em 0.2em 0.1em;
+  padding:0 0.2em;
+  line-height:1.6em;
+  font-size: 87%;
+}
+</style>
+<table>
+ <tr> <th class="right">$prompt_collection_id:</th>    <td class="left">$value_id</td> </tr>
+ <tr> <th class="right">$prompt_dav_name:</th>         <td class="left">$value_dav_name</td> </tr>
+ <tr> <th class="right">$prompt_entries:</th>          <td class="left">$entries</td> </tr>
+ <tr> <th class="right">$prompt_load_file:</th>        <td class="left">##ics_file.file.60##</td> </tr>
+ <tr> <th class="right">$prompt_displayname:</th>      <td class="left">##dav_displayname.input.50##</td> </tr>
+ <tr> <th class="right">$prompt_public:</th>           <td class="left">##publicly_readable.checkbox##</td> </tr>
+ <tr> <th class="right">$prompt_calendar:</th>         <td class="left">##is_calendar.checkbox##</td> </tr>
+ <tr> <th class="right">$prompt_addressbook:</th>      <td class="left">##is_addressbook.checkbox##</td> </tr>
+ <tr> <th class="right">$prompt_privileges:</th><td class="left">##use_default_privs.checkbox## &nbsp; &nbsp; &nbsp;
+ <div id="privileges_settings">
+<input type="button" value="$btn_all" class="submit" title="$btn_all_title" onclick="toggle_privileges('default_privileges', 'all', 'editor_1');">
+<input type="button" value="$btn_rw" class="submit" title="$btn_rw_title"
+ onclick="toggle_privileges('default_privileges', 'read', 'write-properties', 'write-content', 'bind', 'unbind', 'read-free-busy',
+                            'read-current-user-privilege-set', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy',
+                            'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<input type="button" value="$btn_read" class="submit" title="$btn_read_title"
+ onclick="toggle_privileges('default_privileges', 'read', 'read-free-busy', 'schedule-query-freebusy', 'read-current-user-privilege-set' );">
+<input type="button" value="$btn_fb" class="submit" title="$btn_fb_title"
+ onclick="toggle_privileges('default_privileges', 'read-free-busy', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_sd" class="submit" title="$btn_sd_title"
+ onclick="toggle_privileges('default_privileges', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_ss" class="submit" title="$btn_ss_title"
+ onclick="toggle_privileges('default_privileges', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<br>$privileges_set</div></td> </tr>
+ <tr> <th class="right">$prompt_timezone:</th>         <td class="left">##timezone.select##</td> </tr>
+ <tr> <th class="right">$prompt_schedule_transp:</th>  <td class="left">##schedule_transp.select##</td> </tr>
+ <tr> <th class="right">$prompt_description:</th>      <td class="left">##description.textarea.78x6##</td> </tr>
+ <tr> <th class="right"></th>                   <td class="left" colspan="2">##submit##</td> </tr>
+</table>
+</form>
+<script language="javascript">
+toggle_enabled('fld_is_calendar','=fld_timezone','=fld_schedule_transp','!fld_is_addressbook','=fld_ics_file');
+toggle_enabled('fld_is_addressbook','!fld_is_calendar');
+toggle_visible('fld_use_default_privs','!privileges_settings');
+</script>
+
+EOTEMPLATE;
+
+
+$editor->SetTemplate( $template );
+$page_elements[] = $editor;
+
+
+if ( $editor->Available() ) {
+
+  $c->stylesheets[] = 'css/browse.css';
+  $c->scripts[] = 'js/browse.js';
+
+
+  $grantrow = new Editor("Grants", "grants");
+  $grantrow->SetSubmitName( 'savegrantrow' );
+  $grantrow->SetLookup( 'to_principal', 'SELECT principal_id, displayname FROM dav_principal WHERE principal_id NOT IN (SELECT member_id FROM group_member WHERE group_id = '.$id.') ORDER BY displayname' );
+  if ( $can_write_collection ) {
+    if ( $grantrow->IsSubmit() ) {
+      $_POST['by_collection'] = $id;
+      $to_principal = intval($_POST['to_principal']);
+      $orig_to_id =  intval($_POST['orig_to_id']);
+      $grantrow->SetWhere( "by_collection=".qpg($id)." AND to_principal=$orig_to_id");
+      if ( isset($_POST['grant_privileges']) ) {
+        $privilege_bitpos = array_flip($privilege_names);
+        $priv_names = array_keys($_POST['grant_privileges']);
+        $privs = privilege_to_bits($priv_names);
+        $_POST['privileges'] = sprintf('%024s',decbin($privs));
+        $grantrow->Assign('privileges', $privs_dec);
+      }
+      $grantrow->Write( );
+      unset($_GET['to_principal']);
+    }
+    elseif ( isset($_GET['delete_grant']) ) {
+      $qry = new AwlQuery("DELETE FROM grants WHERE by_collection=:grantor_id AND to_principal = :to_principal",
+                            array( ':grantor_id' => $id, ':to_principal' => intval($_GET['delete_grant']) ));
+      $qry->Exec('collection-edit');
+    }
+  }
+
+  function edit_grant_row( $row_data ) {
+    global $grantrow, $id, $privilege_xlate, $privilege_names;
+    global $btn_all, $btn_all_title, $btn_rw, $btn_rw_title, $btn_read, $btn_read_title;
+    global $btn_fb, $btn_fb_title, $btn_sd, $btn_sd_title, $btn_ss, $btn_ss_title;
+
+    $submit_label = translate('Grant');
+    if ( $row_data->to_principal > -1 ) {
+      $grantrow->SetRecord( $row_data );
+      $submit_label = translate('Apply Changes');
+    }
+
+    $grant_privileges = bindec($grantrow->Value('grant_privileges'));
+    $privileges_set = '<div id="privileges">';
+    for( $i=0; $i < count($privilege_names); $i++ ) {
+      $privilege_set = ( (1 << $i) & $grant_privileges ? ' CHECKED' : '');
+      $privileges_set .= '<label class="privilege"><input name="grant_privileges['.$privilege_names[$i].']" id="grant_privileges_'.$privilege_names[$i].'" type="checkbox"'.$privilege_set.'>'.$privilege_xlate[$privilege_names[$i]].'</label>'."\n";
+    }
+    $privileges_set .= '</div>';
+
+    $orig_to_id = $row_data->to_principal;
+    $form_id = $grantrow->Id();
+    $form_url = preg_replace( '#&(edit|delete)_grant=\d+#', '', $_SERVER['REQUEST_URI'] );
+
+    $template = <<<EOTEMPLATE
+<form method="POST" enctype="multipart/form-data" id="form_$form_id" action="$form_url">
+  <td class="left" colspan="2"><input type="hidden" name="id" value="$id"><input type="hidden" name="orig_to_id" value="$orig_to_id">##to_principal.select##</td>
+  <td class="left" colspan="2">
+<input type="button" value="$btn_all" class="submit" title="$btn_all_title" onclick="toggle_privileges('grant_privileges', 'all', 'form_$form_id');">
+<input type="button" value="$btn_rw" class="submit" title="$btn_rw_title"
+ onclick="toggle_privileges('grant_privileges', 'read', 'write-properties', 'write-content', 'bind', 'unbind', 'read-free-busy',
+                            'read-current-user-privilege-set', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy',
+                            'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<input type="button" value="$btn_read" class="submit" title="$btn_read_title"
+ onclick="toggle_privileges('grant_privileges', 'read', 'read-free-busy', 'schedule-query-freebusy', 'read-current-user-privilege-set' );">
+<input type="button" value="$btn_fb" class="submit" title="$btn_fb_title"
+ onclick="toggle_privileges('grant_privileges', 'read-free-busy', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_sd" class="submit" title="$btn_sd_title"
+ onclick="toggle_privileges('grant_privileges', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_ss" class="submit" title="$btn_ss_title"
+ onclick="toggle_privileges('grant_privileges', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<br>$privileges_set
+  <td class="center">##$submit_label.submit##</td>
+</form>
+
+EOTEMPLATE;
+
+    $grantrow->SetTemplate( $template );
+    $grantrow->Title("");
+
+    return $grantrow->Render();
+  }
+
+  $browser = new Browser(translate('Collection Grants'));
+
+  $browser->AddColumn( 'to_principal', translate('To ID'), 'right', '##principal_link##' );
+  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
+  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || to_principal || '\">' || to_principal || '</a>'" );
+  $browser->AddHidden( 'grant_privileges', 'privileges' );
+  $browser->AddColumn( 'displayname', translate('Display Name') );
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', 'privileges', '', '', 'privilege_format_function' );
+  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );
+
+  if ( $can_write_collection ) {
+    $del_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=collection&id='.$id.'&delete_grant=##to_principal##" class="submit">'.translate('Revoke').'</a>';
+    $edit_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=collection&id='.$id.'&edit_grant=##to_principal##" class="submit">'.translate('Edit').'</a>';
+    $browser->AddColumn( 'action', translate('Action'), 'center', '', "'$edit_link&nbsp;$del_link'" );
+  }
+
+  $browser->SetOrdering( 'displayname', 'A' );
+
+  $browser->SetJoins( 'grants LEFT JOIN dav_principal ON (to_principal = principal_id) ' );
+  $browser->SetWhere( 'by_collection = '.$id );
+
+  if ( $c->enable_row_linking ) {
+    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
+  }
+  else {
+    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+  }
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+  if ( $can_write_collection ) {
+    if ( isset($_GET['edit_grant']) ) {
+      $browser->MatchedRow('to_principal', $_GET['edit_grant'], 'edit_grant_row');
+    }
+    else {
+      $extra_row = array( 'to_principal' => -1 );
+      $browser->MatchedRow('to_principal', -1, 'edit_grant_row');
+      $extra_row = (object) $extra_row;
+      $browser->AddRow($extra_row);
+    }
+  }
+
+
+  $browser = new Browser(translate('Access Tickets'));
+  $browser->AddHidden( 'dav_owner_id' );
+  $browser->AddColumn( 'ticket_id', translate('Ticket ID'), '', '' );
+  $browser->AddColumn( 'target', translate('Target'), '', '<td style="white-space:nowrap;">%s</td>', "'".$c->base_url.'/caldav.php'."' ||COALESCE(d.dav_name,c.dav_name)" );
+  $browser->AddColumn( 'expiry', translate('Expires'), '', '', 'TO_CHAR(expires,\'YYYYMMDD"T"HH:MI:SS\')');
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
+  $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id=##dav_owner_id##&ticket_id=##URL:ticket_id##&subaction=delete_ticket';
+  $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
+
+  $browser->SetOrdering( 'target', 'A' );
+
+  $browser->SetJoins( 'access_ticket t LEFT JOIN collection c ON (target_collection_id=collection_id) LEFT JOIN caldav_data d ON (target_resource_id=dav_id)' );
+  $browser->SetWhere( 'target_collection_id = '.intval($editor->Value('collection_id')) );
+
+  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+  
+/**
+ bind_id          | bigint | not null default nextval('dav_id_seq'::regclass)
+ bound_source_id  | bigint | 
+ access_ticket_id | text   | 
+ dav_owner_id     | bigint | not null
+ parent_container | text   | not null
+ dav_name         | text   | not null
+ dav_displayname  | text   | 
+ */
+
+  $browser = new Browser(translate('Bindings to this Collection'));
+  $browser->AddColumn( 'bind_id', translate('ID'), '', '' );
+  $browser->AddHidden( 'b.dav_owner_id' );
+  $browser->AddColumn( 'bound_as', translate('Bound As'), '', '<td style="white-space:nowrap;">%s</td>', "'".$c->base_url.'/caldav.php'."' ||b.dav_name" );
+  $browser->AddColumn( 'access_ticket_id', translate('Ticket ID'), '', '' );
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
+  $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id=##dav_owner_id##&bind_id=##URL:bind_id##&subaction=delete_binding';
+  $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
+
+  $browser->SetOrdering( 'target', 'A' );
+
+  $browser->SetJoins( 'dav_binding b LEFT JOIN collection c ON (bound_source_id=collection_id) LEFT JOIN access_ticket t ON (ticket_id=access_ticket_id)' );
+  $browser->SetWhere( 'bound_source_id = '.intval($editor->Value('collection_id')) );
+
+  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+}
+
Index: contrib/davical/inc/ui/principal-browse.php
===================================================================
--- contrib/davical/inc/ui/principal-browse.php	(revision 3733)
+++ contrib/davical/inc/ui/principal-browse.php	(revision 3733)
@@ -0,0 +1,50 @@
+<?php
+param_to_global( 'principal_type', 'int', 'type' );
+param_to_global( 'principal_active', '([tf])', 'active' );
+
+$browser = new Browser(translate('Calendar Principals'));
+if ( isset($principal_type) ) {
+  switch($principal_type) {
+    case 1:  $browser->Title(translate('User Calendar Principals'));      break;
+    case 2:  $browser->Title(translate('Resource Calendar Principals'));  break;
+    case 3:  $browser->Title(translate('Group Principals'));              break;
+  }
+}
+
+$browser->AddColumn( 'principal_id', translate('ID'), 'right', '##principal_link##' );
+$browser->AddColumn( 'username', translate('Name') );
+$rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
+$browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || principal_id || '\">' || principal_id || '</a>'" );
+$browser->AddColumn( 'displayname', translate('Display Name') );
+$browser->AddColumn( 'email', translate('EMail') );
+$browser->AddColumn( 'member_of', translate('Is Member of'), '', '', 'is_member_of_list(principal_id)' );
+
+if ( !isset($principal_type) || $principal_type == 3 ) {
+  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );
+}
+
+$browser->SetOrdering( 'username', 'A' );
+$browser->SetJoins( "dav_principal " );
+
+if ( isset($principal_active) && $principal_active == 'f' )
+  $browser->SetWhere( 'NOT user_active' );
+else
+  $browser->SetWhere( 'user_active' );
+
+if ( isset($principal_type) ) {
+  $browser->AndWhere( 'type_id = '.$principal_type );
+}
+
+
+$c->page_title = $browser->Title();
+
+if ( $c->enable_row_linking ) {
+  $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.htmlspecialchars(translate('Click to display user details')).'" class="r%d">', '</tr>', '#even' );
+}
+else {
+  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+}
+
+$browser->DoQuery();
+$page_elements[] = $browser;
+
Index: contrib/davical/inc/ui/principal-edit.php
===================================================================
--- contrib/davical/inc/ui/principal-edit.php	(revision 3733)
+++ contrib/davical/inc/ui/principal-edit.php	(revision 3733)
@@ -0,0 +1,772 @@
+<?php
+
+// Editor component for company records
+$editor = new Editor(translate('Principal'), 'dav_principal');
+
+$editor->SetLookup( 'date_format_type', "SELECT 'E', 'European' UNION SELECT 'U', 'US Format' UNION SELECT 'I', 'ISO Format'" );
+$editor->SetLookup( 'type_id', 'SELECT principal_type_id, principal_type_desc FROM principal_type ORDER BY principal_type_id' );
+$editor->SetLookup( 'locale', 'SELECT \'\', \''.translate("*** Default Locale ***").'\' UNION SELECT locale, locale_name_locale FROM supported_locales ORDER BY 1 ASC' );
+$editor->AddAttribute( 'locale', 'title', translate("The preferred language for this person.") );
+$editor->AddAttribute( 'fullname', 'title', translate("The full name for this person, group or other type of principal.") );
+param_to_global('id', 'int', 'old_id', 'principal_id' );
+$editor->SetWhere( 'principal_id='.$id );
+
+$editor->AddField('is_admin', 'EXISTS( SELECT 1 FROM role_member WHERE role_no = 1 AND role_member.user_no = dav_principal.user_no )' );
+$editor->AddAttribute('is_admin', 'title', translate('An "Administrator" user has full rights to the whole DAViCal System'));
+
+$privilege_names = array( 'read', 'write-properties', 'write-content', 'unlock', 'read-acl', 'read-current-user-privilege-set',
+                         'bind', 'unbind', 'write-acl', 'read-free-busy', 'schedule-deliver-invite', 'schedule-deliver-reply',
+                         'schedule-query-freebusy', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );
+
+$delete_collection_confirmation_required = null;
+$delete_principal_confirmation_required = null;
+
+function handle_subaction( $subaction ) {
+  global $session, $c, $id, $editor;
+  global $delete_collection_confirmation_required;
+  global $delete_principal_confirmation_required;
+  global $delete_ticket_confirmation_required;
+  global $delete_binding_confirmation_required;
+  
+  dbg_error_log('admin-principal-edit',':handle_action: Action %s', $subaction );
+
+  switch( $subaction ) {
+    case 'delete_collection':
+      dbg_error_log('admin-principal-edit',':handle_action: Deleting collection %s for principal %d', $_GET['dav_name'], $id );
+      if ( $session->AllowedTo('Admin')
+                || ($id > 0 && $session->principal_id == $id) ) {
+        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
+          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete collection %s for principal %d', $_GET['dav_name'], $id );
+          $qry = new AwlQuery('DELETE FROM collection WHERE dav_name=?;', $_GET['dav_name'] );
+          if ( $qry->Exec() ) {
+            $c->messages[] = i18n('Collection deleted');
+            return true;
+          }
+          else {
+            $c->messages[] = i18n('There was an error writing to the database.');
+            return false;
+          }
+        }
+        else {
+          $c->messages[] = i18n('Please confirm deletion of collection - see below');
+          $delete_collection_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
+          return false;
+        }
+      }
+      break;
+
+    case 'delete_principal':
+      dbg_error_log('admin-principal-edit',':handle_action: Deleting principal %d', $id );
+      if ( $session->AllowedTo('Admin') ) {
+        if ( isset($id) && $id > 1 && $session->CheckConfirmationHash('GET', 'confirm') ) {
+          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete principal %d -%s', $id, $editor->Value('username') );
+          $qry = new AwlQuery('DELETE FROM dav_principal WHERE principal_id=?', $id );
+          if ( $qry->Exec() ) {
+            $c->messages[] = i18n('Principal deleted');
+            return true;
+          }
+          else {
+            $c->messages[] = i18n('There was an error writing to the database.');
+            return false;
+          }
+        }
+        else {
+          $c->messages[] = i18n('Please confirm deletion of the principal');
+          $delete_principal_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
+          return false;
+        }
+      }
+      break;
+
+    case 'delete_ticket':
+      dbg_error_log('admin-principal-edit',':handle_action: Deleting ticket "%s" for principal %d', $_GET['ticket_id'], $id );
+      if ( $session->AllowedTo('Admin')
+                || ($id > 0 && $session->principal_id == $id) ) {
+        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
+          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete ticket "%s" for principal %d', $_GET['ticket_id'], $id );
+          $qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=?;', $_GET['ticket_id'] );
+          if ( $qry->Exec() ) {
+            $c->messages[] = i18n('Access ticket deleted');
+            return true;
+          }
+          else {
+            $c->messages[] = i18n('There was an error writing to the database.');
+            return false;
+          }
+        }
+        else {
+          $c->messages[] = i18n('Please confirm deletion of access ticket - see below');
+          $delete_ticket_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
+          return false;
+        }
+      }
+      break;
+
+    case 'delete_binding':
+      dbg_error_log('admin-principal-edit',':handle_action: Deleting binding "%s" for principal %d', $_GET['bind_id'], $id );
+      if ( $session->AllowedTo('Admin')
+                || ($id > 0 && $session->principal_id == $id) ) {
+        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
+          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete ticket "%s" for principal %d', $_GET['bind_id'], $id );
+          $qry = new AwlQuery('DELETE FROM dav_binding WHERE bind_id=?;', $_GET['bind_id'] );
+          if ( $qry->Exec() ) {
+            $c->messages[] = i18n('Binding deleted');
+            return true;
+          }
+          else {
+            $c->messages[] = i18n('There was an error writing to the database.');
+            return false;
+          }
+        }
+        else {
+          $c->messages[] = i18n('Please confirm deletion of binding - see below');
+          $delete_binding_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
+          return false;
+        }
+      }
+      break;
+
+      default:
+      return false;
+  }
+  return false;
+}
+
+if ( isset($_GET['subaction']) ) {
+  handle_subaction($_GET['subaction']);
+}
+
+
+$can_write_principal = ($session->AllowedTo('Admin') || $session->principal_id == $id );
+$post_values = false;
+
+if ( isset($_POST['xxxxusername']) ) {
+  $_POST['xxxxusername'] = trim(str_replace('/', '', $_POST['xxxxusername']));
+  if ( $_POST['xxxxusername'] == '' ) {
+    $c->messages[] = i18n("The username must not be blank, and may not contain a slash");
+    $can_write_principal = false;
+  }
+};
+if ( isset($_POST['fullname']) && trim($_POST['fullname']) == '' ) {
+  $c->messages[] = i18n("The full name must not be blank.");
+  $can_write_principal = false;
+};
+if ( isset($_POST['email']) && trim($_POST['email']) == '' ) {
+  $c->messages[] = i18n("The email address really should not be blank.");
+}
+
+$pwstars = '@@@@@@@@@@';
+if ( $can_write_principal && $editor->IsSubmit() ) {
+  $editor->WhereNewRecord( "principal_id=(SELECT CURRVAL('dav_id_seq'))" );
+  if ( ! $session->AllowedTo('Admin') ) {
+    unset($_POST['admin_role']);
+    unset($_POST['user_active']);
+  }
+  unset($_POST['password']);
+  if ( $_POST['newpass1'] != '' && $_POST['newpass1'] != $pwstars ) {
+    if ( $_POST['newpass1'] == $_POST['newpass2'] ) {
+      $_POST['password'] = $_POST['newpass1'];
+    }
+    else {
+      $c->messages[] = "Password not updated. The supplied passwords do not match.";
+    }
+  }
+  if ( isset($_POST['fullname']) && !isset($_POST['displayname']) ) {
+    $_POST['displayname'] = $_POST['fullname'];
+  }
+  if ( isset($_POST['default_privileges']) ) {
+    $privilege_bitpos = array_flip($privilege_names);
+    $priv_names = array_keys($_POST['default_privileges']);
+    $privs = privilege_to_bits($priv_names);
+    $_POST['default_privileges'] = sprintf('%024s',decbin($privs));
+    $editor->Assign('default_privileges', $privs_dec);
+  }
+  if ( $editor->IsCreate() ) {
+    $c->messages[] = i18n("Creating new Principal record.");
+  }
+  else {
+    $c->messages[] = i18n("Updating Principal record.");
+  }
+  $editor->Write();
+  if ( $_POST['type_id'] != 3 && $editor->IsCreate() ) {
+    /** We only add the default calendar if it isn't a group, and this is a create action */
+    require_once('auth-functions.php');
+    CreateHomeCalendar($editor->Value('username'));
+  }
+  if ( $session->AllowedTo('Admin') ) {
+    if ( $_POST['is_admin'] == 'on' ) {
+      $sql = 'INSERT INTO role_member (role_no, user_no) SELECT 1, dav_principal.user_no FROM dav_principal WHERE user_no = :user_no AND NOT EXISTS(SELECT 1 FROM role_member rm WHERE rm.role_no = 1 AND rm.user_no = dav_principal.user_no )';
+      $editor->Assign('is_admin', 't');
+    }
+    else {
+      $sql = 'DELETE FROM role_member WHERE role_no = 1 AND user_no = :user_no';
+      $editor->Assign('is_admin', 'f');
+    }
+    $params[':user_no'] = $editor->Value('user_no');
+    $qry = new AwlQuery( $sql, $params );
+    $qry->Exec('admin-principal-edit');
+  }
+}
+else if ( isset($id) && $id > 0 ) {
+  $editor->GetRecord();
+  if ( $editor->IsSubmit() ) {
+    $c->messages[] = i18n('You do not have permission to modify this record.');
+  }
+}
+if ( $editor->Available() ) {
+  $c->page_title = $editor->Title(translate('Principal').': '.$editor->Value('fullname'));
+}
+else {
+  $c->page_title = $editor->Title(translate('Create New Principal'));
+  $privs = decbin(privilege_to_bits($c->default_privileges));
+  $editor->Assign('default_privileges', $privs);
+  $editor->Assign('user_active', 't');
+  foreach( $c->template_usr AS $k => $v ) {
+    $editor->Assign($k, $v);
+  }
+}
+if ( $post_values ) {
+  $editor->PostToValues();
+  if ( isset($_POST['default_privileges']) ) {
+    $privilege_bitpos = array_flip($privilege_names);
+    $priv_names = array_keys($_POST['default_privileges']);
+    $privs = privilege_to_bits($priv_names);
+    $_POST['default_privileges'] = sprintf('%024s',decbin($privs));
+    $editor->Assign('default_privileges', $_POST['default_privileges']);
+  }
+}
+
+$privilege_xlate = array(
+  'all' => translate('All privileges'),
+  'read' => translate('Read'),
+  'write-properties' => translate('Write Metadata'),
+  'write-content' => translate('Write Data'),
+  'unlock' => translate('Override a Lock'),
+  'read-acl' => translate('Read Access Controls'),
+  'read-current-user-privilege-set' => translate('Read Current User\'s Access'),
+  'bind' => translate('Create Events/Collections'),
+  'unbind' => translate('Delete Events/Collections'),
+  'write-acl' => translate('Write Access Controls'),
+  'read-free-busy' => translate('Read Free/Busy Information'),
+  'schedule-deliver-invite' => translate('Scheduling: Deliver an Invitation'),
+  'schedule-deliver-reply' => translate('Scheduling: Deliver a Reply'),
+  'schedule-query-freebusy' => translate('Scheduling: Query free/busy'),
+  'schedule-send-invite' => translate('Scheduling: Send an Invitation'),
+  'schedule-send-reply' => translate('Scheduling: Send a Reply'),
+  'schedule-send-freebusy' => translate('Scheduling: Send free/busy'),
+  'write' => translate('Write'),
+  'schedule-deliver' => translate('Scheduling: Delivery'),
+  'schedule-send' => translate('Scheduling: Sending')
+);
+
+/**
+* privilege_format_function is for formatting the binary privileges from the
+* database, including localising them.  This is a hook function for a browser
+* column object, so it takes three parameters:
+* @param mixed $value The value of the column.
+* @param BrowserColumn $column The BrowserColumn object we are hooked into.
+* @param dbrow $row The row object we read from the database.
+* @return string The formatted privileges.
+*/
+function privilege_format_function( $value, $column, $row ) {
+  global $privilege_xlate;
+
+  $privs = bits_to_privilege($value);
+  $formatted = '';
+  foreach( $privs AS $k => $v ) {
+    $formatted .= ($formatted == '' ? '' : ' , ');
+    $v = preg_replace( '{^.*:}', '', $v );
+    $formatted .= (isset($privilege_xlate[$v]) ? $privilege_xlate[$v] : $v );
+  }
+  return $formatted;
+}
+
+
+
+$default_privileges = bindec($editor->Value('default_privileges'));
+$privileges_set = '<div id="privileges">';
+for( $i=0; $i<count($privilege_names); $i++ ) {
+  $privilege_set = ( (1 << $i) & $default_privileges ? ' CHECKED' : '');
+  $privileges_set .= '<label class="privilege"><input name="default_privileges['.$privilege_names[$i].']" id="default_privileges_'.$privilege_names[$i].'" type="checkbox"'.$privilege_set.'>'.$privilege_xlate[$privilege_names[$i]].'</label>'."\n";
+}
+$privileges_set .= '</div>';
+
+$prompt_principal_id = translate('Principal ID');
+$value_id = ( $editor->Available() ? '##principal_id.hidden####principal_id.value##' : translate('New Principal'));
+$prompt_username = translate('Username');
+$prompt_password_1 = translate('Change Password');
+$prompt_password_2 = translate('Confirm Password');
+$prompt_fullname = translate('Fullname');
+$prompt_displayname = translate('Display Name');
+$prompt_email = translate('Email Address');
+$prompt_date_format = translate('Date Format Style');
+$prompt_admin = translate('Administrator');
+$prompt_active = translate('Active');
+$prompt_locale = translate('Locale');
+$prompt_type = translate('Principal Type');
+$prompt_privileges = translate('Privileges granted to All Users');
+
+$btn_all = htmlspecialchars(translate('All'));             $btn_all_title = htmlspecialchars(translate('Toggle all privileges'));
+$btn_rw  = htmlspecialchars(translate('Read/Write'));      $btn_rw_title = htmlspecialchars(translate('Set read+write privileges'));
+$btn_read = htmlspecialchars(translate('Read'));           $btn_read_title = htmlspecialchars(translate('Set read privileges'));
+$btn_fb = htmlspecialchars(translate('Free/Busy'));        $btn_fb_title = htmlspecialchars(translate('Set free/busy privileges'));
+$btn_sd = htmlspecialchars(translate('Schedule Deliver')); $btn_sd_title = htmlspecialchars(translate('Set schedule-deliver privileges'));
+$btn_ss = htmlspecialchars(translate('Schedule Send'));    $btn_ss_title = htmlspecialchars(translate('Set schedule-deliver privileges'));
+
+$admin_row_entry = '';
+$delete_principal_button = '';
+if ( $session->AllowedTo('Admin') ) {
+  $admin_row_entry = ' <tr> <th class="right">'.$prompt_admin.':</th><td class="left">##is_admin.checkbox##</td> </tr>';
+  $admin_row_entry .= ' <tr> <th class="right">'.$prompt_active.':</th><td class="left">##user_active.checkbox##</td> </tr>';
+  if ( isset($id) )
+    $delete_principal_button = '<a href="'.$c->base_url . '/admin.php?action=edit&t=principal&subaction=delete_principal&id='.$id.'" class="submit">' . translate("Delete Principal") . '</a>';
+}
+
+$id = $editor->Value('principal_id');
+$template = <<<EOTEMPLATE
+##form##
+<script language="javascript">
+function toggle_privileges() {
+  var argv = toggle_privileges.arguments;
+  var argc = argv.length;
+
+  if ( argc < 2 ) {
+    return;
+  }
+  var match_me = argv[0];
+
+  var set_to = -1;
+  if ( argv[1] == 'all' ) {
+    var form = document.getElementById(argv[2]);
+    var fieldcount = form.elements.length;
+    var matching = '/^' + match_me + '/';
+    for (var i = 0; i < fieldcount; i++) {
+      var fieldname = form.elements[i].name;
+      if ( fieldname.match( match_me ) ) {
+        if ( set_to == -1 ) {
+          set_to = ( form.elements[i].checked ? 0 : 1 );
+        }
+        form.elements[i].checked = set_to;
+      }
+    }
+  }
+  else {
+    for (var i = 1; i < argc; i++) {
+      var f = document.getElementById( match_me + '_' + argv[i]);
+      if ( set_to == -1 ) {
+        set_to = ( f.checked ? 0 : 1 );
+      }
+      f.checked = set_to;
+    }
+  }
+}
+</script>
+<style>
+th.right, label.privilege {
+  white-space:nowrap;
+}
+label.privilege {
+  margin:0.2em 1em 0.2em 0.1em;
+  padding:0 0.2em;
+  line-height:1.6em;
+  font-size:87%;
+}
+</style>
+<table>
+ <tr> <th class="right">$prompt_principal_id:</th><td class="left">
+  <table width="100%" class="form_inner"><tr>
+   <td>$value_id</td>
+   <td align="right">$delete_principal_button</td>
+  </tr></table>
+ </td></tr>
+ <tr> <th class="right">$prompt_username:</th>    <td class="left">##xxxxusername.input.50##</td> </tr>
+ <tr> <th class="right">$prompt_password_1:</th>  <td class="left">##newpass1.password.$pwstars##</td> </tr>
+ <tr> <th class="right">$prompt_password_2:</th>  <td class="left">##newpass2.password.$pwstars##</td> </tr>
+ <tr> <th class="right">$prompt_fullname:</th>    <td class="left">##fullname.input.50##</td> </tr>
+ <tr> <th class="right">$prompt_email:</th>       <td class="left">##email.input.50##</td> </tr>
+ <tr> <th class="right">$prompt_locale:</th>      <td class="left">##locale.select##</td> </tr>
+ <tr> <th class="right">$prompt_date_format:</th> <td class="left">##date_format_type.select##</td> </tr>
+ <tr> <th class="right">$prompt_type:</th>        <td class="left">##type_id.select##</td> </tr>
+ $admin_row_entry
+ <tr> <th class="right" style="white-space:normal;">$prompt_privileges:</th><td class="left">
+<input type="button" value="$btn_all" class="submit" title="$btn_all_title" onclick="toggle_privileges('default_privileges', 'all', 'editor_1');">
+<input type="button" value="$btn_rw" class="submit" title="$btn_rw_title"
+ onclick="toggle_privileges('default_privileges', 'read', 'write-properties', 'write-content', 'bind', 'unbind', 'read-free-busy',
+                            'read-current-user-privilege-set', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy',
+                            'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<input type="button" value="$btn_read" class="submit" title="$btn_read_title"
+ onclick="toggle_privileges('default_privileges', 'read', 'read-free-busy', 'schedule-query-freebusy', 'read-current-user-privilege-set' );">
+<input type="button" value="$btn_fb" class="submit" title="$btn_fb_title"
+ onclick="toggle_privileges('default_privileges', 'read-free-busy', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_sd" class="submit" title="$btn_sd_title"
+ onclick="toggle_privileges('default_privileges', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_ss" class="submit" title="$btn_ss_title"
+ onclick="toggle_privileges('default_privileges', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<br>$privileges_set</td> </tr>
+ <tr> <th class="right"></th>                   <td class="left" colspan="2">##submit##</td> </tr>
+</table>
+</form>
+EOTEMPLATE;
+
+$editor->SetTemplate( $template );
+$page_elements[] = $editor;
+
+if ( isset($delete_principal_confirmation_required) ) {
+  $html = '<p class="error">';
+  $html .= sprintf('<b>%s</b> \'%s\' <a class="error" href="%s&%s">%s</a> %s',
+       translate('Deleting Principal:'), $editor->Value('displayname'), $_SERVER['REQUEST_URI'],
+        $delete_principal_confirmation_required, translate('Confirm Deletion of the Principal'),
+        translate('All of the principal\'s calendars and events will be unrecoverably deleted.') );
+  $html .= "</p>\n";
+  $page_elements[] = $html;
+}
+
+
+if ( isset($id) && $id > 0 ) {
+  $browser = new Browser(translate('Group Memberships'));
+  $c->stylesheets[] = 'css/browse.css';
+  $c->scripts[] = 'js/browse.js';
+
+  $browser->AddColumn( 'group_id', translate('ID'), 'right', '##principal_link##' );
+  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
+  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || principal_id || '\">' || principal_id || '</a>'" );
+  $browser->AddColumn( 'displayname', translate('Display Name') );
+  $browser->AddColumn( 'member_of', translate('Is Member of'), '', '', 'is_member_of_list(principal_id)' );
+  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );
+
+  $browser->SetOrdering( 'displayname', 'A' );
+
+  $browser->SetJoins( "group_member LEFT JOIN dav_principal ON (group_id = principal_id) " );
+  $browser->SetWhere( 'user_active AND member_id = '.$id );
+
+  if ( $c->enable_row_linking ) {
+    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
+  }
+  else {
+    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+  }
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+
+  if ( $editor->Value('type_id') == 3 ) {
+
+    $grouprow = new Editor("Group Members", "group_member");
+    $grouprow->SetLookup( 'member_id', 'SELECT principal_id, coalesce(displayname,fullname,username) FROM dav_principal WHERE principal_id NOT IN (SELECT member_id FROM group_member WHERE group_id = '.$id.') AND principal_id != '.$id);
+    $grouprow->SetSubmitName( 'savegrouprow' );
+
+    if ( $can_write_principal ) {
+      if ( $grouprow->IsSubmit() ) {
+        if ( $grouprow->IsUpdate() )
+          $c->messages[] = translate('Updating Member of this Group Principal');
+        else
+          $c->messages[] = translate('Adding new member to this Group Principal');
+
+        $_POST['group_id'] = $id;
+        $member_id = intval($_POST['member_id']);
+        $grouprow->SetWhere( "group_id=".qpg($id)." AND member_id=$member_id");
+        $grouprow->Write( );
+        unset($_GET['member_id']);
+      }
+      elseif ( isset($_GET['delete_member']) ) {
+        $qry = new AwlQuery("DELETE FROM group_member WHERE group_id=:group_id AND member_id = :member_id",
+                              array( ':group_id' => $id, ':member_id' => intval($_GET['delete_member']) ));
+        $qry->Exec('principal-edit');
+        $c->messages[] = translate('Member deleted from this Group Principal');
+      }
+    }
+
+    function edit_group_row( $row_data ) {
+      global $grouprow, $id, $c;
+
+      $form_url = preg_replace( '#&(edit|delete)_group=\d+#', '', $_SERVER['REQUEST_URI'] );
+
+      $template = <<<EOTEMPLATE
+<form method="POST" enctype="multipart/form-data" id="add_group" action="$form_url">
+  <td class="left"><input type="hidden" name="id" value="$id"></td>
+  <td class="left" colspan="3">##member_id.select## &nbsp; ##Add.submit##</td>
+  <td class="center"></td>
+</form>
+
+EOTEMPLATE;
+
+      $grouprow->SetTemplate( $template );
+      $grouprow->Title("");
+      if ( $row_data->group_id > -1 ) $grouprow->SetRecord( $row_data );
+
+      return $grouprow->Render();
+    }
+
+    $browser = new Browser(translate('Group Members'));
+
+    $browser->AddColumn( 'group_id', translate('ID'), 'right', '##principal_link##' );
+    $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
+    $browser->AddHidden( 'principal_id' );
+    $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || principal_id || '\">' || principal_id || '</a>'" );
+    $browser->AddColumn( 'displayname', translate('Display Name') );
+    $browser->AddColumn( 'member_of', translate('Is Member of'), '', '', 'is_member_of_list(principal_id)' );
+    $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );
+
+    if ( $can_write_principal ) {
+      $del_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&delete_member=##principal_id##" class="submit">'.translate('Remove').'</a>';
+      $browser->AddColumn( 'action', translate('Action'), 'center', '', "'$edit_link&nbsp;$del_link'" );
+    }
+
+    $browser->SetOrdering( 'displayname', 'A' );
+
+    $browser->SetJoins( "group_member LEFT JOIN dav_principal ON (member_id = principal_id) " );
+    $browser->SetWhere( 'user_active AND group_id = '.$id );
+
+    if ( $c->enable_row_linking ) {
+      $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
+    }
+    else {
+      $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+    }
+    $browser->DoQuery();
+    $page_elements[] = $browser;
+
+    if ( $can_write_principal ) {
+      $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+      $extra_row = array( 'group_id' => -1 );
+      $browser->MatchedRow('group_id', -1, 'edit_group_row');
+      $extra_row = (object) $extra_row;
+      $browser->AddRow($extra_row);
+    }
+  }
+
+
+    $grantrow = new Editor("Grants", "grants");
+    $grantrow->SetSubmitName( 'savegrantrow' );
+    $edit_grant_clause = '';
+    if ( isset($_GET['edit_grant']) ) {
+      $edit_grant_clause = ' AND to_principal != '.intval($_GET['edit_grant']);
+    }
+    $grantrow->SetLookup( 'to_principal', 'SELECT principal_id, displayname FROM dav_principal WHERE principal_id NOT IN (SELECT to_principal FROM grants WHERE by_principal = '.$id.$edit_grant_clause.') ORDER BY fullname' );
+    if ( $can_write_principal ) {
+      if ( $grantrow->IsSubmit() ) {
+        if ( $grantrow->IsUpdate() )
+          $c->messages[] = translate('Updating grants by this Principal');
+        else
+          $c->messages[] = translate('Granting new privileges from this Principal');
+        $_POST['by_principal'] = $id;
+        $to_principal = intval($_POST['to_principal']);
+        $orig_to_id =  intval($_POST['orig_to_id']);
+        $grantrow->SetWhere( "by_principal=".qpg($id)." AND to_principal=$orig_to_id");
+        if ( isset($_POST['grant_privileges']) ) {
+          $privilege_bitpos = array_flip($privilege_names);
+          $priv_names = array_keys($_POST['grant_privileges']);
+          $privs = privilege_to_bits($priv_names);
+          $_POST['privileges'] = sprintf('%024s',decbin($privs));
+          $grantrow->Assign('privileges', $privs_dec);
+        }
+        $grantrow->Write( );
+        unset($_GET['to_principal']);
+      }
+      elseif ( isset($_GET['delete_grant']) ) {
+        $qry = new AwlQuery("DELETE FROM grants WHERE by_principal=:grantor_id AND to_principal = :to_principal",
+                              array( ':grantor_id' => $id, ':to_principal' => intval($_GET['delete_grant']) ));
+        $qry->Exec('principal-edit');
+        $c->messages[] = translate('Deleted a grant from this Principal');
+      }
+    }
+
+    function edit_grant_row( $row_data ) {
+      global $grantrow, $id, $c, $privilege_xlate, $privilege_names;
+      global $btn_all, $btn_all_title, $btn_rw, $btn_rw_title, $btn_read, $btn_read_title;
+      global $btn_fb, $btn_fb_title, $btn_sd, $btn_sd_title, $btn_ss, $btn_ss_title;
+
+      if ( $row_data->to_principal > -1 ) {
+        $grantrow->SetRecord( $row_data );
+      }
+
+      $grant_privileges = bindec($grantrow->Value('grant_privileges'));
+      $privileges_set = '<div id="privileges">';
+      for( $i=0; $i < count($privilege_names); $i++ ) {
+        $privilege_set = ( (1 << $i) & $grant_privileges ? ' CHECKED' : '');
+        $privileges_set .= '<label class="privilege"><input name="grant_privileges['.$privilege_names[$i].']" id="grant_privileges_'.$privilege_names[$i].'" type="checkbox"'.$privilege_set.'>'.$privilege_xlate[$privilege_names[$i]].'</label>'."\n";
+      }
+      $privileges_set .= '</div>';
+
+      $orig_to_id = $row_data->to_principal;
+      $form_id = $grantrow->Id();
+      $form_url = preg_replace( '#&(edit|delete)_grant=\d+#', '', $_SERVER['REQUEST_URI'] );
+
+      $template = <<<EOTEMPLATE
+<form method="POST" enctype="multipart/form-data" id="form_$form_id" action="$form_url">
+  <td class="left" colspan="2"><input type="hidden" name="id" value="$id"><input type="hidden" name="orig_to_id" value="$orig_to_id">##to_principal.select##</td>
+  <td class="left" colspan="2">
+<input type="button" value="$btn_all" class="submit" title="$btn_all_title" onclick="toggle_privileges('grant_privileges', 'all', 'form_$form_id');">
+<input type="button" value="$btn_rw" class="submit" title="$btn_rw_title"
+ onclick="toggle_privileges('grant_privileges', 'read', 'write-properties', 'write-content', 'bind', 'unbind', 'read-free-busy',
+                            'read-current-user-privilege-set', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy',
+                            'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<input type="button" value="$btn_read" class="submit" title="$btn_read_title"
+ onclick="toggle_privileges('grant_privileges', 'read', 'read-free-busy', 'schedule-query-freebusy', 'read-current-user-privilege-set' );">
+<input type="button" value="$btn_fb" class="submit" title="$btn_fb_title"
+ onclick="toggle_privileges('grant_privileges', 'read-free-busy', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_sd" class="submit" title="$btn_sd_title"
+ onclick="toggle_privileges('grant_privileges', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy' );">
+<input type="button" value="$btn_ss" class="submit" title="$btn_ss_title"
+ onclick="toggle_privileges('grant_privileges', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
+<br>$privileges_set
+  <td class="center">##submit##</td>
+</form>
+
+EOTEMPLATE;
+
+      $grantrow->SetTemplate( $template );
+      $grantrow->Title("");
+
+      return $grantrow->Render();
+    }
+
+  $browser = new Browser(translate('Principal Grants'));
+
+  $browser->AddColumn( 'to_principal', translate('To ID'), 'right', '##principal_link##' );
+  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
+  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || to_principal || '\">' || to_principal || '</a>'" );
+  $browser->AddHidden( 'grant_privileges', 'privileges' );
+  $browser->AddColumn( 'displayname', translate('Display Name') );
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', 'privileges', '', '', 'privilege_format_function' );
+  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );
+
+  if ( $can_write_principal ) {
+    $del_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&delete_grant=##to_principal##" class="submit">'.translate('Revoke').'</a>';
+    $edit_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&edit_grant=##to_principal##" class="submit">'.translate('Edit').'</a>';
+    $browser->AddColumn( 'action', translate('Action'), 'center', '', "'$edit_link&nbsp;$del_link'" );
+  }
+
+  $browser->SetOrdering( 'displayname', 'A' );
+
+  $browser->SetJoins( "grants LEFT JOIN dav_principal ON (to_principal = principal_id) " );
+  $browser->SetWhere( 'by_principal = '.$id );
+
+  if ( $c->enable_row_linking ) {
+    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
+  }
+  else {
+    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+  }
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+
+  if ( $can_write_principal ) {
+    if ( isset($_GET['edit_grant']) ) {
+      $browser->MatchedRow('to_principal', $_GET['edit_grant'], 'edit_grant_row');
+    }
+    else if ( isset($id ) ) {
+      $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+      $extra_row = array( 'to_principal' => -1 );
+      $browser->MatchedRow('to_principal', -1, 'edit_grant_row');
+      $extra_row = (object) $extra_row;
+      $browser->AddRow($extra_row);
+    }
+  }
+
+
+  $browser = new Browser(translate('Access Tickets'));
+
+  $browser->AddColumn( 'ticket_id', translate('Ticket ID'), '', '' );
+  $browser->AddColumn( 'target', translate('Target'), '', '<td style="white-space:nowrap;">%s</td>', "'".$c->base_url.'/caldav.php'."' ||COALESCE(d.dav_name,c.dav_name)" );
+  $browser->AddColumn( 'expiry', translate('Expires'), '', '', 'TO_CHAR(expires,\'YYYY-MM-DD HH:MI:SS\')');
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
+  $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id='.$id.'&ticket_id=##URL:ticket_id##&subaction=delete_ticket';
+  $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
+
+  $browser->SetOrdering( 'target', 'A' );
+
+  $browser->SetJoins( 'access_ticket t LEFT JOIN collection c ON (target_collection_id=collection_id) LEFT JOIN caldav_data d ON (target_resource_id=dav_id)' );
+  $browser->SetWhere( 'dav_owner_id = '.intval($editor->Value('principal_id')) );
+
+//  $browser->AddRow( array( 'target' => '<a href="'.$rowurl.'&principal_id='.intval($editor->Value('principal_id')).'" class="submit">'.translate('Create Ticket').'</a>' ));
+
+  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+  if ( isset($delete_ticket_confirmation_required) ) {
+    $html = '<table><tr><td class="error">';
+    $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
+                translate('Deleting Ticket:'), $_GET['ticket_id'], $_SERVER['REQUEST_URI'],
+                $delete_ticket_confirmation_required,
+                translate('Confirm Deletion of the Ticket'),
+                translate('The access ticket will be deleted.') );
+    $html .= "</td></tr></table>\n";
+    $page_elements[] = $html;
+  }
+
+
+  $browser = new Browser(translate('Principal Collections'));
+
+  $browser->AddColumn( 'collection_id', translate('ID'), 'right', '##collection_link##' );
+  $rowurl = $c->base_url . '/admin.php?action=edit&t=collection&id=';
+  $browser->AddHidden( 'collection_link', "'<a href=\"$rowurl' || collection_id || '\">' || collection_id || '</a>'" );
+  $browser->AddColumn( 'dav_name', translate('Path') );
+  $browser->AddColumn( 'dav_displayname', translate('Display Name') );
+  $browser->AddColumn( 'publicly_readable', translate('Public'), 'centre', '', 'CASE WHEN publicly_readable THEN \''.translate('Yes').'\' ELSE \''.translate('No').'\' END' );
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '',
+          "COALESCE( privileges_list(default_privileges), '[".translate('from principal')."]')" );
+  $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id='.$id.'&dav_name=##URL:dav_name##&subaction=delete_collection';
+  $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
+
+  $browser->SetOrdering( 'dav_name', 'A' );
+
+  $browser->SetJoins( "collection " );
+  $browser->SetWhere( 'user_no = '.intval($editor->Value('user_no')) );
+
+  $browser->AddRow( array( 'dav_name' => '<a href="'.$rowurl.'&user_no='.intval($editor->Value('user_no')).'" class="submit">'.translate('Create Collection').'</a>' ));
+
+  if ( $c->enable_row_linking ) {
+    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
+  }
+  else {
+    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+  }
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+  if ( isset($delete_collection_confirmation_required) ) {
+    $html = '<table><tr><td class="error">';
+    $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
+                translate('Deleting Collection:'), $_GET['dav_name'], $_SERVER['REQUEST_URI'],
+                $delete_collection_confirmation_required,
+                translate('Confirm Deletion of the Collection'),
+                translate('All collection data will be unrecoverably deleted.') );
+    $html .= "</td></tr></table>\n";
+    $page_elements[] = $html;
+  }
+
+  $browser = new Browser(translate('Bindings to this Principal\'s Collections'));
+  $browser->AddColumn( 'bind_id', translate('ID'), '', '' );
+  $browser->AddHidden( 'b.dav_owner_id' );
+  $browser->AddHidden( 'p.principal_id' );
+  $browser->AddColumn( 'bound_as', translate('Bound As'), '', '<td style="white-space:nowrap;">%s</td>', "'".$c->base_url.'/caldav.php'."' ||b.dav_name" );
+  $browser->AddColumn( 'access_ticket_id', translate('Ticket ID'), '', '' );
+  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
+  $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id=##principal_id##&bind_id=##bind_id##&subaction=delete_binding';
+  $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
+
+  $browser->SetOrdering( 'target', 'A' );
+
+  $browser->SetJoins( 'dav_binding b LEFT JOIN collection c ON (bound_source_id=collection_id) LEFT JOIN access_ticket t ON (ticket_id=access_ticket_id) LEFT JOIN principal p USING(user_no)' );
+  $browser->SetWhere( 'p.principal_id = '.intval($editor->Value('principal_id')) );
+
+  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
+
+  $browser->DoQuery();
+  $page_elements[] = $browser;
+
+  if ( isset($delete_binding_confirmation_required) ) {
+    $html = '<table><tr><td class="error">';
+    $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
+                translate('Deleting Binding:'), $_GET['bind_id'], $_SERVER['REQUEST_URI'],
+                $delete_binding_confirmation_required,
+                translate('Confirm Deletion of the Binding'),
+                translate('The binding will be deleted.') );
+    $html .= "</td></tr></table>\n";
+    $page_elements[] = $html;
+  }
+  
+}
Index: contrib/davical/inc/log_caldav_action.php
===================================================================
--- contrib/davical/inc/log_caldav_action.php	(revision 3733)
+++ contrib/davical/inc/log_caldav_action.php	(revision 3733)
@@ -0,0 +1,40 @@
+<?php
+/**
+* Allows logging of CalDAV actions (PUT/DELETE) for possible export or sync
+* through some other glue.
+*
+* @package   davical
+* @category Technical
+* @subpackage   logging
+* @author    Andrew McMillan <andrew@morphoss.com>
+* @copyright Morphoss Ltd
+* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
+*
+* This file is intended to be used as a template, perhaps the user of this service
+* will want to log actions in a very different manner and this can be used as an
+* example of how to go about doing that.
+*/
+
+/**
+* Log the action
+* @param string $action_type INSERT / UPDATE or DELETE
+* @param string $uid The UID of the modified item
+* @param integer $user_no The user owning the containing collection.
+* @param integer $collection_id The ID of the containing collection.
+* @param string $dav_name The DAV path of the item, relative to the DAViCal base path
+*/
+function log_caldav_action( $action_type, $uid, $user_no, $collection_id, $dav_name ) {
+  global $c;
+
+  $logline = sprintf( '%s %s %s %s %s %s', gmdate('Ymd\THis\Z'), $action_type, $uid, $user_no, $collection_id, $dav_name );
+  if ( !isset($c->action_log_name) ) {
+    error_log( $logline );
+    return;
+  }
+
+  $logline .= "\n";
+
+  $logfile = fopen( $c->action_log_name, "a+" );
+  fwrite( $logfile, $logline );
+  fclose($logfile);
+}
