Esempio n. 1
0
<?php

/**
* CalDAV Server - handle PUT method
*
* @package   davical
* @subpackage   caldav
* @author    Andrew McMillan <*****@*****.**>
* @copyright Catalyst .Net Ltd, Morphoss Ltd
* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
*/
dbg_error_log("PUT", "method handler");
require_once 'DAVResource.php';
$dav_resource = new DAVResource($request->path);
if (!$dav_resource->HavePrivilegeTo('DAV::write-content')) {
    $request->DoResponse(403);
}
if (!$dav_resource->Exists() && !$dav_resource->HavePrivilegeTo('DAV::bind')) {
    $request->DoResponse(403);
}
if (!ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['put']) && $c->dbg['put'])) {
    $fh = fopen('/tmp/PUT.txt', 'w');
    if ($fh) {
        fwrite($fh, $request->raw_post);
        fclose($fh);
    }
}
include_once 'caldav-PUT-functions.php';
controlRequestContainer($dav_resource->GetProperty('username'), $dav_resource->GetProperty('user_no'), $dav_resource->bound_from(), true);
$lock_opener = $request->FailIfLocked();
if ($dav_resource->IsCollection()) {
Esempio n. 2
0
/**
* Get XML response for items in the collection
* If '/' is requested, a list of visible users is given, otherwise
* a list of calendars for the user which are parented by this path.
*/
function get_collection_contents($depth, $collection, $parent_path = null)
{
    global $c, $session, $request, $reply, $property_list;
    $bound_from = $collection->bound_from();
    $bound_to = $collection->dav_name();
    if (!isset($parent_path)) {
        $parent_path = $collection->dav_name();
    }
    dbg_error_log('PROPFIND', 'Getting collection contents: Depth %d, Path: %s, Bound from: %s, Bound to: %s', $depth, $collection->dav_name(), $bound_from, $bound_to);
    $date_format = AwlDatabase::HttpDateFormat;
    $responses = array();
    if (!$collection->IsCalendar() && !$collection->IsAddressbook()) {
        /**
         * Calendar/Addressbook collections may not contain collections, so we are only looking in the other ones
         */
        $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth);
        if ($bound_from == '/') {
            $sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5(username || updated::text) AS dav_etag, ";
            $sql .= "to_char(joined at time zone 'GMT',{$date_format}) AS created, ";
            $sql .= "to_char(updated at time zone 'GMT',{$date_format}) AS modified, ";
            $sql .= 'FALSE AS is_calendar, TRUE AS is_principal, FALSE AS is_addressbook, \'principal\' AS type, ';
            $sql .= 'principal_id AS collection_id, ';
            $sql .= 'principal.* ';
            $sql .= 'FROM usr JOIN principal USING (user_no) ';
            $sql .= "WHERE (pprivs(:session_principal::int8,principal.principal_id,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN ";
            $sql .= 'ORDER BY usr.user_no';
        } else {
            $qry = new AwlQuery('SELECT * FROM dav_binding WHERE dav_binding.parent_container = :this_dav_name ORDER BY bind_id', array(':this_dav_name' => $bound_from));
            if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) {
                while ($binding = $qry->Fetch()) {
                    $resource = new DAVResource($binding->dav_name);
                    if ($resource->IsExternal()) {
                        require_once "external-fetch.php";
                        update_external($resource);
                    }
                    if ($resource->HavePrivilegeTo('DAV::read', false)) {
                        $resource->set_bind_location(str_replace($bound_from, $bound_to, $binding->dav_name));
                        $responses[] = $resource->RenderAsXML($property_list, $reply);
                        if ($depth > 0) {
                            $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, $binding->dav_name));
                        }
                    }
                }
            }
            $sql = 'SELECT principal.*, collection.*, \'collection\' AS type ';
            $sql .= 'FROM collection LEFT JOIN principal USING (user_no) ';
            $sql .= 'WHERE parent_container = :this_dav_name ';
            $sql .= ' ORDER BY collection_id';
            $params[':this_dav_name'] = $bound_from;
            unset($params[':session_principal']);
            unset($params[':scan_depth']);
        }
        $qry = new AwlQuery($sql, $params);
        if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) {
            while ($subcollection = $qry->Fetch()) {
                $resource = new DAVResource($subcollection);
                if (!$resource->HavePrivilegeTo('DAV::read')) {
                    continue;
                }
                $resource->set_bind_location(str_replace($bound_from, $bound_to, $subcollection->dav_name));
                $responses[] = $resource->RenderAsXML($property_list, $reply);
                if ($depth > 0) {
                    $responses = array_merge($responses, get_collection_contents($depth - 1, $resource, str_replace($resource->parent_path(), $parent_path, $resource->dav_name())));
                }
            }
        }
        if ((!isset($c->disable_caldav_proxy) || $c->disable_caldav_proxy == false) && $collection->IsPrincipal()) {
            // Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
            dbg_error_log('PROPFIND', 'Adding calendar-proxy-read and write. Path: %s', $bound_from);
            $response = add_proxy_response('read', $bound_from);
            if (isset($response)) {
                $responses[] = $response;
            }
            $response = add_proxy_response('write', $bound_from);
            if (isset($response)) {
                $responses[] = $response;
            }
        }
    }
    /**
     * freebusy permission is not allowed to see the items in a collection.  Must have at least read permission.
     */
    if ($collection->HavePrivilegeTo('DAV::read', false)) {
        dbg_error_log('PROPFIND', 'Getting collection items: Depth %d, Path: %s', $depth, $bound_from);
        $privacy_clause = ' ';
        $todo_clause = ' ';
        $time_limit_clause = ' ';
        if ($collection->IsCalendar()) {
            if (!$collection->HavePrivilegeTo('all', false)) {
                $privacy_clause = " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
            }
            if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$collection->HavePrivilegeTo('all')) {
                $todo_clause = " AND caldav_data.caldav_type NOT IN ('VTODO') ";
            }
            if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) {
                $time_limit_clause = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END) ";
            }
        }
        $sql = 'SELECT collection.*, principal.*, calendar_item.*, caldav_data.*, ';
        $sql .= "to_char(coalesce(calendar_item.created, caldav_data.created) at time zone 'GMT',{$date_format}) AS created, ";
        $sql .= "to_char(coalesce(calendar_item.last_modified, caldav_data.modified) at time zone 'GMT',{$date_format}) AS modified, ";
        $sql .= 'summary AS dav_displayname ';
        $sql .= 'FROM caldav_data LEFT JOIN calendar_item USING( dav_id, user_no, dav_name, collection_id) ';
        $sql .= 'LEFT JOIN collection USING(collection_id,user_no) LEFT JOIN principal USING(user_no) ';
        $sql .= 'WHERE collection.dav_name = :collection_dav_name ' . $time_limit_clause . ' ' . $todo_clause . ' ' . $privacy_clause;
        if (isset($c->strict_result_ordering) && $c->strict_result_ordering) {
            $sql .= " ORDER BY caldav_data.dav_id";
        }
        $qry = new AwlQuery($sql, array(':collection_dav_name' => $bound_from));
        if ($qry->Exec('PROPFIND', __LINE__, __FILE__) && $qry->rows() > 0) {
            while ($item = $qry->Fetch()) {
                if ($bound_from != $bound_to) {
                    $item->bound_from = $item->dav_name;
                    $item->dav_name = str_replace($bound_from, $bound_to, $item->dav_name);
                }
                $resource = new DAVResource($item);
                $responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path);
            }
        }
    }
    return $responses;
}
    if (!$qry->QDo($sql, $params) || $qry->rows() <= 0) {
        $request->DoResponse(500, translate("Database error"));
    }
    $row = $qry->Fetch();
}
$new_token = $row->new_sync_token;
if ($sync_token == $new_token) {
    // No change, so we just re-send the old token.
    $responses[] = new XMLElement('sync-token', 'data:,' . $new_token);
} else {
    $hide_older = '';
    if (isset($c->hide_older_than) && intval($c->hide_older_than) > 0) {
        $hide_older = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END)";
    }
    $hide_todo = '';
    if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$collection->HavePrivilegeTo('all')) {
        $hide_todo = " AND caldav_data.caldav_type NOT IN ('VTODO') ";
    }
    if ($sync_token == 0) {
        $sql = <<<EOSQL
  SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, 201 AS sync_status FROM collection
              LEFT JOIN caldav_data USING (collection_id)
              LEFT JOIN calendar_item USING (dav_id)
                           LEFT JOIN addressbook_resource USING (dav_id)
              WHERE collection.collection_id = :collection_id {$hide_older} {$hide_todo}
     ORDER BY collection.collection_id, caldav_data.dav_id
EOSQL;
        unset($params[':sync_token']);
    } else {
        $sql = <<<EOSQL
  SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, sync_changes.*
Esempio n. 4
0
 /** 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 || $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)
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();
}
/**
* Deliver scheduling replies to organizer and other attendees
* @param vComponent $ical the VCALENDAR to deliver
* @return false on error
*/
function handle_schedule_reply(vCalendar $ical)
{
    global $c, $session, $request;
    $resources = $ical->GetComponents('VTIMEZONE', false);
    $ic = $resources[0];
    $etag = md5($request->raw_post);
    $organizer = $ical->GetOrganizer();
    // for now we treat events with out organizers as an error
    if (empty($organizer)) {
        return false;
    }
    $att = $ical->GetAttendees();
    $attendees = array_merge($organizer, $att);
    dbg_error_log("PUT", "Attempting to deliver scheduling request for %d attendees", count($attendees));
    foreach ($attendees as $k => $attendee) {
        $attendee_email = preg_replace('/^mailto:/i', '', $attendee->Value());
        dbg_error_log("PUT", "Delivering to %s", $attendee_email);
        $attendee_principal = new DAVPrincipal(array('email' => $attendee_email, 'options' => array('allow_by_email' => true)));
        $deliver_path = $attendee_principal->internal_url('schedule_inbox');
        $attendee_email = preg_replace('/^mailto:/i', '', $attendee->Value());
        if ($attendee_email == $request->principal->email) {
            dbg_error_log("PUT", "not delivering to owner");
            continue;
        }
        $ar = new DAVResource($deliver_path);
        if (!$ar->HavePrivilegeTo('schedule-deliver-reply')) {
            $reply = new XMLDocument(array('DAV:' => ''));
            $privnodes = array($reply->href($attendee_principal->url('schedule_inbox')), new XMLElement('privilege'));
            // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
            $reply->NSElement($privnodes[1], 'schedule-deliver-reply');
            $xml = new XMLElement('need-privileges', new XMLElement('resource', $privnodes));
            $xmldoc = $reply->Render('error', $xml);
            $request->DoResponse(403, $xmldoc, 'text/xml; charset="utf-8"');
            continue;
        }
        $ncal = new vCalendar(array('METHOD' => 'REPLY'));
        $ncal->AddComponent(array_merge($ical->GetComponents('VEVENT', false), array($ic)));
        $content = $ncal->Render();
        write_resource(new DAVResource($deliver_path . $etag . '.ics'), $content, $ar, $request->user_no, md5($content), $put_action_type = 'INSERT', $caldav_context = true, $log_action = true, $etag);
    }
    $request->DoResponse(201, 'Created');
}
Esempio n. 7
0
    $where .= 'OR collection.collection_id IN (SELECT bound_source_id FROM dav_binding WHERE dav_binding.dav_name ~ :path_match)) ';
    $params = array(':path_match' => '^' . $target_collection->bound_from());
}
if (is_array($qry_filters)) {
    dbg_log_array("calquery", "qry_filters", $qry_filters, true);
    $components = array();
    $filter_fragment = SqlFilterFragment($qry_filters, $components);
    if ($filter_fragment !== false) {
        $where .= ' ' . $filter_fragment['sql'];
        $params = array_merge($params, $filter_fragment['params']);
    }
}
if ($target_collection->Privileges() != privilege_to_bits('DAV::all')) {
    $where .= " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
}
if (isset($c->hide_TODO) && $c->hide_TODO && !$target_collection->HavePrivilegeTo('DAV::write-content')) {
    $where .= " AND caldav_data.caldav_type NOT IN ('VTODO') ";
}
if (isset($c->hide_older_than) && intval($c->hide_older_than > 0)) {
    $where .= " AND calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') ";
}
$sql = 'SELECT caldav_data.*,calendar_item.*  FROM collection INNER JOIN caldav_data USING(collection_id) INNER JOIN calendar_item USING(dav_id) ' . $where;
if (isset($c->strict_result_ordering) && $c->strict_result_ordering) {
    $sql .= " ORDER BY caldav_data.dav_id";
}
$qry = new AwlQuery($sql, $params);
if ($qry->Exec("calquery", __LINE__, __FILE__) && $qry->rows() > 0) {
    while ($calendar_object = $qry->Fetch()) {
        if (!$need_post_filter || apply_filter($qry_filters, $calendar_object)) {
            if ($bound_from != $target_collection->dav_name()) {
                $calendar_object->dav_name = str_replace($bound_from, $target_collection->dav_name(), $calendar_object->dav_name);
Esempio n. 8
0
<?php

/**
* CalDAV Server - handle PROPPATCH method
*
* @package   davical
* @subpackage   caldav
* @author    Andrew McMillan <*****@*****.**>
* @copyright Morphoss Ltd - http://www.morphoss.com/
* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
dbg_error_log("PROPPATCH", "method handler");
require_once 'iCalendar.php';
require_once 'DAVResource.php';
$dav_resource = new DAVResource($request->path);
if (!($dav_resource->HavePrivilegeTo('DAV::write-properties') || $dav_resource->IsBinding())) {
    $request->DoResponse(403);
}
$position = 0;
$xmltree = BuildXMLTree($request->xml_tags, $position);
// echo $xmltree->Render();
if ($xmltree->GetTag() != "DAV::propertyupdate") {
    $request->DoResponse(403);
}
/**
* Find the properties being set, and the properties being removed
*/
$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
$rmprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::remove/DAV::prop/*");
/**
* We build full status responses for failures.  For success we just record
Esempio n. 9
0
<?php

/**
* CalDAV Server - handle PROPPATCH method
*
* @package   davical
* @subpackage   caldav
* @author    Andrew McMillan <*****@*****.**>
* @copyright Morphoss Ltd - http://www.morphoss.com/
* @license   http://gnu.org/copyleft/gpl.html GNU GPL v2
*/
dbg_error_log("PROPPATCH", "method handler");
require_once 'vCalendar.php';
require_once 'DAVResource.php';
$dav_resource = new DAVResource($request->path);
if (!$dav_resource->HavePrivilegeTo('DAV::write-properties')) {
    $parent = $dav_resource->GetParentContainer();
    if (!$dav_resource->IsBinding() || !$parent->HavePrivilegeTo('DAV::write')) {
        $request->PreconditionFailed(403, 'DAV::write-properties', 'You do not have permission to write properties to that resource');
    }
}
$position = 0;
$xmltree = BuildXMLTree($request->xml_tags, $position);
// echo $xmltree->Render();
if ($xmltree->GetNSTag() != "DAV::propertyupdate") {
    $request->PreconditionFailed(403, 'DAV::propertyupdate', 'XML request did not contain a &lt;propertyupdate&gt; tag');
}
/**
* Find the properties being set, and the properties being removed
*/
$setprops = $xmltree->GetPath("/DAV::propertyupdate/DAV::set/DAV::prop/*");
Esempio n. 10
0
/**
* Deliver scheduling replies to organizer and other attendees
* @param iCalComponent $ical the VCALENDAR to deliver
* @return false on error
*/
function handle_schedule_reply($ical)
{
    global $c, $session, $request;
    $resources = $ical->GetComponents('VTIMEZONE', false);
    $ic = $resources[0];
    $etag = md5($request->raw_post);
    $organizer = $ic->GetProperties('ORGANIZER');
    // for now we treat events with out organizers as an error
    if (count($organizer) < 1) {
        return false;
    }
    $attendees = array_merge($organizer, $ic->GetProperties('ATTENDEE'));
    $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
    if (count($wr_attendees) > 0) {
        dbg_error_log("POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property");
        foreach ($wr_attendees as $k => $v) {
            $attendees[] = $v;
        }
    }
    dbg_error_log("POST", "Attempting to deliver scheduling request for %d attendees", count($attendees));
    foreach ($attendees as $k => $attendee) {
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        dbg_error_log("POST", "Delivering to %s", $attendee_email);
        $attendee_principal = new CalDAVPrincipal(array('email' => $attendee_email, 'options' => array('allow_by_email' => true)));
        $deliver_path = preg_replace('/^.*caldav.php/', '', $attendee_principal->schedule_inbox_url);
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        if ($attendee_email == $request->principal->email) {
            dbg_error_log("POST", "not delivering to owner");
            continue;
        }
        $ar = new DAVResource($deliver_path);
        if (!$ar->HavePrivilegeTo('schedule-deliver-reply')) {
            $reply = new XMLDocument(array('DAV:' => ''));
            $privnodes = array($reply->href(ConstructURL($attendee_principal->schedule_inbox_url)), new XMLElement('privilege'));
            // RFC3744 specifies that we can only respond with one needed privilege, so we pick the first.
            $reply->NSElement($privnodes[1], 'schedule-deliver-reply');
            $xml = new XMLElement('need-privileges', new XMLElement('resource', $privnodes));
            $xmldoc = $reply->Render('error', $xml);
            $request->DoResponse(403, $xmldoc, 'text/xml; charset="utf-8"');
            continue;
        }
        $ncal = new iCalComponent();
        $ncal->VCalendar();
        $ncal->AddProperty('METHOD', 'REPLY');
        $ncal->AddComponent(array_merge($ical->GetComponents('VEVENT', false), array($ic)));
        $content = $ncal->Render();
        write_resource($attendee_principal->user_no, $deliver_path . $etag . '.ics', $content, $ar->GetProperty('collection_id'), $request->user_no, md5($content), $ncal, $put_action_type = 'INSERT', $caldav_context = true, $log_action = true, $etag);
    }
    $request->DoResponse(201, 'Created');
}
Esempio n. 11
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 (strtoupper($item->caldav_type)) {
        case 'VJOURNAL':
        case 'VEVENT':
        case 'VTODO':
            $displayname = $item->summary;
            $type = 'calendar';
            $contenttype = 'text/calendar';
            if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) || isset($properties['DAV::displayname'])) {
                if (!$request->AllowedTo('all') && $session->user_no != $item->user_no) {
                    // the user is not admin / owner of this calendar looking at his calendar and can not admin the other cal
                    if ($item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read')) {
                        dbg_error_log("REPORT", "Anonymising confidential event for: %s", $item->dav_name);
                        $vcal = new vCalendar($caldav_data);
                        $caldav_data = $vcal->Confidential()->Render();
                        $displayname = translate('Busy');
                    }
                }
            }
            if (isset($c->hide_alarm) && $c->hide_alarm) {
                $dav_resource = new DAVResource($item->dav_name);
                if (isset($properties['urn:ietf:params:xml:ns:caldav:calendar-data']) && !$dav_resource->HavePrivilegeTo('write')) {
                    dbg_error_log("REPORT", "Stripping event alarms for: %s", $item->dav_name);
                    $vcal = new vCalendar($caldav_data);
                    $vcal->ClearComponents('VALARM');
                    $caldav_data = $vcal->Render();
                }
            }
            break;
        case 'VCARD':
            $displayname = $item->fn;
            $type = 'vcard';
            $contenttype = 'text/vcard';
            break;
    }
    $url = ConstructURL($item->dav_name);
    $prop = new XMLElement("prop");
    $need_resource = false;
    foreach ($properties as $full_tag => $v) {
        $base_tag = preg_replace('{^.*:}', '', $full_tag);
        switch ($full_tag) {
            case 'DAV::getcontentlength':
                $contentlength = strlen($caldav_data);
                $prop->NewElement($base_tag, $contentlength);
                break;
            case 'DAV::getlastmodified':
                $prop->NewElement($base_tag, ISODateToHTTPDate($item->modified));
                break;
            case 'urn:ietf:params:xml:ns:caldav:calendar-data':
                if ($type == 'calendar') {
                    $reply->CalDAVElement($prop, $base_tag, $caldav_data);
                } else {
                    $unsupported[] = $base_tag;
                }
                break;
            case 'urn:ietf:params:xml:ns:carddav:address-data':
                if ($type == 'vcard') {
                    $reply->CardDAVElement($prop, $base_tag, $caldav_data);
                } else {
                    $unsupported[] = $base_tag;
                }
                break;
            case 'DAV::getcontenttype':
                $prop->NewElement($base_tag, $contenttype);
                break;
            case 'DAV::current-user-principal':
                $prop->NewElement("current-user-principal", $request->current_user_principal_xml);
                break;
            case 'DAV::displayname':
                $prop->NewElement($base_tag, $displayname);
                break;
            case 'DAV::resourcetype':
                $prop->NewElement($base_tag);
                // Just an empty resourcetype for a non-collection.
                break;
            case 'DAV::getetag':
                $prop->NewElement($base_tag, '"' . $item->dav_etag . '"');
                break;
            case '"current-user-privilege-set"':
                $prop->NewElement($base_tag, privileges($request->permissions));
                break;
            default:
                // It's harder.  We need the DAVResource() to get this one.
                $need_resource = true;
        }
        if ($need_resource) {
            break;
        }
    }
    $href = new XMLElement("href", $url);
    if ($need_resource) {
        if (!isset($dav_resource)) {
            $dav_resource = new DAVResource($item->dav_name);
        }
        $elements = $dav_resource->GetPropStat(array_keys($properties), $reply);
        array_unshift($elements, $href);
    } else {
        $elements = array($href);
        $status = new XMLElement("status", "HTTP/1.1 200 OK");
        $elements[] = new XMLElement("propstat", array($prop, $status));
        if (count($denied) > 0) {
            $status = new XMLElement("status", "HTTP/1.1 403 Forbidden");
            $noprop = new XMLElement("prop");
            foreach ($denied as $k => $v) {
                $reply->NSElement($noprop, $v);
            }
            $elements[] = new XMLElement("propstat", array($noprop, $status));
        }
        if (!$request->PreferMinimal() && count($unsupported) > 0) {
            $status = new XMLElement("status", "HTTP/1.1 404 Not Found");
            $noprop = new XMLElement("prop");
            foreach ($unsupported as $k => $v) {
                $reply->NSElement($noprop, $v);
            }
            $elements[] = new XMLElement("propstat", array($noprop, $status));
        }
    }
    $response = new XMLElement("response", $elements);
    return $response;
}