From 3bb6cd447999580101f77166040fbe71161d28a5 Mon Sep 17 00:00:00 2001 From: Frank Steinberg Date: Thu, 21 Jan 2016 13:33:30 +0100 Subject: [PATCH] Resolve attendee group names to lists of individual users. Configurable by $c->enable_attendee_group_resolution (from !21) --- config/example-config.php | 9 +++++++ inc/caldav-POST.php | 29 +++++++++++++++++++++++ inc/caldav-PUT-functions.php | 46 ++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/config/example-config.php b/config/example-config.php index 9dad9295..9503fc5f 100644 --- a/config/example-config.php +++ b/config/example-config.php @@ -300,6 +300,15 @@ $c->admin_email ='calendar-admin@example.com'; */ // $c->allow_get_email_visibility = false; +/** +* EXPERIMENTAL: +* If true, names of groups (prefixed with "@") given as an event attendee +* will get resolved to a list of members of that group. Note that CalDAV +* clients might get confused by this server behavior until they get +* synced again. Default: false. +*/ +// $c->enable_attendee_group_resolution = true; + /*************************************************************************** * * diff --git a/inc/caldav-POST.php b/inc/caldav-POST.php index f722f880..88df1286 100644 --- a/inc/caldav-POST.php +++ b/inc/caldav-POST.php @@ -50,6 +50,35 @@ function handle_freebusy_request( $ic ) { } dbg_error_log( "POST", "Responding with free/busy for %d attendees", count($attendees) ); + if ($c->enable_attendee_group_resolution) { + $new_attendees = array(); + foreach( $attendees AS $attendee ) { + $v = $attendee->Value(); + unset($localname); + if ($v == "invalid:nomail") { + $localname = $attendee->GetParameterValue("CN"); + } else if ((preg_match('/^@/', $v) == 1) || (preg_match('/mailto:@/',$v) == 1)) { + $localname = preg_replace('/^.*@/', '', $v); + } else if (preg_match('/@/', $v) != 1) { + $localname = $v; + } + if ($localname) { + dbg_error_log( 'POST', 'try to resolve local attendee %s', $localname); + $qry = new AwlQuery('SELECT fullname, email FROM usr WHERE user_no = (SELECT user_no FROM principal WHERE type_id = 1 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username)))) UNION SELECT fullname, email FROM usr WHERE user_no IN (SELECT user_no FROM principal WHERE principal_id IN (SELECT member_id FROM group_member WHERE group_id = (SELECT principal_id FROM principal WHERE type_id = 3 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username))))))', array(':username' => strtolower($localname))); + if ( $qry->Exec('POST',__LINE__,__FILE__) && $qry->rows() >= 1 ) { + dbg_error_log( 'POST', 'resolved local name %s to %d individual attendees', $localname, $qry->rows()); + while ($row = $qry->Fetch()) { + dbg_error_log( 'POST', 'adding individual attendee %s <%s>', $row->fullname, $row->email); + $new_attendees[] = new vProperty("ATTENDEE:mailto:" . $row->email); + } + } + } else { + $new_attendees[] = clone($attendee); + } + } + $attendees = $new_attendees; + } + foreach( $attendees AS $k => $attendee ) { $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() ); dbg_error_log( "POST", "Calculating free/busy for %s", $attendee_email ); diff --git a/inc/caldav-PUT-functions.php b/inc/caldav-PUT-functions.php index cd40b82a..c5453a98 100644 --- a/inc/caldav-PUT-functions.php +++ b/inc/caldav-PUT-functions.php @@ -510,6 +510,52 @@ function do_scheduling_requests( vCalendar $resource, $create, $old_data = null return do_scheduling_reply($resource,$organizer); } + if ($c->enable_attendee_group_resolution) { + $mail_domain = preg_replace( '/^.*@/i', '', $c->admin_email ); + $attendees = $resource->GetAttendees(); + $new_attendees = array(); + foreach( $attendees AS $attendee ) { + $v = $attendee->Value(); + unset($localname); + if ($v == "invalid:nomail") { + $localname = $attendee->GetParameterValue("CN"); + } else if ((preg_match('/^@/', $v) == 1) || (preg_match('/mailto:@/',$v) == 1)) { + $localname = preg_replace('/^.*@/', '', $v); + } else if (preg_match('/@/', $v) != 1) { + $localname = $v; + } else if (preg_match('/@'.$mail_domain.'/', $v) == 1) { + $localname = preg_replace('/@.*$/', '', $v); + $localname = preg_replace('/^mailto:/', '', $localname); + } + if ($localname) { + dbg_error_log( 'PUT', 'try to resolve local attendee %s', $localname); + $qry = new AwlQuery('SELECT fullname, email FROM usr WHERE user_no = (SELECT user_no FROM principal WHERE type_id = 1 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username)))) UNION SELECT fullname, email FROM usr WHERE user_no IN (SELECT user_no FROM principal WHERE principal_id IN (SELECT member_id FROM group_member WHERE group_id = (SELECT principal_id FROM principal WHERE type_id = 3 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username))))))', array(':username' => strtolower($localname))); + if ( $qry->Exec('PUT',__LINE__,__FILE__) && $qry->rows() >= 1 ) { + dbg_error_log( 'PUT', 'resolved local name %s to %d individual attendees', $localname, $qry->rows()); + while ($row = $qry->Fetch()) { + if ($row->email == $request->principal->email()) continue; + dbg_error_log( 'PUT', 'adding individual attendee %s <%s>', $row->fullname, $row->email); + $a = clone($attendee); + $a->SetParameterValue("CN", $row->fullname); + $a->SetParameterValue("PARTSTAT", "NEEDS-ACTION"); + $a->Value("mailto:" . $row->email); + $new_attendees[] = $a; + } + } else { + $new_attendees[] = clone($attendee); + } + } else { + $new_attendees[] = clone($attendee); + } + } + $events = $resource->GetComponents("VEVENT"); + $event = $events[0]; + $event->SetProperties($new_attendees,'ATTENDEE'); + // this is just to reset the private attribute "attendees" in the $resource object + $resource->UpdateAttendeeStatus("this-is-nonsense", new vProperty("ATTENDEE:dummy")); + $attendees = $resource->GetAttendees(); + } + // required because we set the schedule status on the original object (why clone() not works here?) $orig_resource = new vCalendar($resource->Render(null, true));