/** * Return a DAV::href XML element, or an array of them * @param mixed $url The URL (or array of URLs) to be wrapped in DAV::href tags * * @return XMLElement The newly created XMLElement object. */ function href($url) { if (is_array($url)) { $set = array(); foreach ($url as $href) { $set[] = $this->href($href); } return $set; } if (preg_match('[@+ ]', $url)) { trace_bug('URL "%s" was not encoded before call to XMLDocument::href()', $url); $url = str_replace('%2F', '/', rawurlencode($url)); } return $this->NewXMLElement('href', $url, false, 'DAV:'); }
/** * Actually write the resource to the database. All checking of whether this is reasonable * should be done before this is called. * * @param DAVResource $resource The resource being written * @param string $caldav_data The actual data to be written * @param DAVResource $collection 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 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(DAVResource $resource, $caldav_data, DAVResource $collection, $author, &$etag, $put_action_type, $caldav_context, $log_action = true, $weak_etag = null) { global $tz_regex, $session; $path = $resource->bound_from(); $user_no = $collection->user_no(); $vcal = new vCalendar($caldav_data); $resources = $vcal->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]; if (!$first instanceof vComponent) { print $vcal->Render(); fatal('This is not a vComponent!'); } $resource_type = $first->GetType(); } $collection_id = $collection->collection_id(); $qry = new AwlQuery(); $qry->Begin(); $dav_params = array(':etag' => $etag, ':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $author, ':weak_etag' => $weak_etag); $calitem_params = array(':etag' => $etag); if ($put_action_type == 'INSERT') { $qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id, null AS caldav_data'); } else { $qry->QDo('SELECT dav_id, caldav_data FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path)); } if ($qry->rows() != 1 || !($row = $qry->Fetch())) { // No dav_id? => We're toast! trace_bug('No dav_id for "%s" on %s!!!', $path, $create_resource ? 'create' : 'update'); rollback_on_error($caldav_context, $user_no, $path); return false; } $dav_id = $row->dav_id; $old_dav_data = $row->caldav_data; $dav_params[':dav_id'] = $dav_id; $calitem_params[':dav_id'] = $dav_id; $due = null; if ($first->GetType() == 'VTODO') { $due = $first->GetPValue('DUE'); } $calitem_params[':due'] = $due; $dtstart = $first->GetPValue('DTSTART'); if (empty($dtstart)) { $dtstart = $due; } $calitem_params[':dtstart'] = $dtstart; $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 { // In this case we'll construct the SQL directly as a calculation relative to :dtstart $dtend = 'NULL'; if ($first->GetPValue('DURATION') != '' and $dtstart != '') { $duration = trim(preg_replace('#[PT]#', ' ', $first->GetPValue('DURATION'))); if ($duration == '') { $duration = '0 seconds'; } $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. * */ $dtstart_prop = $first->GetProperty('DTSTART'); $value_type = $dtstart_prop->GetParameterValue('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'; } } } $dtstamp = $first->GetPValue('DTSTAMP'); if (!isset($dtstamp) || $dtstamp == '') { // Strictly, we're dealing with an out of spec component here, but we'll try and survive $dtstamp = gmdate('Ymd\\THis\\Z'); } $calitem_params[':dtstamp'] = $dtstamp; $last_modified = $first->GetPValue('LAST-MODIFIED'); if (!isset($last_modified) || $last_modified == '') { $last_modified = $dtstamp; } $dav_params[':modified'] = $last_modified; $calitem_params[':modified'] = $last_modified; $created = $first->GetPValue('CREATED'); if ($created == '00001231T000000Z') { $created = '20001231T000000Z'; } $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_olson = 'Turkmenikikamukau'; // I really hope this location doesn't exist! $tzid = GetTZID($first); if (!empty($tzid)) { $timezones = $vcal->GetComponents('VTIMEZONE'); foreach ($timezones as $k => $tz) { if ($tz->GetPValue('TZID') != $tzid) { /** * We'll skip any tz definitions that are for a TZID other than the DTSTART/DUE on the first VEVENT/VTODO */ dbg_error_log('ERROR', ' Event uses TZID[%s], skipping included TZID[%s]!', $tz->GetPValue('TZID'), $tzid); continue; } $olson = olson_from_tzstring($tzid); if (empty($olson)) { $olson = $tz->GetPValue('X-LIC-LOCATION'); if (!empty($olson)) { $olson = olson_from_tzstring($olson); } } } dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($olson) ? $olson : ''); if (!empty($olson) && $olson != $last_olson && preg_match($tz_regex, $olson)) { dbg_error_log('PUT', ' Setting timezone to %s', $olson); if ($olson != '') { $qry->QDo('SET TIMEZONE TO \'' . $olson . "'"); } $last_olson = $olson; } $params = array(':tzid' => $tzid); $qry = new AwlQuery('SELECT 1 FROM timezones WHERE tzid = :tzid', $params); if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) { $params[':olson_name'] = $olson; $params[':vtimezone'] = isset($tz) ? $tz->Render() : null; $qry->QDo('INSERT INTO timezones (tzid, olson_name, active, vtimezone) VALUES(:tzid,:olson_name,false,:vtimezone)', $params); } if (!isset($olson) || $olson == '') { $olson = $tzid; } } $qry->QDo('SELECT new_sync_token(0,' . $collection_id . ')'); $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[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE'); $calitem_params[':status'] = $first->GetPValue('STATUS'); if (!$collection->IsSchedulingCollection()) { if (do_scheduling_requests($vcal, $put_action_type == 'INSERT', $old_dav_data, true)) { $dav_params[':dav_data'] = $vcal->Render(null, true); $etag = null; } } if (!isset($dav_params[':modified'])) { $dav_params[':modified'] = 'now'; } if ($put_action_type == 'INSERT') { $sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag ) VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, :created, :modified, :collection_id, :weak_etag )'; $dav_params[':collection_id'] = $collection_id; $dav_params[':user_no'] = $user_no; $dav_params[':dav_name'] = $path; $dav_params[':created'] = isset($created) && $created != '' ? $created : $dtstamp; } else { $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user, modified=:modified, weak_etag=:weak_etag WHERE dav_id=:dav_id'; } $qry = new AwlQuery($sql, $dav_params); if (!$qry->Exec('PUT', __LINE__, __FILE__)) { fatal('Insert into calendar_item failed...'); rollback_on_error($caldav_context, $user_no, $path); return false; } 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, :dav_id, :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; $calitem_params[':collection_id'] = $collection_id; $calitem_params[':user_no'] = $user_no; $calitem_params[':dav_name'] = $path; $calitem_params[':created'] = $dav_params[':created']; } 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, due=:due, percent_complete=:percent_complete, status=:status WHERE dav_id=:dav_id EOSQL; $sync_change = 200; } write_alarms($dav_id, $first); write_attendees($dav_id, $vcal); 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(); if (function_exists('post_commit_action')) { post_commit_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path); } // Uncache anything to do with the collection $cache = getCacheInstance(); $cache_ns = 'collection-' . preg_replace('{/[^/]*$}', '/', $path); $cache->delete($cache_ns, null); dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path); return true; // Success! }
/** * 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_id(); break; case 'principal_id': if (!isset($this->principal)) { $this->FetchPrincipal(); } return $this->principal->principal_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; } case 'resource': if (!isset($this->resource)) { $this->FetchResource(); } return clone $this->resource; break; case 'dav-data': if (!isset($this->resource)) { $this->FetchResource(); } trace_bug("Exists " . ($this->exists ? "true" : "false")); return $this->resource->caldav_data; break; case 'principal': if (!isset($this->principal)) { $this->FetchPrincipal(); } return clone $this->principal; break; default: if (isset($this->{$name})) { if (!is_object($this->{$name})) { return $this->{$name}; } return clone $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}; } } } if (isset($this->{$name})) { if (!is_object($this->{$name})) { return $this->{$name}; } return clone $this->{$name}; } // dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name ); } return $value; }
/** * Writes the data to a member in the collection and returns the segment_name of the * resource in our internal namespace. * * @param vCalendar $vcal The resource to be written. * @param boolean $create_resource True if this is a new resource. * @param boolean $do_scheduling True if we should also do scheduling for this write. Default false. * @param string $segment_name The name of the resource within the collection, or null if this * call should invent one based on the UID of the vCalendar. * @param boolean $log_action Whether to log this action. Defaults to false since this is normally called * in situations where one is writing secondary data. * @return string The segment_name of the resource within the collection, as written, or false on failure. */ function WriteCalendarMember(vCalendar $vcal, $create_resource, $do_scheduling = false, $segment_name = null, $log_action = false) { if (!$this->IsSchedulingCollection() && !$this->IsCalendar()) { dbg_error_log('PUT', '"%s" is not a calendar or scheduling collection!', $this->dav_name); return false; } global $session, $caldav_context; $resources = $vcal->GetComponents('VTIMEZONE', false); // Not matching VTIMEZONE $user_no = $this->user_no(); $collection_id = $this->collection_id(); if (!isset($resources[0])) { dbg_error_log('PUT', 'No calendar content!'); rollback_on_error($caldav_context, $user_no, $this->dav_name . '/' . $segment_name, translate('No calendar content'), 412); return false; } else { $first = $resources[0]; $resource_type = $first->GetType(); } $uid = $vcal->GetUID(); if (empty($segment_name)) { $segment_name = $uid . '.ics'; } $path = $this->dav_name() . $segment_name; $caldav_data = $vcal->Render(); $etag = md5($caldav_data); $weak_etag = null; $qry = new AwlQuery(); $existing_transaction_state = $qry->TransactionState(); if ($existing_transaction_state == 0) { $qry->Begin(); } if ($create_resource) { $qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id'); } else { $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path)); } if ($qry->rows() != 1 || !($row = $qry->Fetch())) { if (!$create_resource) { // Looks like we will have to create it, even if the caller thought we wouldn't $qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id'); if ($qry->rows() != 1 || !($row = $qry->Fetch())) { // No dav_id? => We're toast! trace_bug('No dav_id for "%s" on %s!!!', $path, $create_resource ? 'create' : 'update'); rollback_on_error($caldav_context, $user_no, $path); return false; } $create_resource = true; dbg_error_log('PUT', 'Unexpected need to create resource at "%s"', $path); } } $dav_id = $row->dav_id; $calitem_params = array(':dav_name' => $path, ':user_no' => $user_no, ':etag' => $etag, ':dav_id' => $dav_id); $dav_params = array_merge($calitem_params, array(':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $session->user_no, ':weak_etag' => $weak_etag)); if (!$this->IsSchedulingCollection() && $do_scheduling) { if (do_scheduling_requests($vcal, $create_resource)) { $dav_params[':dav_data'] = $vcal->Render(null, true); $etag = null; } } if ($create_resource) { $sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag ) VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )'; $dav_params[':collection_id'] = $collection_id; } else { $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 dav_id=:dav_id'; } if (!$qry->QDo($sql, $dav_params)) { rollback_on_error($caldav_context, $user_no, $path); return false; } $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'); /* * It seems that some calendar clients don't set a class... * RFC2445, 4.8.1.3: Default is PUBLIC */ if ($this->IsPublicOnly() || !isset($class) || $class == '') { $class = 'PUBLIC'; } $calitem_params[':class'] = $class; /** Calculate what timezone to set, first, if possible */ $last_olson = 'Turkmenikikamukau'; // I really hope this location doesn't exist! $tzid = self::GetTZID($first); if (!empty($tzid)) { $tz = $vcal->GetTimeZone($tzid); $olson = $vcal->GetOlsonName($tz); if (!empty($olson) && $olson != $last_olson) { dbg_error_log('PUT', ' Setting timezone to %s', $olson); $qry->QDo('SET TIMEZONE TO \'' . $olson . "'"); $last_olson = $olson; } } $created = $first->GetPValue('CREATED'); if ($created == '00001231T000000Z') { $created = '20001231T000000Z'; } $calitem_params[':created'] = $created; $calitem_params[':tzid'] = $tzid; $calitem_params[':uid'] = $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 ($create_resource) { $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; } if (!$this->IsSchedulingCollection()) { $this->WriteCalendarAlarms($dav_id, $vcal); $this->WriteCalendarAttendees($dav_id, $vcal); $put_action_type = $create_resource ? 'INSERT' : 'UPDATE'; 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)); if ($existing_transaction_state == 0) { $qry->Commit(); } dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $session->user_no, $etag, $path); return $segment_name; }
/** * Handle Basic HTTP Authentication (not secure unless https) */ function BasicAuthSession() { global $c; /** * Get HTTP Auth to work with PHP+FastCGI */ if (!isset($_SERVER['AUTHORIZATION']) && isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) { $_SERVER['AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION']; } if (isset($_SERVER['AUTHORIZATION']) && !empty($_SERVER['AUTHORIZATION'])) { list($type, $cred) = explode(" ", $_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']) && (isset($_SERVER["REMOTE_USER"]) && !empty($_SERVER["REMOTE_USER"]) || isset($_SERVER["REDIRECT_REMOTE_USER"]) && !empty($_SERVER["REDIRECT_REMOTE_USER"]))) { if (is_array($c->authenticate_hook['server_auth_type']) && in_array(strtolower($_SERVER['AUTH_TYPE']), array_map('strtolower', $c->authenticate_hook['server_auth_type'])) || !is_array($c->authenticate_hook['server_auth_type']) && strtolower($c->authenticate_hook['server_auth_type']) == strtolower($_SERVER['AUTH_TYPE'])) { /** * The authentication has happened in the server, and we should accept it. */ if (isset($_SERVER["REMOTE_USER"])) { $_SERVER['PHP_AUTH_USER'] = $_SERVER['REMOTE_USER']; } else { $_SERVER['PHP_AUTH_USER'] = $_SERVER['REDIRECT_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 ($p = $this->CheckPassword($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) { if (isset($p->active) && !isset($p->user_active)) { trace_bug('Some authentication failed to return a dav_principal record and needs fixing.'); $p->user_active = $p->active; } /** * Maybe some external authentication didn't return false for an inactive * user, so we'll be pedantic here. */ if ($p->user_active) { $this->AssignSessionDetails($p); return; } } } if (isset($c->allow_unauthenticated) && $c->allow_unauthenticated) { $this->AssignSessionDetails('unauthenticated'); $this->logged_in = false; return; } $this->AuthFailedResponse(); // Does not return }
/** * Does the actual processing of the iTIP CANCEL message on behalf of an ATTENDEE, * which generally means writing it into the ATTENDEE's default calendar. * * @param vCalendar $vcal The message. * @param vProperty $attendee * @param WritableCollection $attendee_calendar */ function processItipCancel(vCalendar $vcal, vProperty $attendee, WritableCollection $attendee_calendar, Principal $attendee_principal) { global $request; dbg_error_log('schedule', 'Processing iTIP CANCEL to %s', $attendee->Value()); header("Debug: Could maybe do the iMIP message dance for attendee " . $attendee->Value()); if (!$attendee_calendar->Exists()) { if (doImipMessage('CANCEL', $attendee_principal->email(), $vcal)) { return '1.1'; // Scheduling whoosit 'Sent' } else { dbg_error_log('ERROR', 'Default calendar at "%s" does not exist for attendee "%s"', $attendee_calendar->dav_name(), $attendee->Value()); return '5.2'; // No scheduling support for user } } $sql = 'SELECT caldav_data.dav_name FROM caldav_data JOIN calendar_item USING(dav_id) '; $sql .= 'WHERE caldav_data.collection_id IN (SELECT collection_id FROM collection WHERE is_calendar AND user_no =?) '; $sql .= 'AND uid=? LIMIT 1'; $uids = $vcal->GetPropertiesByPath('/VCALENDAR/*/UID'); if (count($uids) == 0) { dbg_error_log('schedule', 'No UID in VCALENDAR - giving up on CANCEL processing.'); return '3.8'; } $uid = $uids[0]->Value(); $qry = new AwlQuery($sql, $attendee_principal->user_no(), $uid); if (!$qry->Exec('schedule', __LINE__, __FILE__) || $qry->rows() < 1) { dbg_error_log('schedule', 'Could not find ATTENDEE copy of original event - not trying to DELETE it!'); return '1.2'; } $row = $qry->Fetch(); if ($attendee_calendar->actualDeleteCalendarMember($row->dav_name) === false) { dbg_error_log('ERROR', 'Could not delete calendar member %s for %s', $row->dav_name(), $attendee->Value()); trace_bug('Failed to write scheduling resource.'); return '5.2'; } return '1.2'; // Scheduling invitation delivered successfully }