function testing_get_method($filename)
{
    $body = file_get_contents($filename);
    $ical = new iCalComponent();
    $ical->ParseFrom($body);
    $props = $ical->GetPropertiesByPath("VCALENDAR/METHOD");
    if (count($props) > 0) {
        printf("METHOD %s\n", $props[0]->Value());
    }
}
Пример #2
0
 /**
  * Generate a VTODO from a SyncAppointment(Exception)
  * @param string $data
  * @param string $id
  * @return iCalComponent
  */
 private function _ParseASTaskToVTodo($data, $id)
 {
     $vtodo = new iCalComponent();
     $vtodo->SetType("VTODO");
     if (isset($data->body)) {
         $vtodo->AddProperty("DESCRIPTION", $data->body);
     }
     if (isset($data->asbody->data)) {
         if (isset($data->nativebodytype) && $data->nativebodytype == SYNC_BODYPREFERENCE_RTF) {
             $rtfparser = new rtf();
             $rtfparser->loadrtf(base64_decode($data->asbody->data));
             $rtfparser->output("ascii");
             $rtfparser->parse();
             $vtodo->AddProperty("DESCRIPTION", $rtfparser->out);
         } else {
             $vtodo->AddProperty("DESCRIPTION", $data->asbody->data);
         }
     }
     if (isset($data->complete)) {
         if ($data->complete == "0") {
             $vtodo->AddProperty("STATUS", "NEEDS-ACTION");
         } else {
             $vtodo->AddProperty("STATUS", "COMPLETED");
         }
     }
     if (isset($data->datecompleted)) {
         $vtodo->AddProperty("COMPLETED", gmdate("Ymd\\THis\\Z", $data->datecompleted));
     }
     if ($data->utcduedate) {
         $vtodo->AddProperty("DUE", gmdate("Ymd\\THis\\Z", $data->utcduedate));
     }
     if (isset($data->importance)) {
         if ($data->importance == "1") {
             $vtodo->AddProperty("PRIORITY", 6);
         } elseif ($data->importance == "2") {
             $vtodo->AddProperty("PRIORITY", 9);
         } else {
             $vtodo->AddProperty("PRIORITY", 1);
         }
     }
     if (isset($data->recurrence)) {
         $vtodo->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence));
     }
     if ($data->reminderset && $data->remindertime) {
         $valarm = new iCalComponent();
         $valarm->SetType("VALARM");
         $valarm->AddProperty("ACTION", "DISPLAY");
         $valarm->AddProperty("TRIGGER;VALUE=DATE-TIME", gmdate("Ymd\\THis\\Z", $data->remindertime));
         $vtodo->AddComponent($valarm);
     }
     if (isset($data->sensitivity)) {
         switch ($data->sensitivity) {
             case "0":
                 $vtodo->AddProperty("CLASS", "PUBLIC");
                 break;
             case "2":
                 $vtodo->AddProperty("CLASS", "PRIVATE");
                 break;
             case "3":
                 $vtodo->AddProperty("CLASS", "CONFIDENTIAL");
                 break;
         }
     }
     if (isset($data->utcstartdate)) {
         $vtodo->AddProperty("DTSTART", gmdate("Ymd\\THis\\Z", $data->utcstartdate));
     }
     if (isset($data->subject)) {
         $vtodo->AddProperty("SUMMARY", $data->subject);
     }
     if (isset($data->rtf)) {
         $rtfparser = new rtf();
         $rtfparser->loadrtf(base64_decode($data->rtf));
         $rtfparser->output("ascii");
         $rtfparser->parse();
         $vtodo->AddProperty("DESCRIPTION", $rtfparser->out);
     }
     if (isset($data->categories) && is_array($data->categories)) {
         $vtodo->AddProperty("CATEGORIES", implode(",", $data->categories));
     }
     return $vtodo;
 }
Пример #3
0
 /**
  * The constructor takes an array of args.  If there is an element called 'icalendar'
  * then that will be parsed into the iCalendar object.  Otherwise the array elements
  * are converted into properties of the iCalendar object directly.
  */
 function iRemoteCalendar($args)
 {
     global $c;
     $this->tz_locn = "";
     if (!isset($args) || !(is_array($args) || is_object($args))) {
         return;
     }
     if (is_object($args)) {
         settype($args, 'array');
     }
     $this->component = new iCalComponent();
     if (isset($args['icalendar'])) {
         $this->component->ParseFrom($args['icalendar']);
         $this->lines = preg_split('/\\r?\\n/', $args['icalendar']);
         $this->SaveTimeZones();
         $first =& $this->component->FirstNonTimezone();
         if ($first) {
             $this->type = $first->GetType();
             $this->properties = $first->GetProperties();
         } else {
             $this->properties = array();
         }
         $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!');
         return;
     }
     if (isset($args['type'])) {
         $this->type = $args['type'];
         unset($args['type']);
     } else {
         $this->type = 'VEVENT';
         // Default to event
     }
     $this->component->SetType('VCALENDAR');
     $this->component->SetProperties(array(new iCalProp('PRODID:-//davical.org//NONSGML AWL Calendar//EN'), new iCalProp('VERSION:2.0'), new iCalProp('CALSCALE:GREGORIAN')));
     $first = new iCalComponent();
     $first->SetType($this->type);
     $this->properties = array();
     foreach ($args as $k => $v) {
         dbg_error_log("iCalendar", ":Initialise: %s to >>>%s<<<", $k, $v);
         $property = new iCalProp();
         $property->Name($k);
         $property->Value($v);
         $this->properties[] = $property;
     }
     $first->SetProperties($this->properties);
     $this->component->SetComponents(array($first));
     $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!');
     /**
      * @todo Need to handle timezones!!!
      */
     if ($this->tz_locn == "") {
         $this->tz_locn = $this->Get("tzid");
         if ((!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid)) {
             $this->tz_locn = $c->local_tzid;
         }
     }
 }
Пример #4
0
/**
 * Modify a text/calendar part to transform it in a reply
 *
 * @access private
 * @param $part             MIME part
 * @param $response         Response numeric value
 * @param $condition_value  string
 * @return string MIME text/calendar
 */
function reply_meeting_calendar($part, $response, $username)
{
    $status_attendee = "ACCEPTED";
    // 1 or default is ACCEPTED
    $status_event = "CONFIRMED";
    switch ($response) {
        case 1:
            $status_attendee = "ACCEPTED";
            $status_event = "CONFIRMED";
            break;
        case 2:
            $status_attendee = $status_event = "TENTATIVE";
            break;
        case 3:
            // We won't hit this case ever, because we won't create an event if we are rejecting it
            $status_attendee = "DECLINED";
            $status_event = "CANCELLED";
            break;
    }
    $ical = new iCalComponent();
    $ical->ParseFrom($part->body);
    $ical->SetPValue("METHOD", "REPLY");
    $ical->SetCPParameterValue("VEVENT", "STATUS", $status_event, null);
    // Update my information as attendee, but only mine
    $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", $status_attendee, sprintf("MAILTO:%s", $username));
    $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "RSVP", null, sprintf("MAILTO:%s", $username));
    return $ical->Render();
}
Пример #5
0
function obfuscated_event($icalendar)
{
    // The user is not admin / owner of this calendar looking at his calendar and can not admin the other cal,
    // or maybe they don't have *read* access but they got here, so they must at least have free/busy access
    // so we will present an obfuscated version of the event that just says "Busy" (translated :-)
    $confidential = new iCalComponent();
    $confidential->SetType($icalendar->GetType());
    $confidential->AddProperty('SUMMARY', translate('Busy'));
    $confidential->AddProperty('CLASS', 'CONFIDENTIAL');
    $confidential->SetProperties($icalendar->GetProperties('DTSTART'), 'DTSTART');
    $confidential->SetProperties($icalendar->GetProperties('RRULE'), 'RRULE');
    $confidential->SetProperties($icalendar->GetProperties('DURATION'), 'DURATION');
    $confidential->SetProperties($icalendar->GetProperties('DTEND'), 'DTEND');
    $confidential->SetProperties($icalendar->GetProperties('UID'), 'UID');
    $confidential->SetProperties($icalendar->GetProperties('CREATED'), 'CREATED');
    return $confidential;
}
Пример #6
0
 /**
  * Converts a text/calendar part into SyncMeetingRequest
  *
  * @access private
  * @param $part    MIME part
  * @param $output  SyncMail object
  */
 private function parseMeetingCalendar($part, &$output)
 {
     $ical = new iCalComponent();
     $ical->ParseFrom($part->body);
     if (isset($part->ctype_parameters["method"])) {
         switch (strtolower($part->ctype_parameters["method"])) {
             case "cancel":
                 $output->messageclass = "IPM.Schedule.Meeting.Canceled";
                 break;
             case "counter":
                 $output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
                 break;
             case "reply":
                 $props = $ical->GetPropertiesByPath('!VTIMEZONE/ATTENDEE');
                 if (count($props) == 1) {
                     $props_params = $props[0]->Parameters();
                     if (isset($props_params["PARTSTAT"])) {
                         switch (strtolower($props_params["PARTSTAT"])) {
                             case "accepted":
                                 $output->messageclass = "IPM.Schedule.Meeting.Resp.Pos";
                                 break;
                             case "needs-action":
                             case "tentative":
                                 $output->messageclass = "IPM.Schedule.Meeting.Resp.Tent";
                                 break;
                             case "declined":
                                 $output->messageclass = "IPM.Schedule.Meeting.Resp.Neg";
                                 break;
                             default:
                                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - Unknown reply status %s", strtolower($props_params["PARTSTAT"])));
                                 $output->messageclass = "IPM.Appointment";
                                 break;
                         }
                     } else {
                         ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - No reply status found"));
                         $output->messageclass = "IPM.Appointment";
                     }
                 } else {
                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - There are not attendees"));
                     $output->messageclass = "IPM.Appointment";
                 }
                 break;
             case "request":
                 $output->messageclass = "IPM.Schedule.Meeting.Request";
                 break;
             default:
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - Unknown method %s", strtolower($part->headers["method"])));
                 $output->messageclass = "IPM.Appointment";
                 break;
         }
     } else {
         ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - No method header"));
         $output->messageclass = "IPM.Appointment";
     }
     $props = $ical->GetPropertiesByPath('VEVENT/DTSTAMP');
     if (count($props) == 1) {
         $output->meetingrequest->dtstamp = Utils::MakeUTCDate($props[0]->Value());
     }
     $props = $ical->GetPropertiesByPath('VEVENT/UID');
     if (count($props) == 1) {
         $output->meetingrequest->globalobjid = $props[0]->Value();
     }
     $props = $ical->GetPropertiesByPath('VEVENT/DTSTART');
     if (count($props) == 1) {
         $output->meetingrequest->starttime = Utils::MakeUTCDate($props[0]->Value());
         if (strlen($props[0]->Value()) == 8) {
             $output->meetingrequest->alldayevent = 1;
         }
     }
     $props = $ical->GetPropertiesByPath('VEVENT/DTEND');
     if (count($props) == 1) {
         $output->meetingrequest->endtime = Utils::MakeUTCDate($props[0]->Value());
         if (strlen($props[0]->Value()) == 8) {
             $output->meetingrequest->alldayevent = 1;
         }
     }
     $props = $ical->GetPropertiesByPath('VEVENT/ORGANIZER');
     if (count($props) == 1) {
         $output->meetingrequest->organizer = str_ireplace("MAILTO:", "", $props[0]->Value());
     }
     $props = $ical->GetPropertiesByPath('VEVENT/LOCATION');
     if (count($props) == 1) {
         $output->meetingrequest->location = $props[0]->Value();
     }
     $props = $ical->GetPropertiesByPath('VEVENT/CLASS');
     if (count($props) == 1) {
         switch ($props[0]->Value()) {
             case "PUBLIC":
                 $output->meetingrequest->sensitivity = "0";
                 break;
             case "PRIVATE":
                 $output->meetingrequest->sensitivity = "2";
                 break;
             case "CONFIDENTIAL":
                 $output->meetingrequest->sensitivity = "3";
                 break;
             default:
                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parseMeetingCalendar() - No sensitivity class. Using 2"));
                 $output->meetingrequest->sensitivity = "2";
                 break;
         }
     }
     // Get $tz from first timezone
     $props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
     if (count($props) > 0) {
         // TimeZones shouldn't have dots
         $tzname = str_replace(".", "", $props[0]->Value());
         $tz = TimezoneUtil::GetFullTZFromTZName($tzname);
     } else {
         $tz = TimezoneUtil::GetFullTZ();
     }
     $output->meetingrequest->timezone = base64_encode(TimezoneUtil::getSyncBlobFromTZ($tz));
     // Fixed values
     $output->meetingrequest->instancetype = 0;
     $output->meetingrequest->responserequested = 1;
     $output->meetingrequest->busystatus = 2;
     // TODO: reminder
     $output->meetingrequest->reminder = "";
 }
<?php

require_once 'vendor/autoload.php';
define('LOGLEVEL', LOGLEVEL_DEBUG);
define('LOGUSERLEVEL', LOGLEVEL_DEVICEID);
date_default_timezone_set('Europe/Madrid');
$body = file_get_contents('testing/samples/meeting_request.txt');
$ical = new iCalComponent();
$ical->ParseFrom($body);
$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
if (count($props) > 0) {
    $tzid = $props[0]->Value();
    printf("TZID %s\n", $props[0]->Value());
}
print_r(TimezoneUtil::GetFullTZFromTZName($tzid));
$body = file_get_contents('testing/samples/meeting_request_rim.txt');
$ical = new iCalComponent();
$ical->ParseFrom($body);
$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
if (count($props) > 0) {
    $tzid = $props[0]->Value();
    printf("TZID %s\n", $props[0]->Value());
}
print_r(TimezoneUtil::GetFullTZFromTZName($tzid));
Пример #8
0
<?php

/**
 * Handle the FREE-BUSY-QUERY variant of REPORT
 */
include_once "freebusy-functions.php";
$fbq_content = $xmltree->GetContent('urn:ietf:params:xml:ns:caldav:free-busy-query');
$fbq_start = $fbq_content[0]->GetAttribute('start');
$fbq_end = $fbq_content[0]->GetAttribute('end');
if (!(isset($fbq_start) || isset($fbq_end))) {
    $request->DoResponse(400, 'All valid freebusy requests MUST contain a time-range filter');
}
$range_start = new RepeatRuleDateTime($fbq_start);
$range_end = new RepeatRuleDateTime($fbq_end);
/** We use the same code for the REPORT, the POST and the freebusy GET... */
$freebusy = get_freebusy('^' . $request->path . $request->DepthRegexTail(), $range_start, $range_end);
$result = new iCalComponent();
$result->VCalendar();
$result->AddComponent($freebusy);
$request->DoResponse(200, $result->Render(), 'text/calendar');
// Won't return from that
Пример #9
0
 /**
  * Changes every property passed as an associative array (key will be
  * uppercased) on given component. DTSTART, DTEND and
  * DURATION are ignored, use make_start and make_end instead
  *
  * @param iCalComponent $component
  * @param array $properties
  */
 function change_properties($component, $properties)
 {
     $properties = array_change_key_case($properties, CASE_UPPER);
     foreach ($properties as $p => $v) {
         if ($p == 'DTSTART' || $p == 'DTEND' || $p == 'DURATION') {
             continue;
         }
         // TODO: multivalued properties?
         // TRANSP
         if ($p == 'TRANSP') {
             if ($v != 'OPAQUE' && $v != 'TRANSPARENT') {
                 log_message('ERROR', 'Invalid TRANSP value (' . $v . ').  Ignoring.');
                 continue;
             }
         }
         $component->deleteProperty($p);
         if (!empty($v)) {
             $component->setProperty($p, $v);
         }
     }
     return $component;
 }
Пример #10
0
function handle_freebusy_request($ic)
{
    global $c, $session, $request;
    $reply = new XMLDocument(array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C"));
    $responses = array();
    $fbq_start = $ic->GetPValue('DTSTART');
    $fbq_end = $ic->GetPValue('DTEND');
    if (!(isset($fbq_start) || isset($fbq_end))) {
        $request->DoResponse(400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND');
    }
    $range_start = new RepeatRuleDateTime($fbq_start);
    $range_end = new RepeatRuleDateTime($fbq_end);
    $attendees = $ic->GetProperties('ATTENDEE');
    if (preg_match('# iCal/\\d#', $_SERVER['HTTP_USER_AGENT'])) {
        dbg_error_log("POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property");
        $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
        foreach ($wr_attendees as $k => $v) {
            $attendees[] = $v;
        }
    }
    dbg_error_log("POST", "Responding with free/busy for %d attendees", count($attendees));
    foreach ($attendees as $k => $attendee) {
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        dbg_error_log("POST", "Calculating free/busy for %s", $attendee_email);
        /** @TODO: Refactor this so we only do one query here and loop through the results */
        $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email);
        $qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params);
        if (!$qry->Exec('POST', __LINE__, __FILE__)) {
            $request->DoResponse(501, 'Database error');
        }
        if ($qry->rows() > 1) {
            // Unlikely, but if we get more than one result we'll do an exact match instead.
            if (!$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params)) {
                $request->DoResponse(501, 'Database error');
            }
            if ($qry->rows() == 0) {
                /** Sigh... Go back to the original case-insensitive match */
                $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params);
            }
        }
        $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
        $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()));
        if ($qry->rows() == 0) {
            $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User");
            $reply->CalDAVElement($response, "calendar-data");
            $responses[] = $response;
            continue;
        }
        if (!($attendee_usr = $qry->Fetch())) {
            $request->DoResponse(501, 'Database error');
        }
        if ((privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0) {
            $reply->CalDAVElement($response, "request-status", "3.8;No authority");
            $reply->CalDAVElement($response, "calendar-data");
            $responses[] = $response;
            continue;
        }
        $attendee_path_match = '^/' . $attendee_usr->username . '/';
        $fb = get_freebusy($attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p));
        $fb->AddProperty('UID', $ic->GetPValue('UID'));
        $fb->SetProperties($ic->GetProperties('ORGANIZER'), 'ORGANIZER');
        $fb->AddProperty($attendee);
        $vcal = new iCalComponent();
        $vcal->VCalendar(array('METHOD' => 'REPLY'));
        $vcal->AddComponent($fb);
        $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
        $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()));
        $reply->CalDAVElement($response, "request-status", "2.0;Success");
        // Cargo-cult setting
        $reply->CalDAVElement($response, "calendar-data", $vcal->Render());
        $responses[] = $response;
    }
    $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav');
    $request->XMLResponse(200, $response);
}
Пример #11
0
function get_freebusy($path_match, $range_start, $range_end, $bin_privs = null)
{
    global $request;
    //  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) ";
    }
    // $debugging = true;
    $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'," . iCalendar::SqlUTCFormat() . ') AS start, ';
    $sql .= "to_char(calendar_item.dtend at time zone 'GMT'," . iCalendar::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 ( $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));
            foreach ($expansion as $k => $v) {
                //        echo "=====================================================\n";
                //        printf( "Type: %s\n", $v->GetType());
                //        print_r($v);
                //        echo "-----------------------------------------------------\n";
                $start_date = $v->GetProperty('DTSTART');
                if (!isset($start_date)) {
                    continue;
                }
                $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) {
                    continue;
                }
                $thisfb = $start_date->UTC() . '/' . $end_date->UTC() . $extra;
                array_push($fbtimes, $thisfb);
            }
        }
    }
    $freebusy = new iCalComponent();
    $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;
}
Пример #12
0
    /**
     * Return general server-related properties for this URL
     */
    function ResourceProperty($tag, $prop, &$reply, &$denied)
    {
        global $c, $session, $request;
        //    dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
        if ($reply === null) {
            $reply = $GLOBALS['reply'];
        }
        switch ($tag) {
            case 'DAV::allprop':
                $property_list = $this->DAV_AllProperties();
                $discarded = array();
                foreach ($property_list as $k => $v) {
                    $this->ResourceProperty($v, $prop, $reply, $discarded);
                }
                break;
            case 'DAV::href':
                $prop->NewElement('href', ConstructURL($this->dav_name));
                break;
            case 'DAV::resource-id':
                if ($this->resource_id > 0) {
                    $reply->DAVElement($prop, 'resource-id', $reply->href(ConstructURL('/.resources/' . $this->resource_id)));
                } else {
                    return false;
                }
                break;
            case 'DAV::parent-set':
                $sql = <<<EOQRY
SELECT b.parent_container FROM dav_binding b JOIN collection c ON (b.bound_source_id=c.collection_id)
 WHERE regexp_replace( b.dav_name, '^.*/', c.dav_name ) = :bound_from
EOQRY;
                $qry = new AwlQuery($sql, array(':bound_from' => $this->bound_from()));
                $parents = array();
                if ($qry->Exec('DAVResource', __LINE__, __FILE__) && $qry->rows() > 0) {
                    while ($row = $qry->Fetch()) {
                        $parents[$row->parent_container] = true;
                    }
                }
                $parents[preg_replace('{(?<=/)[^/]+/?$}', '', $this->bound_from())] = true;
                $parents[preg_replace('{(?<=/)[^/]+/?$}', '', $this->dav_name())] = true;
                $parent_set = $reply->DAVElement($prop, 'parent-set');
                foreach ($parents as $parent => $v) {
                    if (preg_match('{^(.*)?/([^/]+)/?$}', $parent, $matches)) {
                        $reply->DAVElement($parent_set, 'parent', array(new XMLElement('href', ConstructURL($matches[1])), new XMLElement('segment', $matches[2])));
                    } else {
                        if ($parent == '/') {
                            $reply->DAVElement($parent_set, 'parent', array(new XMLElement('href', '/'), new XMLElement('segment', ConstructURL('/') == '/caldav.php/' ? 'caldav.php' : '')));
                        }
                    }
                }
                break;
            case 'DAV::getcontenttype':
                if (!isset($this->contenttype) && !$this->_is_collection && !isset($this->resource)) {
                    $this->FetchResource();
                }
                $prop->NewElement('getcontenttype', $this->contenttype);
                break;
            case 'DAV::resourcetype':
                $resourcetypes = $prop->NewElement('resourcetype');
                if ($this->_is_collection) {
                    $type_list = $this->GetProperty('resourcetype');
                    if (!is_array($type_list)) {
                        return true;
                    }
                    //        dbg_error_log( 'DAVResource', ':ResourceProperty: "%s" are "%s".', $tag, implode(', ',$type_list) );
                    foreach ($type_list as $k => $v) {
                        if ($v == '') {
                            continue;
                        }
                        $reply->NSElement($resourcetypes, $v);
                    }
                    if ($this->_is_binding) {
                        $reply->NSElement($resourcetypes, 'http://xmlns.davical.org/davical:webdav-binding');
                    }
                }
                break;
            case 'DAV::getlastmodified':
                /** getlastmodified is HTTP Date format: i.e. the Last-Modified header in response to a GET */
                $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')));
                break;
            case 'DAV::creationdate':
                /** creationdate is ISO8601 format */
                $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created'), true));
                break;
            case 'DAV::getcontentlength':
                if ($this->_is_collection) {
                    return false;
                }
                if (!isset($this->resource)) {
                    $this->FetchResource();
                }
                if (isset($this->resource)) {
                    $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data));
                }
                break;
            case 'DAV::getcontentlanguage':
                $locale = isset($c->current_locale) ? $c->current_locale : '';
                if (isset($this->locale) && $this->locale != '') {
                    $locale = $this->locale;
                }
                $reply->NSElement($prop, $tag, $locale);
                break;
            case 'DAV::acl-restrictions':
                $reply->NSElement($prop, $tag, array(new XMLElement('grant-only'), new XMLElement('no-invert')));
                break;
            case 'DAV::inherited-acl-set':
                $inherited_acls = array();
                if (!$this->_is_collection) {
                    $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
                }
                $reply->NSElement($prop, $tag, $inherited_acls);
                break;
            case 'DAV::owner':
                // The principal-URL of the owner
                if ($this->IsExternal()) {
                    $reply->DAVElement($prop, 'owner', $reply->href(ConstructURL($this->collection->bound_from)));
                } else {
                    $reply->DAVElement($prop, 'owner', $reply->href(ConstructURL(DeconstructURL($this->principal_url()))));
                }
                break;
            case 'DAV::add-member':
                if (!$this->_is_collection) {
                    return false;
                }
                if (isset($c->post_add_member) && $c->post_add_member === false) {
                    return false;
                }
                $reply->DAVElement($prop, 'add-member', $reply->href(ConstructURL(DeconstructURL($this->url())) . '?add-member'));
                break;
                // Empty tag responses.
            // Empty tag responses.
            case 'DAV::group':
            case 'DAV::alternate-URI-set':
                $reply->NSElement($prop, $tag);
                break;
            case 'DAV::getetag':
                if ($this->_is_collection) {
                    return false;
                }
                $reply->NSElement($prop, $tag, $this->unique_tag());
                break;
            case 'http://calendarserver.org/ns/:getctag':
                if (!$this->_is_collection) {
                    return false;
                }
                $reply->NSElement($prop, $tag, $this->unique_tag());
                break;
            case 'DAV::sync-token':
                if (!$this->_is_collection) {
                    return false;
                }
                $sync_token = $this->sync_token();
                if (empty($sync_token)) {
                    return false;
                }
                $reply->NSElement($prop, $tag, $sync_token);
                break;
            case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
                $proxy_type = 'read';
            case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
                if (isset($c->disable_caldav_proxy) && $c->disable_caldav_proxy) {
                    return false;
                }
                if (!isset($proxy_type)) {
                    $proxy_type = 'write';
                }
                // ProxyFor is an already constructed URL
                $reply->CalendarserverElement($prop, 'calendar-proxy-' . $proxy_type . '-for', $reply->href($this->principal->ProxyFor($proxy_type)));
                break;
            case 'DAV::current-user-privilege-set':
                if ($this->HavePrivilegeTo('DAV::read-current-user-privilege-set')) {
                    $reply->NSElement($prop, $tag, $this->BuildPrivileges());
                } else {
                    $denied[] = $tag;
                }
                break;
            case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
                if (!$this->IsCalendar() && !$this->IsSchedulingCollection()) {
                    return false;
                }
                $reply->NSElement($prop, $tag, 'text/calendar');
                break;
            case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
                if (!$this->_is_collection) {
                    return false;
                }
                if ($this->IsCalendar()) {
                    if (!isset($this->dead_properties)) {
                        $this->FetchDeadProperties();
                    }
                    if (isset($this->dead_properties[$tag])) {
                        $set_of_components = $this->dead_properties[$tag];
                        foreach ($set_of_components as $k => $v) {
                            if (preg_match('{(VEVENT|VTODO|VJOURNAL|VTIMEZONE|VFREEBUSY|VPOLL|VAVAILABILITY)}', $v, $matches)) {
                                $set_of_components[$k] = $matches[1];
                            } else {
                                unset($set_of_components[$k]);
                            }
                        }
                    } else {
                        if (isset($c->default_calendar_components) && is_array($c->default_calendar_components)) {
                            $set_of_components = $c->default_calendar_components;
                        } else {
                            $set_of_components = array('VEVENT', 'VTODO', 'VJOURNAL');
                        }
                    }
                } else {
                    if ($this->IsSchedulingCollection()) {
                        $set_of_components = array('VEVENT', 'VTODO', 'VFREEBUSY');
                    } else {
                        return false;
                    }
                }
                $components = array();
                foreach ($set_of_components as $v) {
                    $components[] = $reply->NewXMLElement('comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
                }
                $reply->CalDAVElement($prop, 'supported-calendar-component-set', $components);
                break;
            case 'DAV::supported-method-set':
                $prop->NewElement('supported-method-set', $this->BuildSupportedMethods());
                break;
            case 'DAV::supported-report-set':
                $prop->NewElement('supported-report-set', $this->BuildSupportedReports($reply));
                break;
            case 'DAV::supportedlock':
                $prop->NewElement('supportedlock', new XMLElement('lockentry', array(new XMLElement('lockscope', new XMLElement('exclusive')), new XMLElement('locktype', new XMLElement('write')))));
                break;
            case 'DAV::supported-privilege-set':
                $prop->NewElement('supported-privilege-set', $request->BuildSupportedPrivileges($reply));
                break;
            case 'DAV::principal-collection-set':
                $prop->NewElement('principal-collection-set', $reply->href(ConstructURL('/')));
                break;
            case 'DAV::current-user-principal':
                $prop->NewElement('current-user-principal', $reply->href(ConstructURL(DeconstructURL($request->principal->url()))));
                break;
            case 'SOME-DENIED-PROPERTY':
                /** indicating the style for future expansion */
                $denied[] = $reply->Tag($tag);
                break;
            case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
                if (!$this->_is_collection) {
                    return false;
                }
                if (!isset($this->collection->vtimezone) || $this->collection->vtimezone == '') {
                    return false;
                }
                $cal = new iCalComponent();
                $cal->VCalendar();
                $cal->AddComponent(new iCalComponent($this->collection->vtimezone));
                $reply->NSElement($prop, $tag, $cal->Render());
                break;
            case 'urn:ietf:params:xml:ns:carddav:address-data':
            case 'urn:ietf:params:xml:ns:caldav:calendar-data':
                if ($this->_is_collection) {
                    return false;
                }
                if (!isset($c->sync_resource_data_ok) || $c->sync_resource_data_ok == false) {
                    return false;
                }
                if (!isset($this->resource)) {
                    $this->FetchResource();
                }
                $reply->NSElement($prop, $tag, $this->resource->caldav_data);
                break;
            case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
                if (!$this->_is_collection || !$this->_is_addressbook) {
                    return false;
                }
                $reply->NSElement($prop, $tag, 65500);
                break;
            case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
                if (!$this->_is_collection || !$this->_is_addressbook) {
                    return false;
                }
                $address_data = $reply->NewXMLElement('address-data', false, array('content-type' => 'text/vcard', 'version' => '3.0'), 'urn:ietf:params:xml:ns:carddav');
                $reply->NSElement($prop, $tag, $address_data);
                break;
            case 'DAV::acl':
                if ($this->HavePrivilegeTo('DAV::read-acl')) {
                    $reply->NSElement($prop, $tag, $this->GetACL($reply));
                } else {
                    $denied[] = $tag;
                }
                break;
            case 'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery':
            case 'DAV::ticketdiscovery':
                $reply->NSElement($prop, 'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply));
                break;
            default:
                $property_value = $this->GetProperty(preg_replace('{^(DAV:|urn:ietf:params:xml:ns:ca(rd|l)dav):}', '', $tag));
                if (isset($property_value)) {
                    $reply->NSElement($prop, $tag, $property_value);
                } else {
                    if (!isset($this->dead_properties)) {
                        $this->FetchDeadProperties();
                    }
                    if (isset($this->dead_properties[$tag])) {
                        $reply->NSElement($prop, $tag, $this->dead_properties[$tag]);
                    } else {
                        //            dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
                        return false;
                    }
                }
        }
        return true;
    }
Пример #13
0
/**
* This function will import a whole calendar
* @param string $ics_content the ics file to import
* @param int $user_no the user wich will receive this ics file
* @param string $path the $path where it will be store such as /user_foo/home/
* @param boolean $caldav_context Whether we are responding via CalDAV or interactively
*
* Any VEVENTs with the same UID will be concatenated together
*/
function import_collection($ics_content, $user_no, $path, $caldav_context, $appending = false)
{
    global $c, $session, $tz_regex;
    if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put']))) {
        $fh = fopen('/tmp/PUT-2.txt', 'w');
        if ($fh) {
            fwrite($fh, $ics_content);
            fclose($fh);
        }
    }
    $calendar = new iCalComponent($ics_content);
    $timezones = $calendar->GetComponents('VTIMEZONE', true);
    $components = $calendar->GetComponents('VTIMEZONE', false);
    $displayname = $calendar->GetPValue('X-WR-CALNAME');
    if (!$appending && isset($displayname)) {
        $sql = 'UPDATE collection SET dav_displayname = :displayname WHERE dav_name = :dav_name';
        $qry = new AwlQuery($sql, array(':displayname' => $displayname, ':dav_name' => $path));
        if (!$qry->Exec('PUT', __LINE__, __FILE__)) {
            rollback_on_error($caldav_context, $user_no, $path);
        }
    }
    $tz_ids = array();
    foreach ($timezones as $k => $tz) {
        $tz_ids[$tz->GetPValue('TZID')] = $k;
    }
    /** Build an array of resources.  Each resource is an array of iCalComponent */
    $resources = array();
    foreach ($components as $k => $comp) {
        $uid = $comp->GetPValue('UID');
        if ($uid == null || $uid == '') {
            continue;
        }
        if (!isset($resources[$uid])) {
            $resources[$uid] = array();
        }
        $resources[$uid][] = $comp;
        /** Ensure we have the timezone component for this in our array as well */
        $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
        if (!isset($tzid) || $tzid == '') {
            $tzid = $comp->GetPParamValue('DUE', 'TZID');
        }
        if (!isset($resources[$uid][$tzid]) && isset($tz_ids[$tzid])) {
            $resources[$uid][$tzid] = $timezones[$tz_ids[$tzid]];
        }
    }
    $sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
    $qry = new AwlQuery($sql, array(':dav_name' => $path));
    if (!$qry->Exec('PUT', __LINE__, __FILE__)) {
        rollback_on_error($caldav_context, $user_no, $path);
    }
    if (!$qry->rows() == 1) {
        dbg_error_log('ERROR', ' PUT: Collection does not exist at "%s" for user %d', $path, $user_no);
        rollback_on_error($caldav_context, $user_no, $path);
    }
    $collection = $qry->Fetch();
    if (!(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import)) {
        $qry->Begin();
    }
    $base_params = array(':collection_id' => $collection->collection_id);
    if (!$appending) {
        if (!$qry->QDo('DELETE FROM calendar_item WHERE collection_id = :collection_id', $base_params) || !$qry->QDo('DELETE FROM caldav_data WHERE collection_id = :collection_id', $base_params)) {
            rollback_on_error($caldav_context, $user_no, $collection->collection_id);
        }
    }
    $dav_data_insert = <<<EOSQL
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, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id )
EOSQL;
    $calitem_insert = <<<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;
    $last_tz_locn = '';
    foreach ($resources as $uid => $resource) {
        if (isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) {
            $qry->Begin();
        }
        /** Construct the VCALENDAR data */
        $vcal = new iCalComponent();
        $vcal->VCalendar();
        $vcal->SetComponents($resource);
        create_scheduling_requests($vcal);
        $icalendar = $vcal->Render();
        /** As ever, we mostly deal with the first resource component */
        $first = $resource[0];
        $dav_data_params = $base_params;
        $dav_data_params[':user_no'] = $user_no;
        $dav_data_params[':dav_name'] = sprintf('%s%s.ics', $path, $uid);
        $dav_data_params[':etag'] = md5($icalendar);
        $calitem_params = $dav_data_params;
        $dav_data_params[':dav_data'] = $icalendar;
        $dav_data_params[':caldav_type'] = $first->GetType();
        $dav_data_params[':session_user'] = $session->user_no;
        if (!$qry->QDo($dav_data_insert, $dav_data_params)) {
            rollback_on_error($caldav_context, $user_no, $path);
        }
        $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $dav_data_params[':dav_name']));
        if ($qry->rows() == 1 && ($row = $qry->Fetch())) {
            $dav_id = $row->dav_id;
        }
        $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 = 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.
                 *
                 */
                $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;
        /** RFC2445, 4.8.1.3: Default is PUBLIC, or also if overridden by the collection settings */
        $class = $collection->public_events_only == 't' ? 'PUBLIC' : $first->GetPValue('CLASS');
        if (!isset($class) || $class == '') {
            $class = 'PUBLIC';
        }
        $calitem_params[':class'] = $class;
        /** Calculate what timezone to set, first, if possible */
        $tzid = $first->GetPParamValue('DTSTART', 'TZID');
        if (!isset($tzid) || $tzid == '') {
            $tzid = $first->GetPParamValue('DUE', 'TZID');
        }
        if (isset($tzid) && $tzid != '') {
            if (isset($resource[$tzid])) {
                $tz = $resource[$tzid];
                $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
            } else {
                unset($tz);
                unset($tz_locn);
            }
            if (!isset($tz_locn) || !preg_match($tz_regex, $tz_locn)) {
                if (preg_match('#([^/]+/[^/]+)$#', $tzid, $matches)) {
                    $tz_locn = $matches[1];
                }
            }
            dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($tz_locn) ? $tz_locn : '');
            if (isset($tz_locn) && $tz_locn != $last_tz_locn && preg_match($tz_regex, $tz_locn)) {
                dbg_error_log('PUT', ' Setting timezone to %s', $tz_locn);
                if ($tz_locn != '') {
                    $qry->QDo('SET TIMEZONE TO \'' . $tz_locn . "'");
                }
                $last_tz_locn = $tz_locn;
            }
            $params = array(':tzid' => $tzid);
            $qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params);
            if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) {
                $params[':tzlocn'] = $tz_locn;
                $params[':tzspec'] = isset($tz) ? $tz->Render() : null;
                $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params);
            }
            if (!isset($tz_locn) || $tz_locn == '') {
                $tz_locn = $tzid;
            }
        } else {
            $tzid = null;
        }
        $sql = str_replace('##dtend##', $dtend, $calitem_insert);
        $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[':due'] = $first->GetPValue('DUE');
        $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
        $calitem_params[':status'] = $first->GetPValue('STATUS');
        $created = $first->GetPValue('CREATED');
        if ($created == '00001231T000000Z') {
            $created = '20001231T000000Z';
        }
        $calitem_params[':created'] = $created;
        if (!$qry->QDo($sql, $calitem_params)) {
            rollback_on_error($caldav_context, $user_no, $path);
        }
        write_alarms($dav_id, $first);
        write_attendees($dav_id, $first);
        create_scheduling_requests($vcal);
        if (isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import) {
            $qry->Commit();
        }
    }
    if (!(isset($c->skip_bad_event_on_import) && $c->skip_bad_event_on_import)) {
        if (!$qry->Commit()) {
            rollback_on_error($caldav_context, $user_no, $path);
        }
    }
}
Пример #14
0
$ic = new iCalComponent($resource->caldav_data);
$resource->caldav_data = preg_replace('{(?<!\\r)\\n}', "\r\n", $resource->caldav_data);
/** Default deny... */
$allowed = false;
if ($dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $resource->user_no || $session->user_no == $resource->logged_user || $c->allow_get_email_visibility && $ic->IsAttendee($session->email)) {
    /**
     * These people get to see all of the event, and they should always
     * get any alarms as well.
     */
    $allowed = true;
} else {
    if ($resource->class != 'PRIVATE') {
        $allowed = true;
        // but we may well obfuscate it below
        if (!$dav_resource->HavePrivilegeTo('DAV::read') || $resource->class == 'CONFIDENTIAL' && !$request->HavePrivilegeTo('DAV::write-content')) {
            $ical = new iCalComponent($resource->caldav_data);
            $comps = $ical->GetComponents('VTIMEZONE', false);
            $confidential = obfuscated_event($comps[0]);
            $ical->SetComponents(array($confidential), $resource->caldav_type);
            $resource->caldav_data = $ical->Render();
        }
    }
}
// else $resource->class == 'PRIVATE' and this person may not see it.
if (!$allowed) {
    $request->DoResponse(403, translate("Forbidden"));
}
header('Etag: "' . $resource->dav_etag . '"');
header('Content-Length: ' . strlen($resource->caldav_data));
$contenttype = 'text/plain';
switch ($resource->caldav_type) {
Пример #15
0
/**
* Return XML for a single component from the DB
*
* @param array $properties The properties for this component
* @param string $item The DB row data for this component
*
* @return string An XML document which is the response for the component
*/
function component_to_xml($properties, $item)
{
    global $session, $c, $request, $reply;
    dbg_error_log("REPORT", "Building XML Response for item '%s'", $item->dav_name);
    $denied = array();
    $unsupported = array();
    $caldav_data = $item->caldav_data;
    $displayname = preg_replace('{^.*/}', '', $item->dav_name);
    $type = 'unknown';
    $contenttype = 'text/plain';
    switch ($item->caldav_type) {
        case 'VJOURNAL':
        case 'VEVENT':
        case 'VTODO':
            $displayname = $item->summary;
            $type = 'calendar';
            $contenttype = 'text/calendar';
            break;
        case 'VCARD':
            $displayname = $item->fn;
            $type = 'vcard';
            $contenttype = 'text/vcard';
            break;
    }
    if (isset($properties['calendar-data']) || isset($properties['displayname'])) {
        if (!$request->AllowedTo('all') && $session->user_no != $item->user_no) {
            // the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
            /** @todo We should examine the ORGANIZER and ATTENDEE fields in the event.  If this person is there then they should see this */
            if ($type == 'calendar' && $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read')) {
                $ical = new iCalComponent($caldav_data);
                $resources = $ical->GetComponents('VTIMEZONE', false);
                $first = $resources[0];
                // if the event is confidential we fake one that just says "Busy"
                $confidential = new iCalComponent();
                $confidential->SetType($first->GetType());
                $confidential->AddProperty('SUMMARY', translate('Busy'));
                $confidential->AddProperty('CLASS', 'CONFIDENTIAL');
                $confidential->SetProperties($first->GetProperties('DTSTART'), 'DTSTART');
                $confidential->SetProperties($first->GetProperties('RRULE'), 'RRULE');
                $confidential->SetProperties($first->GetProperties('DURATION'), 'DURATION');
                $confidential->SetProperties($first->GetProperties('DTEND'), 'DTEND');
                $confidential->SetProperties($first->GetProperties('UID'), 'UID');
                $ical->SetComponents(array($confidential), $confidential->GetType());
                $caldav_data = $ical->Render();
                $displayname = translate('Busy');
            }
        }
    }
    $url = ConstructURL($item->dav_name);
    $prop = new XMLElement("prop");
    foreach ($properties as $k => $v) {
        switch ($k) {
            case 'getcontentlength':
                $contentlength = strlen($caldav_data);
                $prop->NewElement($k, $contentlength);
                break;
            case 'getlastmodified':
                $prop->NewElement($k, ISODateToHTTPDate($item->modified));
                break;
            case 'calendar-data':
                if ($type == 'calendar') {
                    $reply->CalDAVElement($prop, $k, $caldav_data);
                } else {
                    $unsupported[] = $k;
                }
                break;
            case 'address-data':
                if ($type == 'vcard') {
                    $reply->CardDAVElement($prop, $k, $caldav_data);
                } else {
                    $unsupported[] = $k;
                }
                break;
            case 'getcontenttype':
                $prop->NewElement($k, $contenttype);
                break;
            case 'current-user-principal':
                $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
                break;
            case 'displayname':
                $prop->NewElement($k, $displayname);
                break;
            case 'resourcetype':
                $prop->NewElement($k);
                // Just an empty resourcetype for a non-collection.
                break;
            case 'getetag':
                $prop->NewElement($k, '"' . $item->dav_etag . '"');
                break;
            case '"current-user-privilege-set"':
                $prop->NewElement($k, privileges($request->permissions));
                break;
            case 'SOME-DENIED-PROPERTY':
                /** indicating the style for future expansion */
                $denied[] = $k;
                break;
            default:
                dbg_error_log('REPORT', "Request for unsupported property '%s' of calendar item.", $v);
                $unsupported[] = $k;
        }
    }
    $status = new XMLElement("status", "HTTP/1.1 200 OK");
    $propstat = new XMLElement("propstat", array($prop, $status));
    $href = new XMLElement("href", $url);
    $elements = array($href, $propstat);
    if (count($denied) > 0) {
        $status = new XMLElement("status", "HTTP/1.1 403 Forbidden");
        $noprop = new XMLElement("prop");
        foreach ($denied as $k => $v) {
            $noprop->NewElement(strtolower($v));
        }
        $elements[] = new XMLElement("propstat", array($noprop, $status));
    }
    if (count($unsupported) > 0) {
        $status = new XMLElement("status", "HTTP/1.1 404 Not Found");
        $noprop = new XMLElement("prop");
        foreach ($unsupported as $k => $v) {
            $noprop->NewElement(strtolower($v));
        }
        $elements[] = new XMLElement("propstat", array($noprop, $status));
    }
    $response = new XMLElement("response", $elements);
    return $response;
}
Пример #16
0
function export_iCalendar(DAVResource $dav_resource)
{
    global $session, $c, $request;
    if (!$dav_resource->IsCalendar() && !(isset($c->get_includes_subcollections) && $c->get_includes_subcollections)) {
        /** RFC2616 says we must send an Allow header if we send a 405 */
        header("Allow: PROPFIND,PROPPATCH,OPTIONS,MKCOL,REPORT,DELETE");
        $request->DoResponse(405, translate("GET requests on collections are only supported for calendars."));
    }
    /**
     * The CalDAV specification does not define GET on a collection, but typically this is
     * used as a .ics download for the whole collection, which is what we do also.
     */
    if (isset($c->get_includes_subcollections) && $c->get_includes_subcollections) {
        $where = 'caldav_data.collection_id IN ';
        $where .= '(SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match ';
        $where .= 'UNION ';
        $where .= 'SELECT collection_id FROM collection WHERE collection.dav_name ~ :path_match) ';
        $params = array(':path_match' => '^' . $dav_resource->dav_name());
        $distinct = 'DISTINCT ON (calendar_item.uid) ';
    } else {
        $where = 'caldav_data.collection_id = :collection_id ';
        $params = array(':collection_id' => $dav_resource->resource_id());
        $distinct = '';
    }
    $sql = 'SELECT ' . $distinct . ' caldav_data, class, caldav_type, calendar_item.user_no, logged_user ';
    $sql .= 'FROM collection INNER JOIN caldav_data USING(collection_id) ';
    $sql .= 'INNER JOIN calendar_item USING ( dav_id ) WHERE ' . $where;
    if (isset($c->strict_result_ordering) && $c->strict_result_ordering) {
        $sql .= ' ORDER BY calendar_item.uid, calendar_item.dav_id';
    }
    $qry = new AwlQuery($sql, $params);
    if (!$qry->Exec("GET", __LINE__, __FILE__)) {
        $request->DoResponse(500, translate("Database Error"));
    }
    /**
     * Here we are constructing a whole calendar response for this collection, including
     * the timezones that are referred to by the events we have selected.
     */
    $vcal = new iCalComponent();
    $vcal->VCalendar();
    $displayname = $dav_resource->GetProperty('displayname');
    if (isset($displayname)) {
        $vcal->AddProperty("X-WR-CALNAME", $displayname);
    }
    if (!empty($c->auto_refresh_duration)) {
        $vcal->AddProperty("X-APPLE-AUTO-REFRESH-INTERVAL", $c->auto_refresh_duration);
        $vcal->AddProperty("AUTO-REFRESH", $c->auto_refresh_duration);
        $vcal->AddProperty("X-PUBLISHED-TTL", $c->auto_refresh_duration);
    }
    $need_zones = array();
    $timezones = array();
    while ($event = $qry->Fetch()) {
        $ical = new iCalComponent($event->caldav_data);
        /** Save the timezone component(s) into a minimal set for inclusion later */
        $event_zones = $ical->GetComponents('VTIMEZONE', true);
        foreach ($event_zones as $k => $tz) {
            $tzid = $tz->GetPValue('TZID');
            if (!isset($tzid)) {
                continue;
            }
            if ($tzid != '' && !isset($timezones[$tzid])) {
                $timezones[$tzid] = $tz;
            }
        }
        /** Work out which ones are actually used here */
        $comps = $ical->GetComponents('VTIMEZONE', false);
        foreach ($comps as $k => $comp) {
            $tzid = $comp->GetPParamValue('DTSTART', 'TZID');
            if (isset($tzid) && !isset($need_zones[$tzid])) {
                $need_zones[$tzid] = 1;
            }
            $tzid = $comp->GetPParamValue('DUE', 'TZID');
            if (isset($tzid) && !isset($need_zones[$tzid])) {
                $need_zones[$tzid] = 1;
            }
            $tzid = $comp->GetPParamValue('DTEND', 'TZID');
            if (isset($tzid) && !isset($need_zones[$tzid])) {
                $need_zones[$tzid] = 1;
            }
            if ($dav_resource->HavePrivilegeTo('all', false) || $session->user_no == $event->user_no || $session->user_no == $event->logged_user || isset($session->email) && $c->allow_get_email_visibility && $comp->IsAttendee($session->email)) {
                /**
                 * These people get to see all of the event, and they should always
                 * get any alarms as well.
                 */
                $vcal->AddComponent($comp);
                continue;
            }
            /** No visibility even of the existence of these events if they aren't admin/owner/attendee */
            if ($event->class == 'PRIVATE') {
                continue;
            }
            if (!$dav_resource->HavePrivilegeTo('DAV::read') || $event->class == 'CONFIDENTIAL') {
                $vcal->AddComponent(obfuscated_event($comp));
            } elseif (isset($c->hide_alarm) && $c->hide_alarm) {
                // Otherwise we hide the alarms (if configured to)
                $comp->ClearComponents('VALARM');
                $vcal->AddComponent($comp);
            } else {
                $vcal->AddComponent($comp);
            }
        }
    }
    /** Put the timezones on there that we need */
    foreach ($need_zones as $tzid => $v) {
        if (isset($timezones[$tzid])) {
            $vcal->AddComponent($timezones[$tzid]);
        }
    }
    return $vcal->Render();
}
Пример #17
0
<?php

require_once 'vendor/autoload.php';
$body = file_get_contents('testing/samples/meeting_request.txt');
$ical = new iCalComponent();
$ical->ParseFrom($body);
$props = $ical->GetPropertiesByPath('!VTIMEZONE/ATTENDEE');
if (count($props) == 1) {
    if (isset($props[0]->Parameters()["PARTSTAT"])) {
        printf("DOES THIS CAUSE ERROR? %s\n", $props[0]->Parameters()["PARTSTAT"]);
    }
}
// MODIFICATIONS
// METHOD
$ical->SetPValue("METHOD", "REPLY");
//ATTENDEE
$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", "ACCEPTED");
printf("%s\n", $ical->Render());
$mail = new Mail_mimepart();
$headers = array("MIME-version" => "1.0", "From" => $mail->encodeHeader("from", "Pedro Picapiedra <*****@*****.**>", "UTF-8"), "To" => $mail->encodeHeader("to", "Pablo Marmol <*****@*****.**>", "UTF-8"), "Date" => gmdate("D, d M Y H:i:s", time()) . " GMT", "Subject" => $mail->encodeHeader("subject", "This is a subject", "UTF-8"), "Content-class" => "urn:content-classes:calendarmessage", "Content-transfer-encoding" => "8BIT");
$mail = new Mail_mimepart($ical->Render(), array("content_type" => "text/calendar; method=REPLY; charset=UTF-8", "headers" => $headers));
$message = "";
$encoded_mail = $mail->encode();
foreach ($encoded_mail["headers"] as $k => $v) {
    $message .= $k . ": " . $v . "\r\n";
}
$message .= "\r\n" . $encoded_mail["body"] . "\r\n";
printf("%s\n", $message);
define('LOGLEVEL', LOGLEVEL_DEBUG);
define('LOGUSERLEVEL', LOGLEVEL_DEVICEID);
$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID");
Пример #18
0
 private function AddDefault(iCalComponent $component)
 {
     $properties = $component->GetProperties();
     $now = gmdate("Ymd\\THis\\Z");
     $a = array(1, 1, 1);
     foreach ($properties as $property) {
         //echo "D: " . $property->Name(). ":" . $property->Value() . "<br/>";
         if (strcasecmp('DTSTAMP', $property->Name()) === 0) {
             $property->Value($now);
             $a[0] = 0;
         }
         if (strcasecmp('LAST-MODIFIED', $property->Name()) === 0) {
             $property->Value($now);
             $a[1] = 0;
         }
         if (strcasecmp('X-WEBCAL-GENERATION', $property->Name()) === 0) {
             $property->Value('1');
             $a[2] = 0;
         }
     }
     for ($i = 0; $i < count($a); $i++) {
         //echo $i.':'.$a[$i]."<br/>";
         if ($a[$i]) {
             switch ($i) {
                 case 0:
                     $c['DTSTAMP'] = $now;
                     break;
                 case 1:
                     $c['LAST-MODIFIED'] = $now;
                     break;
                 case 2:
                     $c['X-WEBCAL-GENERATION'] = 1;
                     break;
                 default:
                     continue;
             }
             $key = key($c);
             $val = $c[$key];
             $component->AddProperty($key, $val);
             $c = NULL;
         }
     }
 }