calendar/lib/calendar_itip.php
Aleksander Machniak e11e26ed24 Sync v3.5.7
2021-05-09 09:09:57 +02:00

258 lines
8.4 KiB
PHP

<?php
require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_itip.php');
/**
* iTIP functions for the Calendar plugin
*
* Class providing functionality to manage iTIP invitations
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
* @package @package_name@
*
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
class calendar_itip extends libcalendaring_itip
{
/**
* Constructor to set text domain to calendar
*/
function __construct($plugin, $domain = 'calendar')
{
parent::__construct($plugin, $domain);
$this->db_itipinvitations = $this->rc->db->table_name('itipinvitations', true);
}
/**
* Handler for calendar/itip-status requests
*/
public function get_itip_status($event, $existing = null)
{
$status = parent::get_itip_status($event, $existing);
// don't ask for deleting events when declining
if ($this->rc->config->get('kolab_invitation_calendars')) {
$status['saved'] = false;
}
return $status;
}
/**
* Find invitation record by token
*
* @param string $token Invitation token
*
* @return mixed Invitation record as hash array or False if not found
*/
public function get_invitation($token)
{
if ($parts = $this->decode_token($token)) {
$result = $this->rc->db->query("SELECT * FROM $this->db_itipinvitations WHERE `token` = ?", $parts['base']);
if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
$rec['event'] = unserialize($rec['event']);
$rec['attendee'] = $parts['attendee'];
return $rec;
}
}
return false;
}
/**
* Update the attendee status of the given invitation record
*
* @param array $invitation Invitation record as fetched with calendar_itip::get_invitation()
* @param string $email Attendee email address
* @param string $newstatus New attendee status
*/
public function update_invitation($invitation, $email, $newstatus)
{
if (is_string($invitation)) {
$invitation = $this->get_invitation($invitation);
}
if (!empty($invitation['token']) && !empty($invitation['event'])) {
// update attendee record in event data
foreach ($invitation['event']['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER') {
$organizer = $attendee;
}
else if ($attendee['email'] == $email) {
// nothing to be done here
if ($attendee['status'] == $newstatus) {
return true;
}
$invitation['event']['attendees'][$i]['status'] = $newstatus;
$this->sender = $attendee;
}
}
$invitation['event']['changed'] = new DateTime();
// send iTIP REPLY message to organizer
if (!empty($organizer)) {
$status = strtolower($newstatus);
if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) {
$mailto = !empty($organizer['name']) ? $organizer['name'] : $organizer['email'];
$message = $this->plugin->gettext([
'name' => 'sentresponseto',
'vars' => ['mailto' => $mailto]
]);
$this->rc->output->command('display_message', $message, 'confirmation');
}
else {
$this->rc->output->command('display_message', $this->plugin->gettext('itipresponseerror'), 'error');
}
}
// update record in DB
$query = $this->rc->db->query(
"UPDATE $this->db_itipinvitations SET `event` = ? WHERE `token` = ?",
self::serialize_event($invitation['event']),
$invitation['token']
);
if ($this->rc->db->affected_rows($query)) {
return true;
}
}
return false;
}
/**
* Create iTIP invitation token for later replies via URL
*
* @param array $event Hash array with event properties
* @param string $attendee Attendee email address
*
* @return string Invitation token
*/
public function store_invitation($event, $attendee)
{
static $stored = [];
if (empty($event['uid']) || !$attendee) {
return false;
}
// generate token for this invitation
$token = $this->generate_token($event, $attendee);
$base = substr($token, 0, 40);
// already stored this
if (!empty($stored[$base])) {
return $token;
}
// delete old entry
$this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base);
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
$query = $this->rc->db->query(
"INSERT INTO $this->db_itipinvitations"
. " (`token`, `event_uid`, `user_id`, `event`, `expires`)"
. " VALUES(?, ?, ?, ?, ?)",
$base,
$event_uid,
$this->rc->user->ID,
self::serialize_event($event),
date('Y-m-d H:i:s', $event['end']->format('U') + 86400 * 2)
);
if ($this->rc->db->affected_rows($query)) {
$stored[$base] = 1;
return $token;
}
return false;
}
/**
* Mark invitations for the given event as cancelled
*
* @param array $event Hash array with event properties
*/
public function cancel_itip_invitation($event)
{
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
// flag invitation record as cancelled
$this->rc->db->query(
"UPDATE $this->db_itipinvitations SET `cancelled` = 1"
. " WHERE `event_uid` = ? AND `user_id` = ?",
$event_uid,
$this->rc->user->ID
);
}
/**
* Generate an invitation request token for the given event and attendee
*
* @param array $event Event hash array
* @param string $attendee Attendee email address
*/
public function generate_token($event, $attendee)
{
$event_uid = $event['uid'] . (!empty($event['_instance']) ? '-' . $event['_instance'] : '');
$base = sha1($event_uid . ';' . $this->rc->user->ID);
$mail = base64_encode($attendee);
$hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
return "$base.$mail.$hash";
}
/**
* Decode the given iTIP request token and return its parts
*
* @param string $token Request token to decode
*
* @return mixed Hash array with parts or False if invalid
*/
public function decode_token($token)
{
list($base, $mail, $hash) = explode('.', $token);
// validate and return parts
if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
return ['base' => $base, 'attendee' => base64_decode($mail)];
}
return false;
}
/**
* Helper method to serialize the given event for storing in invitations table
*/
private static function serialize_event($event)
{
$ev = $event;
if (!empty($ev['description'])) {
$ev['description'] = abbreviate_string($ev['description'], 100);
}
unset($ev['attachments']);
return serialize($ev);
}
}