Beispiel #1
0
        }
        $request->DoResponse(403, translate('PUT on a collection is only allowed for text/calendar content against a calendar collection'));
    }
    $dest->NeedPrivilege('DAV::write-content');
}
if (isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists()) {
    $request->PreconditionFailed(412, 'if-none-match', translate('A resource already exists at the destination.'));
}
if (isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag()) {
    $request->PreconditionFailed(412, 'if-match', sprintf('Existing resource ETag of "%s" does not match "%s"', $dest->unique_tag(), $request->etag_if_match));
}
$collection_id = $container->GetProperty('collection_id');
$qry = new AwlQuery();
$qry->Begin();
$etag = md5($request->raw_post);
$params = array(':user_no' => $dest->GetProperty('user_no'), ':dav_name' => $dest->bound_from(), ':etag' => $etag, ':dav_data' => $request->raw_post, ':session_user' => $session->user_no);
if ($dest->Exists()) {
    $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
          modified=current_timestamp, user_no=:user_no, caldav_type=\'VCARD\' WHERE dav_name=:dav_name';
    $response_code = 200;
    $qry->QDo($sql, $params);
    $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']));
} else {
    $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id )
          VALUES( :user_no, :dav_name, :etag, :dav_data, \'VCARD\', :session_user, current_timestamp, current_timestamp, :collection_id )';
    $params[':collection_id'] = $collection_id;
    $response_code = 201;
    $qry->QDo($sql, $params);
    $qry->QDo("SELECT currval('dav_id_seq') AS dav_id");
}
$row = $qry->Fetch();
Beispiel #2
0
$dav_resource = new DAVResource($request->path);
if (!$dav_resource->HavePrivilegeTo('DAV::write-content')) {
    $request->DoResponse(403);
}
if (!$dav_resource->Exists() && !$dav_resource->HavePrivilegeTo('DAV::bind')) {
    $request->DoResponse(403);
}
if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put']) && $c->dbg['put'])) {
    $fh = fopen('/tmp/PUT.txt', 'w');
    if ($fh) {
        fwrite($fh, $request->raw_post);
        fclose($fh);
    }
}
include_once 'caldav-PUT-functions.php';
controlRequestContainer($dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
$lock_opener = $request->FailIfLocked();
if ($dav_resource->IsCollection()) {
    if ($dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true) {
        $request->DoResponse(405);
        // Method not allowed
        return;
    }
    $appending = isset($_GET['mode']) && $_GET['mode'] == 'append';
    /**
     * CalDAV does not define the result of a PUT on a collection.  We treat that
     * as an import. The code is in caldav-PUT-functions.php
     */
    import_collection($request->raw_post, $request->user_no, $request->path, true, $appending);
    $request->DoResponse(200);
    return;
$proplist = array();
$props = $xmltree->GetPath('/DAV::sync-collection/DAV::prop/*');
if (!empty($props)) {
    foreach ($props as $k => $v) {
        $proplist[] = $v->GetNSTag();
    }
}
function display_status($status_code)
{
    return sprintf('HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code));
}
$collection = new DAVResource($request->path);
if (!$collection->Exists()) {
    $request->DoResponse(404);
}
$bound_from = $collection->bound_from();
$collection_path = $collection->dav_name();
$request_via_binding = $bound_from != $collection_path;
$params = array(':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token);
$sql = "SELECT new_sync_token( :sync_token, :collection_id)";
$qry = new AwlQuery($sql, $params);
if (!$qry->Exec("REPORT", __LINE__, __FILE__) || $qry->rows() <= 0) {
    $request->DoResponse(500, translate("Database error"));
}
$row = $qry->Fetch();
if (!isset($row->new_sync_token)) {
    /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */
    $sync_token = 0;
    $params[':sync_token'] = $sync_token;
    if (!$qry->QDo($sql, $params) || $qry->rows() <= 0) {
        $request->DoResponse(500, translate("Database error"));
            break;
            */
            default:
                dbg_error_log("cardquery", "Could not handle unknown tag '%s' in calendar query report", $tag);
                break;
        }
    }
    dbg_error_log("cardquery", "Generated SQL was '%s'", $sql);
    return array('sql' => $sql, 'params' => $params);
}
/**
* Something that we can handle, at least roughly correctly.
*/
$responses = array();
$target_collection = new DAVResource($request->path);
$bound_from = $target_collection->bound_from();
if (!$target_collection->Exists()) {
    $request->DoResponse(404);
}
if (!($target_collection->IsAddressbook() || $target_collection->IsSchedulingCollection())) {
    $request->DoResponse(403, translate('The addressbook-query report must be run against an addressbook collection'));
}
/**
* @todo Once we are past DB version 1.2.1 we can change this query more radically.  The best performance to
* date seems to be:
*   SELECT caldav_data.*,address_item.* FROM collection JOIN address_item USING (collection_id,user_no)
*         JOIN caldav_data USING (dav_id) WHERE collection.dav_name = '/user1/home/'
*              AND caldav_data.caldav_type = 'VEVENT' ORDER BY caldav_data.user_no, caldav_data.dav_name;
*/
$params = array();
$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
function BuildSqlFilter($filter)
{
    $components = array();
    if ($filter->GetNSTag() == "urn:ietf:params:xml:ns:caldav:comp-filter" && $filter->GetAttribute("name") == "VCALENDAR") {
        $filter = $filter->GetContent();
    } else {
        dbg_error_log("calquery", "Got bizarre CALDAV:FILTER[%s=%s]] which does not contain comp-filter = VCALENDAR!!", $filter->GetNSTag(), $filter->GetAttribute("name"));
    }
    return SqlFilterFragment($filter, $components);
}
/**
* Something that we can handle, at least roughly correctly.
*/
$responses = array();
$target_collection = new DAVResource($request->path);
$bound_from = $target_collection->bound_from();
if (!$target_collection->Exists()) {
    $request->DoResponse(404);
}
$params = array();
if (!($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection())) {
    if (!(isset($c->allow_recursive_report) && $c->allow_recursive_report)) {
        $request->DoResponse(403, translate('The calendar-query report must be run against a calendar or a scheduling collection'));
    } else {
        if ($request->path == '/' || $target_collection->IsPrincipal() || $target_collection->IsAddressbook()) {
            $request->DoResponse(403, translate('The calendar-query report may not be run against that URL.'));
        }
    }
    /**
     * We're here because they allow recursive reports, and this appears to be such a location.
     */
Beispiel #6
0
}
function rollback($response_code = 412)
{
    global $request;
    $qry = new AwlQuery('ROLLBACK');
    $qry->Exec('move');
    // Just in case
    $request->DoResponse($response_code);
    // And we don't return from that.
}
$qry = new AwlQuery('BEGIN');
if (!$qry->Exec('move')) {
    rollback(500);
}
$src_name = $src->dav_name();
$dst_name = $dest->IsBinding() ? $dest->bound_from() : $dest->dav_name();
$src_collection = $src->GetProperty('collection_id');
$dst_collection = $dest->GetProperty('collection_id');
$src_user_no = $src->GetProperty('user_no');
$dst_user_no = $dest->GetProperty('user_no');
$cache = getCacheInstance();
$cachekeys = array();
if ($src->IsCollection()) {
    $cachekeys[] = ($src->ContainerType() == 'principal' ? 'principal' : 'collection') . '-' . $src->parent_path();
    $cachekeys[] = ($src->IsPrincipal() == 'principal' ? 'principal' : 'collection') . '-' . $src->dav_name();
    $cachekeys[] = ($src->IsPrincipal() ? 'principal' : 'collection') . '-' . $dest->dav_name();
    if ($dest->Exists()) {
        $qry = new AwlQuery('DELETE FROM collection WHERE dav_name = :dst_name', array(':dst_name' => $dst_name));
        if (!$qry->Exec('move')) {
            rollback(500);
        }
/**
* 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!
}
 *
 * @param arrayref &$filter A reference to an array of XMLElement defining the filter
 *
 * @return string A string suitable for use as an SQL 'WHERE' clause selecting the desired records.
 */
function BuildSqlFilter($filter)
{
    $components = array();
    return SqlFilterFragment($filter, $components);
}
/**
* Something that we can handle, at least roughly correctly.
*/
$responses = array();
$target_collection = new DAVResource($request->path);
$bound_from = $target_collection->bound_from();
if (!$target_collection->Exists()) {
    $request->DoResponse(404);
}
$params = array();
$where = ' WHERE caldav_data.collection_id = ' . $target_collection->resource_id();
if (!($target_collection->IsCalendar() || $target_collection->IsSchedulingCollection())) {
    if (!(isset($c->allow_recursive_report) && $c->allow_recursive_report) || $target_collection->IsSchedulingCollection()) {
        $request->DoResponse(403, translate('The calendar-query report must be run against a calendar or a scheduling collection'));
    }
    /**
     * We're here because they allow recursive reports, and this appears to be such a location.
     */
    $where = 'WHERE (collection.dav_name ~ :path_match ';
    $where .= 'OR collection.collection_id IN (SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match)) ';
    $params = array(':path_match' => '^' . $target_collection->bound_from());
Beispiel #9
0
        }
        fetch_external($row->bind_id, '');
        $request->DoResponse(201);
    } else {
        $request->DoResponse(500, translate('Database Error'));
    }
} else {
    $source = new DAVResource($href);
    if (!$source->Exists()) {
        $request->PreconditionFailed(403, 'DAV::bind-source-exists', translate('The BIND Request MUST identify an existing resource.'));
    }
    if ($source->IsPrincipal() || !$source->IsCollection()) {
        $request->PreconditionFailed(403, 'DAV::binding-allowed', translate('DAViCal only allows BIND requests for collections at present.'));
    }
    if ($source->IsBinding()) {
        $source = new DAVResource($source->bound_from());
    }
    /*
      bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
      bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
      access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
      parent_container TEXT NOT NULL,
      dav_name TEXT UNIQUE NOT NULL,
      dav_displayname TEXT,
      external_url TEXT,
      type TEXT
    */
    $sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname )
  VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname )';
    $params = array(':target_id' => $source->GetProperty('collection_id'), ':ticket_id' => isset($request->ticket) ? $request->ticket->id() : null, ':parent_container' => $parent->dav_name(), ':session_principal' => $session->principal_id, ':dav_name' => $destination_path, ':displayname' => $source->GetProperty('displayname'));
    $qry = new AwlQuery($sql, $params);
    $dav_resource = new DAVResource($request->path);
}
if (!$dav_resource->HavePrivilegeTo('DAV::write-content')) {
    $request->DoResponse(403, 'No write permission');
}
if (!$dav_resource->Exists() && !$dav_resource->HavePrivilegeTo('DAV::bind')) {
    $request->DoResponse(403, 'No bind permission.');
}
if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put']) && $c->dbg['put'])) {
    $fh = fopen('/var/log/davical/PUT.debug', 'w');
    if ($fh) {
        fwrite($fh, $request->raw_post);
        fclose($fh);
    }
}
controlRequestContainer($dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
$lock_opener = $request->FailIfLocked();
if ($dav_resource->IsCollection()) {
    if ($dav_resource->IsPrincipal() || $dav_resource->IsBinding() || !isset($c->readonly_webdav_collections) || $c->readonly_webdav_collections == true) {
        $request->DoResponse(405);
        // Method not allowed
        return;
    }
    $appending = isset($_GET['mode']) && $_GET['mode'] == 'append';
    /**
     * CalDAV does not define the result of a PUT on a collection.  We treat that
     * as an import. The code is in caldav-PUT-functions.php
     */
    import_collection($request->raw_post, $request->user_no, $request->path, true, $appending);
    $request->DoResponse(200);
    return;
if (empty($last_modified)) {
    $last_modified = gmdate('Ymd\\THis\\Z');
    $vcard->AddProperty('REV', $last_modified);
} elseif (stripos($last_modified, 'TZ')) {
    // At least one of my examples has this crap.
    $last_modified = str_replace('TZ', 'T000000Z', $last_modified);
    $vcard->ClearProperties('REV');
    $vcard->AddProperty('REV', $last_modified);
} elseif (preg_match('{^(\\d{8})(\\d{6})Z?}', $last_modified, $matches)) {
    $last_modified = $matches[1] . 'T' . $matches[2] . 'Z';
    $vcard->ClearProperties('REV');
    $vcard->AddProperty('REV', $last_modified);
}
$rendered_card = $vcard->Render();
$etag = md5($rendered_card);
$params = array(':user_no' => $user_no, ':dav_name' => $dest->bound_from(), ':etag' => $etag, ':dav_data' => $rendered_card, ':session_user' => $session->user_no, ':modified' => $last_modified);
if ($dest->Exists()) {
    $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
          modified=:modified, user_no=:user_no, caldav_type=\'VCARD\' WHERE dav_name=:dav_name';
    $response_code = 200;
    $put_action_type = 'UPDATE';
    $qry->QDo($sql, $params);
    $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']));
} else {
    $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id )
          VALUES( :user_no, :dav_name, :etag, :dav_data, \'VCARD\', :session_user, current_timestamp, :modified, :collection_id )';
    $params[':collection_id'] = $collection_id;
    $response_code = 201;
    $qry->QDo($sql, $params);
    $put_action_type = 'INSERT';
    $qry->QDo("SELECT currval('dav_id_seq') AS dav_id");