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; }
/** * 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; }
/** * 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; }
/** * 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; } } }
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; }