Page MenuHomePhorge

No OneTemporary

diff --git a/plugins/calendar/drivers/database/sql/mysql.sql b/plugins/calendar/drivers/database/sql/mysql.sql
index 6b530f07..d7887a9f 100644
--- a/plugins/calendar/drivers/database/sql/mysql.sql
+++ b/plugins/calendar/drivers/database/sql/mysql.sql
@@ -1,77 +1,79 @@
/**
* Roundcube Calendar
*
* Plugin to add a calendar to Roundcube.
*
* @version @package_version@
* @author Lazlo Westerhof
* @author Thomas Bruederli
* @url http://rc-calendar.lazlo.me
* @licence GNU AGPL
* @copyright (c) 2010 Lazlo Westerhof - Netherlands
*
**/
CREATE TABLE `calendars` (
`calendar_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL,
`color` varchar(8) NOT NULL,
`showalarms` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY(`calendar_id`),
+ INDEX `user_name_idx` (`user_id`, `name`),
CONSTRAINT `fk_calendars_user_id` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE `events` (
`event_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`calendar_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`recurrence_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`uid` varchar(255) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`start` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`end` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`recurrence` varchar(255) DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`location` varchar(255) NOT NULL DEFAULT '',
`categories` varchar(255) NOT NULL DEFAULT '',
`all_day` tinyint(1) NOT NULL DEFAULT '0',
`free_busy` tinyint(1) NOT NULL DEFAULT '0',
`priority` tinyint(1) NOT NULL DEFAULT '0',
`sensitivity` tinyint(1) NOT NULL DEFAULT '0',
`alarms` varchar(255) DEFAULT NULL,
`attendees` text DEFAULT NULL,
`notifyat` datetime DEFAULT NULL,
PRIMARY KEY(`event_id`),
- INDEX `uid_idx` (`uid`,`calendar_id`),
- INDEX `recurrence_idx` (`recurrence_id`),
+ INDEX `uid_idx` (`uid`),
+ INDEX `recurrence_idx` (`recurrence_id`),
+ INDEX `calendar_notify_idx` (`calendar_id`,`notifyat`),
CONSTRAINT `fk_events_calendar_id` FOREIGN KEY (`calendar_id`)
REFERENCES `calendars`(`calendar_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE `attachments` (
`attachment_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`event_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`filename` varchar(255) NOT NULL DEFAULT '',
`mimetype` varchar(255) NOT NULL DEFAULT '',
`size` int(11) NOT NULL DEFAULT '0',
`data` longtext NOT NULL DEFAULT '',
PRIMARY KEY(`attachment_id`),
CONSTRAINT `fk_attachments_event_id` FOREIGN KEY (`event_id`)
REFERENCES `events`(`event_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE `itipinvitations` (
`token` VARCHAR(64) NOT NULL,
`event_uid` VARCHAR(255) NOT NULL,
`user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`event` TEXT NOT NULL,
`expires` DATETIME DEFAULT NULL,
`cancelled` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY(`token`),
- INDEX `uid_idx` (`event_uid`,`user_id`),
+ INDEX `uid_idx` (`user_id`,`event_uid`),
CONSTRAINT `fk_itipinvitations_user_id` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
diff --git a/plugins/calendar/drivers/database/sql/postgresql.sql b/plugins/calendar/drivers/database/sql/postgresql.sql
index 7e7aafe4..c8f385e1 100644
--- a/plugins/calendar/drivers/database/sql/postgresql.sql
+++ b/plugins/calendar/drivers/database/sql/postgresql.sql
@@ -1,81 +1,103 @@
/**
* RoundCube Calendar
*
* Plugin to add a calendar to RoundCube.
*
* @version @package_version@
* @author Lazlo Westerhof
* @author Albert Lee
* @author Aleksander Machniak <machniak@kolabsys.com>
* @url http://rc-calendar.lazlo.me
* @licence GNU AGPL
* @copyright (c) 2010 Lazlo Westerhof - Netherlands
*
**/
CREATE SEQUENCE calendar_ids
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
CREATE TABLE calendars (
calendar_id integer DEFAULT nextval('calendar_ids'::regclass) NOT NULL,
user_id integer NOT NULL
REFERENCES users (user_id) ON UPDATE CASCADE ON DELETE CASCADE,
name varchar(255) NOT NULL,
color varchar(8) NOT NULL,
showalarms smallint NOT NULL DEFAULT 1,
PRIMARY KEY (calendar_id)
);
+CREATE INDEX calendars_user_id_idx ON calendars (user_id, name);
+
CREATE SEQUENCE event_ids
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
CREATE TABLE events (
event_id integer DEFAULT nextval('event_ids'::regclass) NOT NULL,
calendar_id integer NOT NULL
REFERENCES calendars (calendar_id) ON UPDATE CASCADE ON DELETE CASCADE,
recurence_id integer NOT NULL DEFAULT 0,
uid varchar(255) NOT NULL DEFAULT '',
created timestamp without time zone DEFAULT now() NOT NULL,
changed timestamp without time zone DEFAULT now(),
"start" timestamp without time zone DEFAULT now() NOT NULL,
"end" timestamp without time zone DEFAULT now() NOT NULL,
recurrence varchar(255) DEFAULT NULL,
title character varying(255) NOT NULL,
description text NOT NULL,
location character varying(255) NOT NULL,
categories character varying(255) NOT NULL,
all_day smallint NOT NULL DEFAULT 0,
free_busy smallint NOT NULL DEFAULT 0,
priority smallint NOT NULL DEFAULT 0,
sensitivity smallint NOT NULL DEFAULT 0,
alarms varchar(255) DEFAULT NULL,
attendees text DEFAULT NULL,
notifyat timestamp without time zone DEFAULT NULL
PRIMARY KEY (event_id)
);
+CREATE INDEX events_calendar_id_notifyat_idx ON events (calendar_id, notifyat);
+CREATE INDEX events_uid_idx ON events (uid);
+CREATE INDEX events_recurrence_id_idx ON events (recurrence_id);
+
CREATE SEQUENCE attachment_ids
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
CREATE TABLE attachments (
attachment_id integer DEFAULT nextval('attachment_ids'::regclass) NOT NULL,
event_id integer NOT NULL
REFERENCES events (event_id) ON DELETE CASCADE ON UPDATE CASCADE,
filename varchar(255) NOT NULL DEFAULT '',
mimetype varchar(255) NOT NULL DEFAULT '',
size integer NOT NULL DEFAULT 0,
data text NOT NULL DEFAULT '',
PRIMARY KEY (attachment_id)
);
+
+CREATE INDEX attachments_user_id_idx ON attachments (user_id);
+
+
+CREATE TABLE itipinvitations (
+ token varchar(64) NOT NULL,
+ event_uid varchar(255) NOT NULL,
+ user_id integer NOT NULL
+ REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+ event TEXT NOT NULL,
+ expires timestamp without time zone DEFAULT NULL,
+ cancelled smallint NOT NULL DEFAULT 0,
+ PRIMARY KEY (token)
+);
+
+CREATE INDEX itipinvitations_user_id_event_uid_idx ON itipinvitations (user_id, event_uid);
diff --git a/plugins/calendar/drivers/database/sql/sqlite.sql b/plugins/calendar/drivers/database/sql/sqlite.sql
index 6323d3e3..c89de5e4 100644
--- a/plugins/calendar/drivers/database/sql/sqlite.sql
+++ b/plugins/calendar/drivers/database/sql/sqlite.sql
@@ -1,74 +1,74 @@
/**
* Roundcube Calendar
*
* Plugin to add a calendar to Roundcube.
*
* @version @package_version@
* @author Lazlo Westerhof
* @author Thomas Bruederli
* @author Albert Lee
* @url http://rc-calendar.lazlo.me
* @licence GNU AGPL
* @copyright (c) 2010 Lazlo Westerhof - Netherlands
*
**/
CREATE TABLE calendars (
calendar_id integer NOT NULL PRIMARY KEY,
user_id integer NOT NULL default '0',
name varchar(255) NOT NULL default '',
color varchar(255) NOT NULL default '',
showalarms tinyint(1) NOT NULL default '1',
CONSTRAINT fk_calendars_user_id FOREIGN KEY (user_id)
REFERENCES users(user_id)
);
CREATE TABLE events (
event_id integer NOT NULL PRIMARY KEY,
calendar_id integer NOT NULL default '0',
recurrence_id integer NOT NULL default '0',
uid varchar(255) NOT NULL default '',
created datetime NOT NULL default '1000-01-01 00:00:00',
changed datetime NOT NULL default '1000-01-01 00:00:00',
start datetime NOT NULL default '1000-01-01 00:00:00',
end datetime NOT NULL default '1000-01-01 00:00:00',
recurrence varchar(255) default NULL,
title varchar(255) NOT NULL,
description text NOT NULL,
location varchar(255) NOT NULL default '',
categories varchar(255) NOT NULL default '',
all_day tinyint(1) NOT NULL default '0',
free_busy tinyint(1) NOT NULL default '0',
priority tinyint(1) NOT NULL default '0',
sensitivity tinyint(1) NOT NULL default '0',
alarms varchar(255) default NULL,
attendees text default NULL,
notifyat datetime default NULL,
CONSTRAINT fk_events_calendar_id FOREIGN KEY (calendar_id)
REFERENCES calendars(calendar_id)
);
CREATE TABLE attachments (
attachment_id integer NOT NULL PRIMARY KEY,
event_id integer NOT NULL default '0',
filename varchar(255) NOT NULL default '',
mimetype varchar(255) NOT NULL default '',
size integer NOT NULL default '0',
data text NOT NULL default '',
CONSTRAINT fk_attachment_event_id FOREIGN KEY (event_id)
REFERENCES events(event_id)
);
CREATE TABLE itipinvitations (
token varchar(64) NOT NULL PRIMARY KEY,
event_uid varchar(255) NOT NULL,
user_id integer NOT NULL default '0',
event text NOT NULL,
expires datetime NOT NULL default '1000-01-01 00:00:00',
cancelled tinyint(1) NOT NULL default '0',
CONSTRAINT fk_itipinvitations_user_id FOREIGN KEY (user_id)
REFERENCES users(user_id)
);
-CREATE INDEX ix_itipinvitations_uid ON itipinvitations(event_uid,user_id);
+CREATE INDEX ix_itipinvitations_uid ON itipinvitations(user_id, event_uid);
diff --git a/plugins/calendar/lib/calendar_itip.php b/plugins/calendar/lib/calendar_itip.php
index e8ce963c..d945d19b 100644
--- a/plugins/calendar/lib/calendar_itip.php
+++ b/plugins/calendar/lib/calendar_itip.php
@@ -1,322 +1,324 @@
<?php
/**
* iTIP functions for the Calendar plugin
*
* Class providing functionality to manage iTIP invitations
*
* @version @package_version@
* @author Thomas Bruederli <roundcube@gmail.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
{
private $rc;
private $cal;
private $event;
function __construct($cal)
{
$this->cal = $cal;
$this->rc = $cal->rc;
$this->sender = $this->rc->user->get_identity();
}
/**
* Send an iTip mail message
*
* @param array Event object to send
* @param string iTip method (REQUEST|REPLY|CANCEL)
* @param array Hash array with recipient data (name, email)
* @param string Mail subject
* @param string Mail body text label
* @param object Mail_mime object with message data
* @return boolean True on success, false on failure
*/
public function send_itip_message($event, $method, $recipient, $subject, $bodytext, $message = null)
{
if (!$this->sender['name'])
$this->sender['name'] = $this->sender['email'];
if (!$message)
$message = $this->compose_itip_message($event, $method);
$mailto = rcube_idn_to_ascii($recipient['email']);
$headers = $message->headers();
$headers['To'] = format_email_recipient($mailto, $recipient['name']);
$headers['Subject'] = $this->cal->gettext(array(
'name' => $subject,
'vars' => array('title' => $event['title'], 'name' => $this->sender['name'])
));
// compose a list of all event attendees
$attendees_list = array();
foreach ((array)$event['attendees'] as $attendee) {
$attendees_list[] = ($attendee['name'] && $attendee['email']) ?
$attendee['name'] . ' <' . $attendee['email'] . '>' :
($attendee['name'] ? $attendee['name'] : $attendee['email']);
}
$mailbody = $this->cal->gettext(array(
'name' => $bodytext,
'vars' => array(
'title' => $event['title'],
'date' => $this->cal->event_date_text($event, true),
'attendees' => join(', ', $attendees_list),
'sender' => $this->sender['name'],
'organizer' => $this->sender['name'],
)
));
// append links for direct invitation replies
if ($method == 'REQUEST' && ($token = $this->store_invitation($event, $recipient['email']))) {
$mailbody .= "\n\n" . $this->cal->gettext(array(
'name' => 'invitationattendlinks',
'vars' => array('url' => $this->cal->get_url(array('action' => 'attend', 't' => $token))),
));
}
else if ($method == 'CANCEL') {
$this->cancel_itip_invitation($event);
}
$message->headers($headers, true);
$message->setTXTBody(rcube_message::format_flowed($mailbody, 79));
// finally send the message
return rcmail_deliver_message($message, $headers['X-Sender'], $mailto, $smtp_error);
}
/**
* Helper function to build a Mail_mime object to send an iTip message
*
* @param array Event object to send
* @param string iTip method (REQUEST|REPLY|CANCEL)
* @return object Mail_mime object with message data
*/
public function compose_itip_message($event, $method)
{
$from = rcube_idn_to_ascii($this->sender['email']);
$sender = format_email_recipient($from, $this->sender['name']);
// compose multipart message using PEAR:Mail_Mime
$message = new Mail_mime("\r\n");
$message->setParam('text_encoding', 'quoted-printable');
$message->setParam('head_encoding', 'quoted-printable');
$message->setParam('head_charset', RCMAIL_CHARSET);
$message->setParam('text_charset', RCMAIL_CHARSET . ";\r\n format=flowed");
// compose common headers array
$headers = array(
'From' => $sender,
'Date' => rcmail_user_date(),
'Message-ID' => rcmail_gen_message_id(),
'X-Sender' => $from,
);
if ($agent = $this->rc->config->get('useragent'))
$headers['User-Agent'] = $agent;
$message->headers($headers);
// attach ics file for this event
$ical = $this->cal->get_ical();
$ics = $ical->export(array($event), $method);
$message->addAttachment($ics, 'text/calendar', 'event.ics', false, '8bit', 'attachment', RCMAIL_CHARSET . "; method=" . $method);
return $message;
}
/**
* Find invitation record by token
*
* @param string 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 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 record as fetched with calendar_itip::get_invitation()
* @param string Attendee email address
* @param string New attendee status
*/
public function update_invitation($invitation, $email, $newstatus)
{
if (is_string($invitation))
$invitation = $this->get_invitation($invitation);
if ($invitation['token'] && $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'] = time();
// send iTIP REPLY message to organizer
if ($organizer) {
$status = strtolower($newstatus);
if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
$this->rc->output->command('display_message', $this->cal->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
else
$this->rc->output->command('display_message', $this->cal->gettext('itipresponseerror'), 'error');
}
// update record in DB
$query = $this->rc->db->query(
"UPDATE 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 Hash array with event properties
* @param string Attendee email address
* @return string Invitation token
*/
public function store_invitation($event, $attendee)
{
static $stored = array();
if (!$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 ($stored[$base])
return $token;
-
+
+ // @TODO: REPLACE works only with MySQL
+
$query = $this->rc->db->query(
"REPLACE INTO 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'] + 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 Hash array with event properties
*/
public function cancel_itip_invitation($event)
{
// flag invitation record as cancelled
$this->rc->db->query(
"UPDATE 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 hash array
* @param string Attendee email address
*/
public function generate_token($event, $attendee)
{
$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 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 array('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;
$ev['description'] = abbreviate_string($ev['description'], 100);
unset($ev['attachments']);
return serialize($ev);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 3:46 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
197164
Default Alt Text
(20 KB)

Event Timeline