* @copyright Catalyst .Net Ltd, Morphoss Ltd * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once("AwlCache.php"); require_once("XMLDocument.php"); require_once("DAVPrincipal.php"); include("DAVTicket.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 DAVPrincipal */ var $principal; /** * The 'current_user_principal_xml' the DAV:current-user-principal answer. An * XMLElement object with an or 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 value of any 'Destionation:' header, if present. */ var $destination; /** * 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->options = $options; if ( !isset($this->options['allow_by_email']) ) $this->options['allow_by_email'] = false; if ( !isset($c->raw_post) ) $c->raw_post = file_get_contents( 'php://input'); $this->raw_post = $c->raw_post; 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']; if ( preg_match('{^(https?)://([a-z.-]+)(:[0-9]+)?(/.*)$}', $this->destination, $matches ) ) { $this->destination = $matches[4]; } } $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 /