3
|
1 <?php
|
|
2
|
|
3 require_once realpath(__DIR__ . '/../../libcalendaring/lib/libcalendaring_itip.php');
|
|
4
|
|
5 /**
|
|
6 * iTIP functions for the Calendar plugin
|
|
7 *
|
|
8 * Class providing functionality to manage iTIP invitations
|
|
9 *
|
|
10 * @version @package_version@
|
|
11 * @author Thomas Bruederli <bruederli@kolabsys.com>
|
|
12 * @package @package_name@
|
|
13 *
|
|
14 * Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
|
|
15 *
|
|
16 * This program is free software: you can redistribute it and/or modify
|
|
17 * it under the terms of the GNU Affero General Public License as
|
|
18 * published by the Free Software Foundation, either version 3 of the
|
|
19 * License, or (at your option) any later version.
|
|
20 *
|
|
21 * This program is distributed in the hope that it will be useful,
|
|
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
24 * GNU Affero General Public License for more details.
|
|
25 *
|
|
26 * You should have received a copy of the GNU Affero General Public License
|
|
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
28 */
|
|
29 class calendar_itip extends libcalendaring_itip
|
|
30 {
|
|
31 /**
|
|
32 * Constructor to set text domain to calendar
|
|
33 */
|
|
34 function __construct($plugin, $domain = 'calendar')
|
|
35 {
|
|
36 parent::__construct($plugin, $domain);
|
|
37
|
|
38 $this->db_itipinvitations = $this->rc->db->table_name('itipinvitations', true);
|
|
39 }
|
|
40
|
|
41 /**
|
|
42 * Handler for calendar/itip-status requests
|
|
43 */
|
|
44 public function get_itip_status($event, $existing = null)
|
|
45 {
|
|
46 $status = parent::get_itip_status($event, $existing);
|
|
47
|
|
48 // don't ask for deleting events when declining
|
|
49 if ($this->rc->config->get('kolab_invitation_calendars'))
|
|
50 $status['saved'] = false;
|
|
51
|
|
52 return $status;
|
|
53 }
|
|
54
|
|
55 /**
|
|
56 * Find invitation record by token
|
|
57 *
|
|
58 * @param string Invitation token
|
|
59 * @return mixed Invitation record as hash array or False if not found
|
|
60 */
|
|
61 public function get_invitation($token)
|
|
62 {
|
|
63 if ($parts = $this->decode_token($token)) {
|
|
64 $result = $this->rc->db->query("SELECT * FROM $this->db_itipinvitations WHERE `token` = ?", $parts['base']);
|
|
65 if ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
|
|
66 $rec['event'] = unserialize($rec['event']);
|
|
67 $rec['attendee'] = $parts['attendee'];
|
|
68 return $rec;
|
|
69 }
|
|
70 }
|
|
71
|
|
72 return false;
|
|
73 }
|
|
74
|
|
75 /**
|
|
76 * Update the attendee status of the given invitation record
|
|
77 *
|
|
78 * @param array Invitation record as fetched with calendar_itip::get_invitation()
|
|
79 * @param string Attendee email address
|
|
80 * @param string New attendee status
|
|
81 */
|
|
82 public function update_invitation($invitation, $email, $newstatus)
|
|
83 {
|
|
84 if (is_string($invitation))
|
|
85 $invitation = $this->get_invitation($invitation);
|
|
86
|
|
87 if ($invitation['token'] && $invitation['event']) {
|
|
88 // update attendee record in event data
|
|
89 foreach ($invitation['event']['attendees'] as $i => $attendee) {
|
|
90 if ($attendee['role'] == 'ORGANIZER') {
|
|
91 $organizer = $attendee;
|
|
92 }
|
|
93 else if ($attendee['email'] == $email) {
|
|
94 // nothing to be done here
|
|
95 if ($attendee['status'] == $newstatus)
|
|
96 return true;
|
|
97
|
|
98 $invitation['event']['attendees'][$i]['status'] = $newstatus;
|
|
99 $this->sender = $attendee;
|
|
100 }
|
|
101 }
|
|
102 $invitation['event']['changed'] = new DateTime();
|
|
103
|
|
104 // send iTIP REPLY message to organizer
|
|
105 if ($organizer) {
|
|
106 $status = strtolower($newstatus);
|
|
107 if ($this->send_itip_message($invitation['event'], 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
|
|
108 $this->rc->output->command('display_message', $this->plugin->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
|
|
109 else
|
|
110 $this->rc->output->command('display_message', $this->plugin->gettext('itipresponseerror'), 'error');
|
|
111 }
|
|
112
|
|
113 // update record in DB
|
|
114 $query = $this->rc->db->query(
|
|
115 "UPDATE $this->db_itipinvitations
|
|
116 SET `event` = ?
|
|
117 WHERE `token` = ?",
|
|
118 self::serialize_event($invitation['event']),
|
|
119 $invitation['token']
|
|
120 );
|
|
121
|
|
122 if ($this->rc->db->affected_rows($query))
|
|
123 return true;
|
|
124 }
|
|
125
|
|
126 return false;
|
|
127 }
|
|
128
|
|
129
|
|
130 /**
|
|
131 * Create iTIP invitation token for later replies via URL
|
|
132 *
|
|
133 * @param array Hash array with event properties
|
|
134 * @param string Attendee email address
|
|
135 * @return string Invitation token
|
|
136 */
|
|
137 public function store_invitation($event, $attendee)
|
|
138 {
|
|
139 static $stored = array();
|
|
140
|
|
141 if (!$event['uid'] || !$attendee)
|
|
142 return false;
|
|
143
|
|
144 // generate token for this invitation
|
|
145 $token = $this->generate_token($event, $attendee);
|
|
146 $base = substr($token, 0, 40);
|
|
147
|
|
148 // already stored this
|
|
149 if ($stored[$base])
|
|
150 return $token;
|
|
151
|
|
152 // delete old entry
|
|
153 $this->rc->db->query("DELETE FROM $this->db_itipinvitations WHERE `token` = ?", $base);
|
|
154
|
|
155 $event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
|
156
|
|
157 $query = $this->rc->db->query(
|
|
158 "INSERT INTO $this->db_itipinvitations
|
|
159 (`token`, `event_uid`, `user_id`, `event`, `expires`)
|
|
160 VALUES(?, ?, ?, ?, ?)",
|
|
161 $base,
|
|
162 $event_uid,
|
|
163 $this->rc->user->ID,
|
|
164 self::serialize_event($event),
|
|
165 date('Y-m-d H:i:s', $event['end']->format('U') + 86400 * 2)
|
|
166 );
|
|
167
|
|
168 if ($this->rc->db->affected_rows($query)) {
|
|
169 $stored[$base] = 1;
|
|
170 return $token;
|
|
171 }
|
|
172
|
|
173 return false;
|
|
174 }
|
|
175
|
|
176 /**
|
|
177 * Mark invitations for the given event as cancelled
|
|
178 *
|
|
179 * @param array Hash array with event properties
|
|
180 */
|
|
181 public function cancel_itip_invitation($event)
|
|
182 {
|
|
183 $event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
|
184
|
|
185 // flag invitation record as cancelled
|
|
186 $this->rc->db->query(
|
|
187 "UPDATE $this->db_itipinvitations
|
|
188 SET `cancelled` = 1
|
|
189 WHERE `event_uid` = ? AND `user_id` = ?",
|
|
190 $event_uid,
|
|
191 $this->rc->user->ID
|
|
192 );
|
|
193 }
|
|
194
|
|
195 /**
|
|
196 * Generate an invitation request token for the given event and attendee
|
|
197 *
|
|
198 * @param array Event hash array
|
|
199 * @param string Attendee email address
|
|
200 */
|
|
201 public function generate_token($event, $attendee)
|
|
202 {
|
|
203 $event_uid = $event['uid'] . ($event['_instance'] ? '-' . $event['_instance'] : '');
|
|
204 $base = sha1($event_uid . ';' . $this->rc->user->ID);
|
|
205 $mail = base64_encode($attendee);
|
|
206 $hash = substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6);
|
|
207
|
|
208 return "$base.$mail.$hash";
|
|
209 }
|
|
210
|
|
211 /**
|
|
212 * Decode the given iTIP request token and return its parts
|
|
213 *
|
|
214 * @param string Request token to decode
|
|
215 * @return mixed Hash array with parts or False if invalid
|
|
216 */
|
|
217 public function decode_token($token)
|
|
218 {
|
|
219 list($base, $mail, $hash) = explode('.', $token);
|
|
220
|
|
221 // validate and return parts
|
|
222 if ($mail && $hash && $hash == substr(md5($base . $mail . $this->rc->config->get('des_key')), 0, 6)) {
|
|
223 return array('base' => $base, 'attendee' => base64_decode($mail));
|
|
224 }
|
|
225
|
|
226 return false;
|
|
227 }
|
|
228
|
|
229 /**
|
|
230 * Helper method to serialize the given event for storing in invitations table
|
|
231 */
|
|
232 private static function serialize_event($event)
|
|
233 {
|
|
234 $ev = $event;
|
|
235 $ev['description'] = abbreviate_string($ev['description'], 100);
|
|
236 unset($ev['attachments']);
|
|
237 return serialize($ev);
|
|
238 }
|
|
239
|
|
240 }
|