From 0846fa78f3c401d0ef99346335be51267ed2c8d6 Mon Sep 17 00:00:00 2001 From: ralf Date: Wed, 13 Mar 2024 17:25:22 +0200 Subject: [PATCH] * Calendar/CalDAV: do not allow to resurrect a deleted meeting by accepting it again via CalDAV or meeting-request from mail app --- calendar/inc/class.calendar_groupdav.inc.php | 56 ++++++++++++++- calendar/inc/class.calendar_uiforms.inc.php | 15 ++-- calendar/templates/default/meeting.xet | 76 +++++++++++++------- 3 files changed, 107 insertions(+), 40 deletions(-) diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 7e4c0f2f84..78de22c90d 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -1067,6 +1067,25 @@ class calendar_groupdav extends Api\CalDAV\Handler } } + // if path not found, check the UID and return "403 Forbidden" if event is deleted or user has not rights to event with same UID + if (!isset($oldEvent) && ($events = $handler->icaltoegw($vCalendar)) && + ($oldEvents = $this->bo->read(['cal_uid' => $events[0]['uid'], 'cal_reference=0'], null, false, 'server')) !== null) + { + foreach($oldEvents as $oldEvent) + { + if (empty($oldEvent['deleted'])) + { + break; + } + } + if (!empty($oldEvent['deleted'])) + { + $this->caldav->log("Event with UID='{$events[0]['uid']}' has already been deleted!"); + return '403 Forbidden'; + } + // case user has no edit-rights for $oldEvent is handled below + } + if (is_array($oldEvent)) { $eventId = $oldEvent['id']; @@ -1583,6 +1602,21 @@ class calendar_groupdav extends Api\CalDAV\Handler $return_no_access = true; // to allow to check if current use is a participant and reject the event for him $event = $this->_common_get_put_delete('DELETE',$options,$id,$return_no_access); + /* user has delete-rights, check if we have an external organizer and more participants + if ($event && $return_no_access && count($event['participants']) > 2) + { + foreach($event['participants'] as $uid => $status) + { + $quantity = $role = null; + calendar_so::split_status($status, $quantity, $role); + if (!is_numeric($uid) && $role == 'CHAIR') break; + } + if (!(!is_numeric($uid) && $role == 'CHAIR')) + { + $return_no_access = false; // only set status rejected, but do NOT delete the event + } + }*/ + // no event found --> 404 Not Found if (!is_array($event)) { @@ -1643,15 +1677,31 @@ class calendar_groupdav extends Api\CalDAV\Handler * the same UID and/or caldav_name as not deleted events and would block access to valid entries * * @param string|id $id - * @return array|boolean array with entry, false if no read rights, null if $id does not exist + * @return array|boolean array with entry, false if no read rights or deleted, null if $id does not exist */ function read($id) { if (strpos($column=self::$path_attr,'_') === false) $column = 'cal_'.$column; - $event = $this->bo->read(array($column => $id, 'cal_deleted IS NULL', 'cal_reference=0'), null, true, + $event = $this->bo->read(array($column => $id, 'cal_reference=0'), null, true, $date_format = Api\CalDAV::isJSON() ? 'object' : 'server'); - if ($event) $event = array_shift($event); // read with array as 1. param, returns an array of events! + + // read with array as 1. param, returns an array of events! + // as we no longer return only NOT-deleted events, there might be more + if ($event) + { + foreach ($event as $event) + { + if (empty($event['cal_deleted'])) break; + } + // the above prefers a NOT deleted event over deleted noes, thought all might be deleted + if (!empty($event['cal_deleted'])) + { + $retval = false; + if ($this->debug > 0) error_log(__METHOD__."($id) event has been deleted returning ".array2string($retval)); + return $retval; + } + } if (!($retval = $this->bo->check_perms(calendar_bo::ACL_FREEBUSY,$event, 0, 'server')) && // above can be true, if current user is not in master but just a recurrence diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index decaa5fdf7..96dc68f041 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -2130,8 +2130,7 @@ class calendar_uiforms extends calendar_ui $event['end'] += $diff; } - if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true)) && // true = read the exception - !$existing_event['deleted']) + if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true))) // true = read the exception { // check if mail is from extern organizer $from_extern_organizer = false; @@ -2179,7 +2178,7 @@ class calendar_uiforms extends calendar_ui // warn user about party-crashers (non-participants sending a reply) if (!isset($existing_status)) { - if (!empty($event['sender_warning'])) $event['sender_warning'] .= "\n"; + if (!empty($event['sender_warning'])) $event['sender_warning'] .= "\n\n"; $event['sender_warning'] .= lang('Replying "%1" is NOT a participant of the event! Only continue if you want to add as new participant.', $participant); } calendar_so::split_status($existing_status, $quantity, $role); @@ -2255,7 +2254,7 @@ class calendar_uiforms extends calendar_ui $event['recure'] = $this->bo->recure2string($event); $event['all_participants'] = implode(",\n",$this->bo->participants($event, true)); - // EGroupware event has been deleted, dont let user resurect it by accepting again + // EGroupware event has been deleted, don't let user resurrect it by accepting again if ($existing_event && $existing_event['deleted'] && strtolower($ical_method) !== 'cancel') { // check if this is an EGroupware event or has an external organizer @@ -2265,12 +2264,8 @@ class calendar_uiforms extends calendar_ui calendar_so::split_status($status, $quantity, $role); if (!is_numeric($uid) && $role == 'CHAIR') break; } - if (!(!is_numeric($uid) && $role == 'CHAIR')) - { - $event['error'] = lang('Event has been deleted by organizer!'); - $readonlys['button[accept]'] = $readonlys['button[tentativ]'] = - $readonlys['button[reject]'] = $readonlys['button[cancel]'] = true; - } + $event['sender_warning'] = lang('Event has been deleted by organizer!'). + (!empty($event['sender_warning']) ? "\n\n".$event['sender_warning'] : ''); } // ignore events in the past (for recurring events check enddate!) elseif ($this->bo->date2ts($event['start']) < $this->bo->now_su && diff --git a/calendar/templates/default/meeting.xet b/calendar/templates/default/meeting.xet index 0464205224..c09b998d6c 100644 --- a/calendar/templates/default/meeting.xet +++ b/calendar/templates/default/meeting.xet @@ -10,7 +10,7 @@ - + @@ -19,35 +19,42 @@ - + - - - - - - - - - + + + + + + + + + + - - - + + + + + + - - - - - - - + + + + + + + + @@ -61,6 +68,9 @@ + + + @@ -75,9 +85,9 @@ - + - + @@ -107,7 +117,7 @@ overflow-y: auto; } .meetingRequestMessage { - font-size: 120%; + font-size: 150%; } table.meetingRequest { @@ -123,6 +133,7 @@ } .meetingRequest td { padding: 3px; + padding-left: 0; } .meetingRequest a { display: inline-block; @@ -130,11 +141,22 @@ overflow-wrap: break-word; vertical-align: text-top; } - + et2-hbox.dates { + padding-left: 5px; + } + .meetingWarning tr.th { + background-color: #dc2625; + } + .meetingWarning tr.row td { + font-size: 120% !important; + } .meetingRequestError { - color: red; font-style: italic; - font-size: 120%; + color: red; + } + .buttonRow { + padding-top: 10px; + padding-bottom: 10px; }