/**
* This function launches an error
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
* @param int $user_no the user who will receive this ics file
* @param string $path the $path where the PUT failed to store such as /user_foo/home/
* @param string $message An optional error message to return to the client
* @param int $error_no An optional value for the HTTP error code
*/
function rollback_on_error($caldav_context, $user_no, $path, $message = '', $error_no = 500)
{
    global $c, $bad_events;
    if (!$message) {
        $message = translate('Database error');
    }
    $qry = new AwlQuery();
    if ($qry->TransactionState() != 0) {
        $qry->Rollback();
    }
    if ($caldav_context) {
        if (isset($bad_events) && isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) {
            $bad_events[] = $message;
        } else {
            global $request;
            $request->DoResponse($error_no, $message);
        }
        // and we don't return from that, ever...
    }
    $c->messages[] = sprintf(translate('Status: %d, Message: %s, User: %d, Path: %s'), $error_no, $message, $user_no, $path);
}
Esempio n. 2
0
 /**
 CREATE TABLE addressbook_address_email (
 dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
 type TEXT,
 email TEXT,
 property TEXT -- The full text of the property
 );
 */
 function WriteEmails($dav_id)
 {
     $emails = $this->GetProperties('EMAIL');
     $qry = new AwlQuery();
     // Only run a local transaction if we're not in one already.
     $in_transaction = $qry->TransactionState() == 1;
     if (!$in_transaction) {
         $qry->Begin();
     }
     $params = array(':dav_id' => $dav_id);
     $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params);
     foreach ($emails as $email) {
         $params[':type'] = $email->GetParameterValue('TYPE');
         $params[':email'] = $email->Value();
         $params[':property'] = $email->Render();
         $qry->QDo('INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params);
     }
     if (!$in_transaction) {
         $qry->Commit();
     }
 }
Esempio n. 3
0
    /**
     * 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;
    }