/**
  * 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!
}
Exemple #3
0
 /**
  * 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
}