Overview

Packages

  • awl
    • caldav-client-v2
    • RRule
  • davical
    • authentication
      • drivers
    • caldav
    • DAViCalSession
    • DAVTicket
    • external-bind
    • feed
    • HTTPAuthSession
    • iSchedule
    • iSchedule-POST
    • logging
    • metrics
    • Principal
    • propfind
    • PublicSession
    • Request
    • Resource
    • tzservice
  • None

Classes

  • AtomEntry
  • AtomFeed
  • AtomXHTMLContent
  • CalDAVClient
  • CalDAVRequest
  • CalendarInfo
  • CheckResult
  • DAViCalSession
  • DAVPrincipal
  • DAVResource
  • DAVTicket
  • FakeSession
  • HTTPAuthSession
  • imapPamDriver
  • iSchedule
  • ldapDriver
  • Principal
  • PublicSession
  • pwauthPamDriver
  • RepeatRule
  • RepeatRuleDateRange
  • RepeatRuleDateTime
  • RepeatRuleTimeZone
  • Rfc5545Duration
  • rimapPamDriver
  • setupFakeSession
  • squidPamDriver
  • Tools
  • VCard
  • VTimezone
  • WritableCollection

Functions

  • access_ticket_browser
  • add_failure
  • add_proxy_response
  • array_values_mapping
  • auth_functions_deprecated
  • AuthExternalAWL
  • binding_row_editor
  • bindings_to_other_browser
  • bindings_to_us_browser
  • bits_to_privilege
  • build_dependencies_table
  • build_privileges_html
  • build_site_statistics
  • BuildSqlFilter
  • caldav_get_feed
  • calquery_apply_filter
  • cardquery_apply_filter
  • catch_setup_errors
  • check_awl_version
  • check_calendar
  • check_curl
  • check_database_connection
  • check_datetime
  • check_davical_version
  • check_for_expansion
  • check_gettext
  • check_iconv
  • check_ldap
  • check_magic_quotes_gpc
  • check_magic_quotes_runtime
  • check_pdo
  • check_pdo_pgsql
  • check_pgsql
  • check_real_php
  • check_schema_version
  • check_string
  • check_suhosin_server_strip
  • check_xml
  • checkiSchedule
  • collection_privilege_format_function
  • component_to_xml
  • confirm_delete_bind_in
  • confirm_delete_binding
  • confirm_delete_collection
  • confirm_delete_principal
  • confirm_delete_ticket
  • ConstructURL
  • controlRequestContainer
  • create_external
  • CreateDefaultRelationships
  • CreateHomeCalendar
  • CreateHomeCollections
  • DateToISODate
  • DeconstructURL
  • delete_collection
  • deliverItipCancel
  • display_status
  • do_error
  • do_scheduling_for_delete
  • do_scheduling_reply
  • do_scheduling_requests
  • doImipMessage
  • doItipAttendeeReply
  • doItipOrganizerCancel
  • early_catch_fatal_error
  • early_exception_handler
  • edit_binding_row
  • edit_grant_row_collection
  • edit_grant_row_principal
  • edit_group_row
  • edit_ticket_row
  • errorResponse
  • expand_event_instances
  • expand_properties
  • expand_timezone_onsets
  • export_iCalendar
  • fetch_external
  • fix_unique_member
  • generateKeys
  • get_address_properties
  • get_collection_contents
  • get_freebusy
  • get_href_containers
  • get_phpinfo
  • getComponentRange
  • GetItip
  • getPrincipalByID
  • getStaticLdap
  • getStatusMessage
  • GetTZID
  • getUserByEMail
  • getUserByID
  • getUserByName
  • getVCalendarRange
  • grant_row_editor
  • group_members_browser
  • group_memberships_browser
  • group_row_editor
  • handle_cancel_request
  • handle_freebusy_request
  • handle_schedule_reply
  • handle_schedule_request
  • handle_subaction
  • hyperlink
  • i18n
  • IMAP_PAM_check
  • import_addressbook_collection
  • import_calendar_collection
  • import_collection
  • ischedule_cancel
  • ischedule_freebusy_request
  • ischedule_get
  • ischedule_request
  • ISODateToHTTPDate
  • late_catch_fatal_error
  • LDAP_check
  • local_session_sql
  • log_caldav_action
  • log_setup_error
  • logRequestHeaders
  • make_help_link
  • obfuscated_event
  • olson_from_vtimezone
  • principal_collection_browser
  • principal_editor
  • principal_grants_browser
  • principal_privilege_format_function
  • print_metric
  • privilege_to_bits
  • privileges_to_XML
  • process_ace
  • processItipCancel
  • property_response
  • public_events_only
  • PWAUTH_PAM_check
  • rdate_expand
  • RIMAP_check
  • rollback
  • rollback_on_error
  • rrule_expand
  • send_dav_header
  • send_page_header
  • simple_write_resource
  • SqlFilterCardDAV
  • SqlFilterFragment
  • SQUID_PAM_check
  • SRVFormat
  • SRVOk
  • sync_LDAP
  • sync_LDAP_groups
  • sync_user_from_LDAP
  • ticket_row_editor
  • unicodeToUtf8
  • update_external
  • UpdateCollectionTimezones
  • UpdateUserFromExternal
  • utf8ToUnicode
  • write_alarms
  • write_attendees
  • write_resource
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 
<?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) {
  if ( !headers_sent() ) {
    header("Content-type: text/plain");
    header( sprintf("HTTP/1.1 %d %s", 500, getStatusMessage(500)) );
  }
  else {
    echo "<pre>\n";
    // Too late to set resultcode :-(
  }
  try {
    @ob_flush(); // Seems like it should be better to do the following but is problematic on PHP5.3 at least: while ( ob_get_level() > 0 ) ob_end_flush();
  }
  catch( Exception $ignored ) {}
  echo "Exception [".$e->getCode()."] ".$e->getmessage()."\n";
  echo "At line ", $e->getLine(), " of ", $e->getFile(), "\n";
  echo "================= Stack Trace ===================\n";

  $trace = array_reverse($e->getTrace());
  foreach( $trace AS $k => $v ) {
    printf( "%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');

function early_catch_fatal_error() {
  global $request;

  if ( !empty($request) ) return;

  // Getting Last Error
  $e =  error_get_last();

  // Check if Last error is of type FATAL
  if (isset($e['type']) && $e['type'] == E_ERROR) {
    if ( !headers_sent() ) {
      header("Content-type: text/plain");
      header( sprintf("HTTP/1.1 %d %s", 500, getStatusMessage(500)) );
    }
    echo "PHP Fatal error: ".$e['message']."\n";
    echo "At line ", $e['line'], " of ", $e['file'], "\n";
    error_log("PHP Fatal Error: '".$e['message']. "' at line ". $e['line']. " of ". $e['file']);
  }
}
register_shutdown_function('early_catch_fatal_error');

$c->default_timezone = ini_get ( 'date.timezone' );
if (empty ( $c->default_timezone )) {
    $c->default_timezone = 'UTC';
    if (isset ( $_SERVER ['HTTP_X_DAVICAL_TESTCASE'] )) {
        $c->default_timezone = 'Pacific/Auckland';
    }
}

// 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    = 'calendar';
$c->home_addressbook_name = 'addresses';
$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->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->enable_auto_schedule = true;

$c->schema_major = $c->schema_minor = $c->schema_patch = 0;

$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 ( isset($skip_errors) ) $skip_errors = true;
if ( ! @include_once('AWLUtilities.php') ) {
  if ( isset($skip_errors) ) $skip_errors = false;
  $try_paths = array(
        '../../awl/inc'
      , '/usr/share/awl/inc'        // Where it ends up on Debian
      , '/usr/share/php/awl/inc'    // Fedora's standard for PHP libraries
      , '/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";
    @ob_flush(); exit(0);
  }
}
if ( isset($skip_errors) ) $skip_errors = false;

// 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..." );
}


/**
* 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');
  @ob_flush();  exit(0);
}
$config_warnings = trim(ob_get_contents());
ob_end_clean();

/**
* Override server-detected variables with those from X-Forwarded headers
*/
if ( isset($c->trust_x_forwarded) && $c->trust_x_forwarded ) {
  if ( isset($_SERVER['X-Real-IP']) ) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['X-Real-IP'];
  } elseif ( isset($_SERVER['X-Forwarded-For']) ) {
    list($_SERVER['REMOTE_ADDR'], $rest) = explode( ',', $_SERVER['X-Forwarded-For']);
  }
  if ( isset($_SERVER['X-Forwarded-Proto']) ) {
    if ($_SERVER['X-Forwarded-Proto'] == 'https') {
      $_SERVER['HTTPS'] = 'on';
    } else {
      $_SERVER['HTTPS'] = 'off';
    }
  }
  if ( isset($_SERVER['X-Forwarded-Port']) ) {
    $_SERVER['SERVER_PORT'] = $_SERVER['X-Forwarded-Port'];
  }
}

/**
* 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')
                          && (!isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 80) )
                     || ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'
                          && (!isset($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == 443) )
                     ? ''
                     : ':'.$_SERVER['SERVER_PORT']
                 ) );
$c->protocol_server_port_script = $c->protocol_server_port . ($_SERVER['SCRIPT_NAME'] == '/index.php' ? '' : $_SERVER['SCRIPT_NAME']);


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, (isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '$_SERVER[PATH_INFO] undefined'), $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);
if ( function_exists('awl_set_locale') ) {
  awl_set_locale($c->default_locale);
  init_gettext( 'davical', $c->locale_path );
}

/**
* Work out our version
*
*/
$c->code_version = 0;
$c->want_awl_version = '0.57';
$c->version_string = '1.1.5'; // 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,3,2);
$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($c->default_timezone) ) {
    $qry->QDo('SET TIMEZONE TO ?', $c->default_timezone );
  }
}
date_default_timezone_set ( $c->default_timezone );

require_once('Principal.php');

/**
 * 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;

  if ( ! isset($c->_url_script_path) ) {
    $c->protocol_server_port_script = str_replace( 'index.php', 'caldav.php', $c->protocol_server_port_script);
    $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 .= str_replace( '%2F', '/', rawurlencode($partial_path));
  $url = preg_replace( '#^(https?://.+)//#', '$1/', $url );  // Ensure we don't double any '/'
  $url = preg_replace('#^https?://[^/]+#', '', $url );       // Remove any protocol + hostname portion

  if ( strstr($url, 'caldav.php/caldav.php') !== false ) {
//    trace_bug('Duplicated caldav.php/ in URL "%s" from partial_path=%s, force_script=%s', $url, $partial_path, ($force_script?'true':'false'));
    $url = str_replace( 'caldav.php/caldav.php', 'caldav.php', $url );  // Ensure we don't double any 'caldav.php/'
  }

  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);
  $dav_name = trim($dav_name);

  /** 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 ) {
  // It is necessary to use English for this, explicitly.  See Debian BTS Bug#661985 for more info.
  $month = gmstrftime('%m', strtotime($isodate));
  switch( intval($month) ) {
    case 1: $month = 'Jan'; break;
    case 2: $month = 'Feb'; break;
    case 3: $month = 'Mar'; break;
    case 4: $month = 'Apr'; break;
    case 5: $month = 'May'; break;
    case 6: $month = 'Jun'; break;
    case 7: $month = 'Jul'; break;
    case 8: $month = 'Aug'; break;
    case 9: $month = 'Sep'; break;
    case 10: $month = 'Oct'; break;
    case 11: $month = 'Nov'; break;
    case 12: $month = 'Dec'; break;
    default:
      throw new Exception('Invalid month '.$month);
  }
  // Use strtotime since strptime is not available on Windows platform.
  return( gmstrftime('%a, %d '.$month.' %Y %H:%M:%S 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, $in_utc=false ) {
  // Use strtotime since strptime is not available on Windows platform.
  if ( $in_utc ) return( gmdate('Ymd\THis\Z', strtotime($indate)) );
  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;
      case 'fake_privilege_for_input'        : 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' || $resourcetype == '*') && ($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' || $resourcetype == '*') && ($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' || $resourcetype == '*') && ($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;
}

DAViCal API documentation generated by ApiGen