Пример #1
0
/**
* Given a dav_id and an original vCalendar, pull out each of the VALARMs
* and write the values into the calendar_alarm table.
*
* @param int $dav_id The dav_id of the caldav_data we're processing
* @param vComponent The VEVENT or VTODO containing the VALARM
* @return null
*/
function write_alarms($dav_id, vComponent $ical)
{
    $qry = new AwlQuery('DELETE FROM calendar_alarm WHERE dav_id = ' . $dav_id);
    $qry->Exec('PUT', __LINE__, __FILE__);
    $alarms = $ical->GetComponents('VALARM');
    if (count($alarms) < 1) {
        return;
    }
    $qry->SetSql('INSERT INTO calendar_alarm ( dav_id, action, trigger, summary, description, component, next_trigger )
          VALUES( ' . $dav_id . ', :action, :trigger, :summary, :description, :component,
                                      :related::timestamp with time zone + :related_trigger::interval )');
    $qry->Prepare();
    foreach ($alarms as $v) {
        $trigger = array_merge($v->GetProperties('TRIGGER'));
        if ($trigger == null) {
            continue;
        }
        // Bogus data.
        $trigger = $trigger[0];
        $related = null;
        $related_trigger = '0M';
        $trigger_type = $trigger->GetParameterValue('VALUE');
        if (!isset($trigger_type) || $trigger_type == 'DURATION') {
            switch ($trigger->GetParameterValue('RELATED')) {
                case 'DTEND':
                    $related = $ical->GetProperty('DTEND');
                    break;
                case 'DUE':
                    $related = $ical->GetProperty('DUE');
                    break;
                default:
                    $related = $ical->GetProperty('DTSTART');
            }
            $duration = $trigger->Value();
            if (!preg_match('{^-?P(:?\\d+W)?(:?\\d+D)?(:?T(:?\\d+H)?(:?\\d+M)?(:?\\d+S)?)?$}', $duration)) {
                continue;
            }
            $minus = substr($duration, 0, 1) == '-';
            $related_trigger = trim(preg_replace('#[PT-]#', ' ', $duration));
            if ($minus) {
                $related_trigger = preg_replace('{(\\d+[WDHMS])}', '-$1 ', $related_trigger);
            } else {
                $related_trigger = preg_replace('{(\\d+[WDHMS])}', '$1 ', $related_trigger);
            }
        } else {
            if ($trigger_type == 'DATE-TIME') {
                $related = $trigger;
            } else {
                if (false === strtotime($trigger->Value())) {
                    continue;
                }
                // Invalid date.
                $related = $trigger;
            }
        }
        $related_date = new RepeatRuleDateTime($related);
        $qry->Bind(':action', $v->GetPValue('ACTION'));
        $qry->Bind(':trigger', $trigger->Render());
        $qry->Bind(':summary', $v->GetPValue('SUMMARY'));
        $qry->Bind(':description', $v->GetPValue('DESCRIPTION'));
        $qry->Bind(':component', $v->Render());
        $qry->Bind(':related', $related_date->UTC());
        $qry->Bind(':related_trigger', $related_trigger);
        $qry->Exec('PUT', __LINE__, __FILE__);
    }
}
Пример #2
0
function get_freebusy($path_match, $range_start, $range_end, $bin_privs = null)
{
    global $request, $c;
    $debugging = false;
    //    if ( $debugging ) {
    //        printf( "Path: %s\n", $path_match );
    //        print_r( $range_start );
    //        print_r( $range_end );
    //    }
    if (!isset($bin_privs)) {
        $bin_privs = $request->Privileges();
    }
    if (!isset($range_start) || !isset($range_end)) {
        $request->DoResponse(400, 'All valid freebusy requests MUST contain a time-range filter');
    }
    $params = array(':path_match' => $path_match, ':start' => $range_start->UTC(), ':end' => $range_end->UTC());
    $where = ' WHERE caldav_data.dav_name ~ :path_match ';
    $where .= 'AND rrule_event_overlaps( dtstart, dtend, rrule, :start, :end) ';
    $where .= "AND caldav_data.caldav_type IN ( 'VEVENT', 'VTODO' ) ";
    $where .= "AND (calendar_item.transp != 'TRANSPARENT' OR calendar_item.transp IS NULL) ";
    $where .= "AND (calendar_item.status != 'CANCELLED' OR calendar_item.status IS NULL) ";
    $where .= "AND collection.is_calendar AND collection.schedule_transp = 'opaque' ";
    if ($bin_privs != privilege_to_bits('all')) {
        $where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
    }
    $fbtimes = array();
    $sql = 'SELECT caldav_data.caldav_data, calendar_item.rrule, calendar_item.transp, calendar_item.status, ';
    $sql .= "to_char(calendar_item.dtstart at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS start, ';
    $sql .= "to_char(calendar_item.dtend at time zone 'GMT'," . AWLDatabase::SqlUTCFormat . ') AS finish, ';
    $sql .= "calendar_item.class, calendar_item.dav_id ";
    $sql .= 'FROM caldav_data INNER JOIN calendar_item USING(dav_id,user_no,dav_name,collection_id) ';
    $sql .= 'INNER JOIN collection USING(collection_id)';
    $sql .= $where;
    if (isset($c->strict_result_ordering) && $c->strict_result_ordering) {
        $sql .= ' ORDER BY dav_id';
    }
    $qry = new AwlQuery($sql, $params);
    if ($qry->Exec("REPORT", __LINE__, __FILE__) && $qry->rows() > 0) {
        while ($calendar_object = $qry->Fetch()) {
            $extra = '';
            if ($calendar_object->status == 'TENTATIVE') {
                $extra = ';BUSY-TENTATIVE';
            } else {
                if (isset($c->_workaround_client_freebusy_bug) && $c->_workaround_client_freebusy_bug) {
                    $extra = ';BUSY';
                }
            }
            //      if ( $debugging ) {
            //        $extra = ';'.$calendar_object->dav_id;
            //      }
            //      dbg_error_log( "REPORT", " FreeBusy: Not transparent, tentative or cancelled: %s, %s, %s", $calendar_object->start, $calendar_object->finish, $calendar_object->class );
            $ics = new vComponent($calendar_object->caldav_data);
            $expanded = expand_event_instances($ics, $range_start, $range_end);
            $expansion = $expanded->GetComponents(array('VEVENT' => true, 'VTODO' => true, 'VJOURNAL' => true));
            //      if ( $debugging ) echo "===================   $calendar_object->dav_id   ========================\n";
            $dtstart_type = 'DTSTART';
            foreach ($expansion as $k => $v) {
                //        if ( $debugging ) print $k."\n".$v->Render();
                $start_date = $v->GetProperty($dtstart_type);
                if (!isset($start_date) && $v->GetType() != 'VTODO') {
                    $dtstart_type = 'DUE';
                    $start_date = $v->GetProperty($dtstart_type);
                }
                $start_date = new RepeatRuleDateTime($start_date);
                $duration = $v->GetProperty('DURATION');
                $duration = !isset($duration) ? 'P1D' : $duration->Value();
                $end_date = clone $start_date;
                $end_date->modify($duration);
                if ($end_date == $start_date || $end_date < $range_start || $start_date > $range_end) {
                    //            if ( $debugging )
                    //              echo "-----------------------------------------------------\n";
                    continue;
                }
                //        if ( $debugging )
                //            echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
                $thisfb = $start_date->UTC() . '/' . $end_date->UTC() . $extra;
                array_push($fbtimes, $thisfb);
            }
        }
    }
    $freebusy = new vComponent();
    $freebusy->setType('VFREEBUSY');
    $freebusy->AddProperty('DTSTAMP', date('Ymd\\THis\\Z'));
    $freebusy->AddProperty('DTSTART', $range_start->UTC());
    $freebusy->AddProperty('DTEND', $range_end->UTC());
    sort($fbtimes);
    foreach ($fbtimes as $k => $v) {
        $text = explode(';', $v, 2);
        $freebusy->AddProperty('FREEBUSY', $text[0], isset($text[1]) ? array('FBTYPE' => $text[1]) : null);
    }
    return $freebusy;
}
Пример #3
0
/**
* Expand the event instances for an iCalendar VEVENT (or VTODO)
* 
* Note: expansion here does not apply modifications to instances other than modifying start/end/due/duration.
*
* @param object $vResource A vComponent which is a VCALENDAR containing components needing expansion
* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events, default -6 weeks
* @param object $range_end A RepeatRuleDateTime which is the end of the range for events, default +6 weeks
*
* @return vComponent The original vComponent, with the instances of the internal components expanded.
*/
function expand_event_instances($vResource, $range_start = null, $range_end = null)
{
    $components = $vResource->GetComponents();
    if (!isset($range_start)) {
        $range_start = new RepeatRuleDateTime();
        $range_start->modify('-6 weeks');
    }
    if (!isset($range_end)) {
        $range_end = clone $range_start;
        $range_end->modify('+6 months');
    }
    $new_components = array();
    $result_limit = 1000;
    $instances = array();
    $expand = false;
    $dtstart = null;
    foreach ($components as $k => $comp) {
        if ($comp->GetType() != 'VEVENT' && $comp->GetType() != 'VTODO' && $comp->GetType() != 'VJOURNAL') {
            if ($comp->GetType() != 'VTIMEZONE') {
                $new_components[] = $comp;
            }
            continue;
        }
        if (!isset($dtstart)) {
            $dtstart = $comp->GetProperty('DTSTART');
            $dtstart = new RepeatRuleDateTime($dtstart);
            $instances[$dtstart->UTC()] = $comp;
        }
        $p = $comp->GetProperty('RECURRENCE-ID');
        if (isset($p) && $p->Value() != '') {
            $range = $p->GetParameterValue('RANGE');
            $recur_utc = new RepeatRuleDateTime($p);
            $recur_utc = $recur_utc->UTC();
            if (isset($range) && $range == 'THISANDFUTURE') {
                foreach ($instances as $k => $v) {
                    if (DEBUG_RRULE) {
                        printf("Removing overridden instance at: {$k}\n");
                    }
                    if ($k >= $recur_utc) {
                        unset($instances[$k]);
                    }
                }
            } else {
                unset($instances[$recur_utc]);
            }
        } else {
            if (DEBUG_RRULE) {
                $p = $comp->GetProperty('SUMMARY');
                $summary = isset($p) ? $p->Value() : 'not set';
                $p = $comp->GetProperty('UID');
                $uid = isset($p) ? $p->Value() : 'not set';
                printf("Processing event '%s' with UID '%s' starting on %s\n", $summary, $uid, $dtstart->UTC());
                print "Instances at start";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
        $instances = array_merge($instances, rrule_expand($dtstart, 'RRULE', $comp, $range_end));
        if (DEBUG_RRULE) {
            print "After rrule_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        $instances = array_merge($instances, rdate_expand($dtstart, 'RDATE', $comp, $range_end));
        if (DEBUG_RRULE) {
            print "After rdate_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        foreach (rdate_expand($dtstart, 'EXDATE', $comp, $range_end) as $k => $v) {
            unset($instances[$k]);
        }
        if (DEBUG_RRULE) {
            print "After exdate_expand";
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
    }
    $last_duration = null;
    $early_start = null;
    $new_components = array();
    $start_utc = $range_start->UTC();
    $end_utc = $range_end->UTC();
    foreach ($instances as $utc => $comp) {
        if ($utc > $end_utc) {
            break;
        }
        $end_type = $comp->GetType() == 'VTODO' ? 'DUE' : 'DTEND';
        $duration = $comp->GetProperty('DURATION');
        if (!isset($duration) || $duration->Value() == '') {
            $instance_start = $comp->GetProperty('DTSTART');
            $dtsrt = new RepeatRuleDateTime($instance_start);
            $instance_end = $comp->GetProperty($end_type);
            if (isset($instance_end)) {
                $dtend = new RepeatRuleDateTime($instance_end);
                $duration = $dtstart->RFC5545Duration($dtend);
            } else {
                if ($instance_start->GetParameterValue('VALUE') == 'DATE') {
                    $duration = 'P1D';
                } else {
                    $duration = 'P0D';
                    // For clarity
                }
            }
        } else {
            $duration = $duration->Value();
        }
        if ($utc < $start_utc) {
            if (isset($early_start) && isset($last_duration) && $duration == $last_duration) {
                if ($utc < $early_start) {
                    continue;
                }
            } else {
                /** Calculate the latest possible start date when this event would overlap our range start */
                $latest_start = clone $range_start;
                $latest_start->modify('-' . $duration);
                $early_start = $latest_start->UTC();
                $last_duration = $duration;
                if ($utc < $early_start) {
                    continue;
                }
            }
        }
        $component = clone $comp;
        $component->ClearProperties(array('DTSTART' => true, 'DUE' => true, 'DTEND' => true));
        $component->AddProperty('DTSTART', $utc);
        $component->AddProperty('DURATION', $duration);
        $new_components[] = $component;
    }
    $vResource->SetComponents($new_components);
    return $vResource;
}
Пример #4
0
/**
* Expand the instances for a STANDARD or DAYLIGHT component of a VTIMEZONE 
*
* @param object $vResource is a VCALENDAR with a VTIMEZONE containing components needing expansion
* @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events.
* @param object $range_end A RepeatRuleDateTime which is the end of the range for events.
* @param int $offset_from The offset from UTC in seconds at the onset time.
*
* @return array of onset datetimes with UTC from/to offsets
*/
function expand_timezone_onsets(vCalendar $vResource, RepeatRuleDateTime $range_start, RepeatRuleDateTime $range_end)
{
    global $c;
    $vtimezones = $vResource->GetComponents();
    $vtz = $vtimezones[0];
    $components = $vtz->GetComponents();
    $instances = array();
    $dtstart = null;
    $is_date = false;
    $has_repeats = false;
    $zone_tz = $vtz->GetPValue('TZID');
    foreach ($components as $k => $comp) {
        if (DEBUG_EXPAND) {
            printf("Starting TZ expansion for component '%s' in timezone '%s'\n", $comp->GetType(), $zone_tz);
            foreach ($instances as $k => $v) {
                print ' : ' . $k;
            }
            print "\n";
        }
        $dtstart_prop = $comp->GetProperty('DTSTART');
        if (!isset($dtstart_prop)) {
            continue;
        }
        $dtstart = new RepeatRuleDateTime($dtstart_prop);
        $dtstart->setTimeZone('UTC');
        $offset_from = $comp->GetPValue('TZOFFSETFROM');
        $offset_from = $offset_from / 100 * 3600 + abs($offset_from) % 100 * 60 * ($offset_from < 0 ? -1 : 0);
        $offset_from *= -1;
        $offset_from = "{$offset_from} seconds";
        dbg_error_log('tz/update', "%s of offset\n", $offset_from);
        $dtstart->modify($offset_from);
        $is_date = $dtstart->isDate();
        $instances[$dtstart->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
        $rrule = $comp->GetProperty('RRULE');
        $has_repeats = isset($rrule);
        if (!$has_repeats) {
            continue;
        }
        $recur = $comp->GetProperty('RRULE');
        if (isset($recur)) {
            $recur = $recur->Value();
            $this_start = clone $dtstart;
            $rule = new RepeatRule($this_start, $recur, $is_date);
            $i = 0;
            $result_limit = 1000;
            while ($date = $rule->next()) {
                $instances[$date->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
                if ($i++ >= $result_limit || $date > $range_end) {
                    break;
                }
            }
            if (DEBUG_EXPAND) {
                print "After rrule_expand";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
        $properties = $comp->GetProperties('RDATE');
        if (count($properties)) {
            foreach ($properties as $p) {
                $timezone = $p->GetParameterValue('TZID');
                $rdate = $p->Value();
                $rdates = explode(',', $rdate);
                foreach ($rdates as $k => $v) {
                    $rdate = new RepeatRuleDateTime($v, $timezone, $is_date);
                    if ($return_floating_times) {
                        $rdate->setAsFloat();
                    }
                    $instances[$rdate->UTC('Y-m-d\\TH:i:s\\Z')] = $comp;
                    if ($rdate > $range_end) {
                        break;
                    }
                }
            }
            if (DEBUG_EXPAND) {
                print "After rdate_expand";
                foreach ($instances as $k => $v) {
                    print ' : ' . $k;
                }
                print "\n";
            }
        }
    }
    ksort($instances);
    $onsets = array();
    $start_utc = $range_start->UTC('Y-m-d\\TH:i:s\\Z');
    $end_utc = $range_end->UTC('Y-m-d\\TH:i:s\\Z');
    foreach ($instances as $utc => $comp) {
        if ($utc > $end_utc) {
            if (DEBUG_EXPAND) {
                printf("We're done: {$utc} is out of the range.\n");
            }
            break;
        }
        if ($utc < $start_utc) {
            continue;
        }
        $onsets[$utc] = array('from' => $comp->GetPValue('TZOFFSETFROM'), 'to' => $comp->GetPValue('TZOFFSETTO'), 'name' => $comp->GetPValue('TZNAME'), 'type' => $comp->GetType());
    }
    return $onsets;
}
Пример #5
0
 }
 $ic = new vComponent($alarm->caldav_data);
 $expanded = expand_event_instances($ic, $earliest, $expand_range_end);
 $expanded->MaskComponents(array('VEVENT' => 1, 'VTODO' => 1, 'VJOURNAL' => 1));
 $instances = $expanded->GetComponents();
 $trigger = new vProperty($alarm->trigger);
 $related = $trigger->GetParameterValue('RELATED');
 $first = new RepeatRuleDateTime($alarm->dtstart);
 $first->modify($trigger->Value());
 $next = null;
 $last = null;
 foreach ($instances as $k => $component) {
     $when = new RepeatRuleDateTime($component->GetPValue('DTSTART'));
     // a UTC value
     if ($args->debug) {
         printf("refresh: Looking at event instance on '%s'\n", $when->UTC());
     }
     if ($related == 'END') {
         $when->modify($component->GetPValue('DURATION'));
     }
     $when->modify($trigger->Value());
     if ($when > $expand_range_start && $when < $expand_range_end && (!isset($next) || $when < $next)) {
         $next = clone $when;
     }
     if ($args->set_last && (!isset($last) || $when > $last)) {
         $last = clone $when;
     }
 }
 $trigger_type = $trigger->GetParameterValue('VALUE');
 if ($trigger_type == 'DATE' || $trigger_type == 'DATE-TIME' || preg_match('{^\\d{8}T\\d{6}Z?$}', $trigger->Value())) {
     $first = new RepeatRuleDateTime($trigger);
Пример #6
0
function GetItip(VCalendar $vcal, $method, $attendee_value)
{
    $iTIP = $vcal->GetItip($method, $attendee_value);
    $iTIP->AddProperty('REQUEST-STATUS', '2.0');
    $components = $iTIP->GetComponents();
    foreach ($components as $comp) {
        $properties = array();
        foreach ($comp->GetProperties() as $k => $property) {
            switch ($property->Name()) {
                case 'DTSTART':
                case 'DTEND':
                case 'DUE':
                    $when = new RepeatRuleDateTime($property);
                    $properties[] = new vProperty($property->Name() . ":" . $when->UTC());
                    break;
                default:
                    $properties[] = $property;
            }
        }
        $comp->SetProperties($properties);
    }
    return $iTIP;
}
Пример #7
0
DELETE FROM caldav_data JOIN calendar_item USING(dav_id)
      WHERE dav_name LIKE '/someprincipal/%'
        AND (
          (rrule IS NULL AND dtend < archive_before_date)
           OR (last_instance_end < archive_before_date)
        )
*/
$recent = new RepeatRuleDateTime(gmdate('Ymd\\THis\\Z'));
$recent->modify('P-6D');
$archive_before_date = new RepeatRuleDateTime(gmdate('Ymd\\THis\\Z'));
$archive_before_date->modify($args->older);
if ($archive_before_date > $recent) {
    echo "Cowardly refusing to archive events before " + $archive_before_date->format('Y-m-d H:i:s') + "\n";
}
if ($args->debug) {
    printf("Archiving event instances finished before '%s'\n", $archive_before_date->UTC());
}
// SQL to create an archive collection but only if it doesn't exist already.
$archive_collection_sql = <<<EOSQL
INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar,
                        created, modified, public_events_only, publicly_readable, is_addressbook,
                        resourcetypes, schedule_transp, timezone, description)
     SELECT user_no, parent_container, :archive_dav_name, random(),
            'Archive of ' || dav_displayname, true, current_timestamp, current_timestamp, false, false, false,
            resourcetypes, schedule_transp, timezone, 
            'Archive of ' || CASE WHEN description IS NULL OR description = '' THEN dav_name ELSE description END
       FROM collection 
      WHERE dav_name = :collection_dav_name
        AND NOT EXISTS(SELECT 1 FROM collection c2 WHERE c2.dav_name = :archive_dav_name)
EOSQL;
$collection_dav_name = sprintf('/%s/%s/', $args->principal, $args->collection);