document closed Debian bug
[davical.git] / inc / iSchedule-POST.php
blobf78c08a913f06470d0f46adda892959d03f6e4f7
1 <?php
2 /**
3 * iScheduling POST handle remote iSchedule requests
5 * @package davical
6 * @subpackage iSchedule-POST
7 * @author Rob Ostensen <rob@boxacle.net>
8 * @copyright Rob Ostensen
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
12 require_once('iSchedule.php');
13 require_once('vComponent.php');
14 require_once('vCalendar.php');
15 require_once('WritableCollection.php');
16 include_once('freebusy-functions.php');
19 class FakeSession {
20 function __construct($principal) {
21 // Assign each field in the selected record to the object
22 foreach( $principal AS $k => $v ) {
23 $this->{$k} = $v;
25 $this->username = $principal->username();
26 $this->user_no = $principal->user_no();
27 $this->principal_id = $principal->principal_id();
28 $this->email = $principal->email();
29 $this->dav_name = $principal->dav_name();
30 $this->principal = $principal;
32 $this->logged_in = true;
36 function AllowedTo($do_something) {
37 return true;
41 $d = new iSchedule ();
42 if ( $d->validateRequest ( ) ) {
43 $ical = new vCalendar( $request->raw_post );
44 $attendee = Array ();
45 $addresses = Array ();
46 $attendees = $ical->GetAttendees();
47 foreach( $attendees AS $v ) {
48 $email = preg_replace( '/^mailto:/i', '', $v->Value() );
49 $addresses[] = $email;
51 $organizer = $ical->GetOrganizer();
52 $addresses[] = preg_replace( '/^mailto:/i', '', $organizer->Value() );
53 $recipients = Array ();
54 $attendees_ok = Array ();
55 $attendees_fail = Array ();
56 if ( strpos ( $_SERVER['HTTP_RECIPIENT'], ',' ) === false ) { // single recipient
57 $recipients[] = $_SERVER['HTTP_RECIPIENT'];
59 else {
60 $rcpt = explode ( ',', $_SERVER['HTTP_RECIPIENT'] );
61 foreach ( $rcpt as $k => $v ) {
62 $recipients[$k] = preg_replace( '/^mailto:/i', '', trim ( $v ) );
65 if ( ! in_array ( preg_replace( '/^mailto:/i', '', $_SERVER['HTTP_ORIGINATOR'] ), $addresses ) ) { // should this be case sensitive?
66 $request->DoResponse( 412, translate('sender must be organizer or attendee of event') );
68 foreach ( $recipients as $v ) {
69 if ( ! in_array ( preg_replace( '/^mailto:/i', '', $v ), $addresses ) ) { // should this be case sensitive?
70 dbg_error_log('ischedule','recipient missing from event ' . $v );
71 $reply->XMLResponse( 403, translate('recipient must be organizer or attendee of event') . $v );
72 continue;
74 $email = preg_replace( '/^mailto:/', '', $v );
75 dbg_error_log('ischedule','recipient ' . $v );
76 $schedule_target = new Principal('email',$email);
77 if ( $schedule_target == false ){
78 array_push ( $attendees_fail, $schedule_target );
79 continue;
81 array_push ( $attendees_ok, $schedule_target );
82 // TODO: add originator addressbook and existing event lookup as whitelist
84 $method = $ical->GetPValue('METHOD');
85 $content_type = explode ( ';', $_SERVER['CONTENT_TYPE'] );
86 if ( $content_type[0] != 'text/calendar' )
87 $reply->XMLResponse( 406, 'content must be text/calendar' );
88 $content_parts = Array ();
89 foreach ( $content_type as $v ) {
90 list ( $a, $b ) = explode ( '=', trim ( $v ), 2 );
91 $content_parts[strtolower($a)] = strtoupper($b);
93 if ( isset ( $content_parts['method'] ) )
94 $method = $content_parts['method']; // override method from icalendar
95 switch ( $method )
97 case 'REQUEST':
98 if ( $content_parts['component'] == 'VFREEBUSY' ) {
99 ischedule_freebusy_request ( $ical, $attendees_ok, $attendees_fail );
101 if ( $content_parts['component'] == 'VEVENT' ) {
102 ischedule_request ( $ical, $attendees_ok, $attendees_fail );
103 // scheduling event request
105 if ( $content_parts['component'] == 'VTODO' ) {
106 ischedule_request ( $ical, $attendees_ok, $attendees_fail );
107 // scheduling todo request
109 if ( $content_parts['component'] == 'VJOURNAL' ) {
110 // scheduling journal request, not sure how to handle this or if it will ever by used
112 break;
113 case 'REPLY':
114 ischedule_request ( $ical, $attendees_ok, $attendees_fail );
115 break;
116 case 'ADD':
117 ischedule_request ( $ical, $attendees_ok, $attendees_fail );
118 break;
119 case 'CANCEL':
120 ischedule_request ( $ical, $attendees_ok, $attendees_fail );
121 break;
122 case 'PUBLISH':
123 break;
124 case 'REFRESH':
125 break;
126 case 'COUNTER':
127 break;
128 case 'DECLINECOUNTER':
129 break;
130 default:
131 dbg_error_log('ischedule','invalid request' );
132 $request->DoResponse( 400, translate('invalid request') );
135 else {
136 dbg_error_log('ischedule','invalid request' );
137 $request->DoResponse( 400, translate('invalid request') );
140 function ischedule_freebusy_request( $ic, $attendees, $attendees_fail) {
141 global $c, $session, $request;
142 $reply = new XMLDocument( array( "urn:ietf:params:xml:ns:ischedule" => "I" ) );
143 $icalAttendees = $ic->GetAttendees();
144 $responses = array();
145 $ical = $ic->GetComponents('VFREEBUSY');
146 $ical = $ical[0];
147 $fbq_start = $ical->GetPValue('DTSTART');
148 $fbq_end = $ical->GetPValue('DTEND');
149 if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
150 $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
153 $range_start = new RepeatRuleDateTime($fbq_start);
154 $range_end = new RepeatRuleDateTime($fbq_end);
156 foreach( $attendees AS $k => $attendee ) {
157 $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
158 $fb = get_freebusy( '^'.$attendee->dav_name, $range_start, $range_end );
160 $fb->AddProperty( 'UID', $ical->GetPValue('UID') );
161 $fb->SetProperties( $ical->GetProperties('ORGANIZER'), 'ORGANIZER');
162 foreach ( $ical->GetProperties('ATTENDEE') as $at ) {
163 if ( $at->Value() == 'mailto:' . $attendee->email )
164 $fb->AddProperty( $at );
167 $vcal = new vCalendar( array('METHOD' => 'REPLY') );
168 $vcal->AddComponent( $fb );
170 $response = $reply->NewXMLElement( "response", false, false, 'urn:ietf:params:xml:ns:ischedule' );
171 $response->NewElement( "recipient", 'mailto:'.$attendee->email, false, 'urn:ietf:params:xml:ns:ischedule' );
172 $response->NewElement( "request-status", "2.0;Success", false, 'urn:ietf:params:xml:ns:ischedule' );
173 $response->NewElement( "calendar-data", $vcal->Render(), false, 'urn:ietf:params:xml:ns:ischedule' );
175 $responses[] = $response;
178 foreach ( $attendees_fail AS $k => $attendee ) {
179 $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
180 $XMLresponse->NewElement( "recipient", $reply->href('mailto:'.$attendee));
181 $XMLresponse->NewElement( "request-status",'5.3;cannot schedule this user, unknown or access denied');
182 $responses[] = $XMLresponse;
184 $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
185 $request->XMLResponse( 200, $response );
188 function ischedule_request( $ic, $attendees, $attendees_fail ) {
189 global $c, $session, $request;
190 $oldSession = $session;
191 $reply = new XMLDocument( array( "urn:ietf:params:xml:ns:ischedule" => "I" ) );
192 $responses = array();
193 $ical = $ic->GetComponents('VEVENT');
194 $ical = $ical[0];
196 foreach ( $attendees AS $k => $attendee ) {
197 $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
198 dbg_error_log('ischedule','scheduling event for ' .$attendee->email);
199 $schedule_target = new Principal('email',$attendee->email);
200 $response = '3.7'; // Attendee was not found on server.
201 if ( $schedule_target->Exists() ) {
202 $session = new FakeSession($schedule_target);
203 $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar')));
204 if ( !$attendee_calendar->Exists() ) {
205 dbg_error_log('ERROR','Default calendar at "%s" does not exist for user "%s"',
206 $attendee_calendar->dav_name(), $schedule_target->username());
207 $response = '5.3;cannot schedule this user, unknown or access denied'; // No scheduling support for user
209 else {
210 $attendee_inbox = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-inbox')));
211 if ( ! $attendee_inbox->HavePrivilegeTo('schedule-deliver-invite') ) {
212 $response = '3.8;denied'; // No authority to deliver invitations to user.
214 else if ( $attendee_inbox->WriteCalendarMember($ic, false) !== false ) {
215 $response = '2.0;delivered'; // Scheduling invitation delivered successfully
218 $session = $oldSession;
220 dbg_error_log( 'ischedule', 'Status for attendee <%s> set to "%s"', $attendee->email, $response );
221 $XMLresponse->NewElement("recipient", 'mailto:'.$attendee->email, false, 'urn:ietf:params:xml:ns:ischedule' );
222 $XMLresponse->NewElement("request-status", $response, false, 'urn:ietf:params:xml:ns:ischedule' );
223 $responses[] = $XMLresponse;
226 foreach ( $attendees_fail AS $k => $attendee ) {
227 $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
228 $XMLresponse->NewElement("recipient", 'mailto:'.$attendee->email, false, 'urn:ietf:params:xml:ns:ischedule' );
229 $XMLresponse->NewElement("request-status", '5.3;cannot schedule this user, unknown or access denied', false, 'urn:ietf:params:xml:ns:ischedule' );
230 $responses[] = $XMLresponse;
233 $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
234 $request->XMLResponse( 200, $response );
237 function ischedule_cancel( $ic, $attendees, $attendees_fail ) {
238 global $c, $session, $request;
239 $reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C", "urn:ietf:params:xml:ns:ischedule" => "I" ) );
240 $responses = array();
241 $ical = $ic->GetComponents('VEVENT');
242 $ical = $ical[0];
244 foreach ( $attendees AS $k => $attendee ) {
245 $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
246 dbg_error_log('ischedule','scheduling event for ' .$attendee->email);
247 $schedule_target = new Principal('email',$attendee->email);
248 $response = '3.7'; // Attendee was not found on server.
249 if ( $schedule_target->Exists() ) {
250 $attendee_calendar = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-default-calendar')));
251 if ( !$attendee_calendar->Exists() ) {
252 dbg_error_log('ERROR','Default calendar at "%s" does not exist for user "%s"',
253 $attendee_calendar->dav_name(), $schedule_target->username());
254 $response = '5.3;cannot schedule this user, unknown or access denied'; // No scheduling support for user
256 else {
257 $attendee_inbox = new WritableCollection(array('path' => $schedule_target->internal_url('schedule-inbox')));
258 if ( ! $attendee_inbox->HavePrivilegeTo('schedule-deliver-invite') ) {
259 $response = '3.8;denied'; // No authority to deliver invitations to user.
261 else if ( $attendee_inbox->WriteCalendarMember($ic, false) !== false ) {
262 $response = '2.0;delivered'; // Scheduling invitation delivered successfully
266 dbg_error_log( 'PUT', 'Status for attendee <%s> set to "%s"', $attendee->email, $response );
267 $XMLresponse->NewElement("recipient", $reply->href('mailto:'.$attendee->email), false, 'urn:ietf:params:xml:ns:ischedule' );
268 $XMLresponse->NewElement("request-status", $response, false, 'urn:ietf:params:xml:ns:ischedule' );
269 $responses[] = $XMLresponse;
272 foreach ( $attendees_fail AS $k => $attendee ) {
273 $XMLresponse = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:ischedule');
274 $XMLresponse->NewElement("recipient", $reply->href('mailto:'.$attendee->email), false, 'urn:ietf:params:xml:ns:ischedule' );
275 $XMLresponse->NewElement("request-status", '5.3;cannot schedule this user, unknown or access denied', false, 'urn:ietf:params:xml:ns:ischedule' );
276 $responses[] = $XMLresponse;
279 $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
280 $request->XMLResponse( 200, $response );