Overview

Packages

  • awl
    • caldav-client-v2
    • RRule
  • davical
    • authentication
      • drivers
    • caldav
    • DAViCalSession
    • DAVTicket
    • external-bind
    • feed
    • HTTPAuthSession
    • iSchedule
    • iSchedule-POST
    • logging
    • metrics
    • Principal
    • propfind
    • PublicSession
    • Request
    • Resource
    • tzservice
  • None
  • PHP

Classes

  • FakeSession

Functions

  • ischedule_cancel
  • ischedule_freebusy_request
  • ischedule_request
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3: * iScheduling POST handle remote iSchedule requests
  4: *
  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
 10: */
 11: 
 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');
 17: 
 18: 
 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;
 24:     }
 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;
 31: 
 32:     $this->logged_in = true;
 33: 
 34:   }
 35: 
 36:   function AllowedTo($do_something) {
 37:     return true;
 38:   }
 39: }
 40: 
 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;
 50:   }
 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'];
 58:   }
 59:   else {
 60:     $rcpt = explode ( ',', $_SERVER['HTTP_RECIPIENT'] );
 61:     foreach ( $rcpt as $k => $v ) {
 62:       $recipients[$k] = preg_replace( '/^mailto:/i', '', trim ( $v ) );
 63:     }
 64:   }
 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') );
 67:   }
 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;
 73:     }
 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;
 80:     }
 81:     array_push ( $attendees_ok, $schedule_target );
 82:     // TODO: add originator addressbook and existing event lookup as whitelist
 83:   }
 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);
 92:   }
 93:   if ( isset ( $content_parts['method'] ) )
 94:     $method = $content_parts['method']; // override method from icalendar
 95:   switch ( $method )
 96:   {
 97:     case 'REQUEST':
 98:       if ( $content_parts['component'] == 'VFREEBUSY' ) {
 99:         ischedule_freebusy_request ( $ical, $attendees_ok, $attendees_fail );
100:       }
101:       if ( $content_parts['component'] == 'VEVENT' ) {
102:         ischedule_request ( $ical, $attendees_ok, $attendees_fail );
103:         // scheduling event request
104:       }
105:       if ( $content_parts['component'] == 'VTODO' ) {
106:         ischedule_request ( $ical, $attendees_ok, $attendees_fail );
107:         // scheduling todo request
108:       }
109:       if ( $content_parts['component'] == 'VJOURNAL' ) {
110:         // scheduling journal request, not sure how to handle this or if it will ever by used
111:       }
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') );
133:   }
134: }
135: else {
136:   dbg_error_log('ischedule','invalid request' );
137:   $request->DoResponse( 400, translate('invalid request') );
138: }
139: 
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' );
151:   }
152: 
153:   $range_start = new RepeatRuleDateTime($fbq_start);
154:   $range_end   = new RepeatRuleDateTime($fbq_end);
155: 
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 );
159: 
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 );
165:     }
166: 
167:     $vcal = new vCalendar( array('METHOD' => 'REPLY') );
168:     $vcal->AddComponent( $fb );
169: 
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' );
174: 
175:     $responses[] = $response;
176:   }
177: 
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;
183:   }
184:   $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
185:   $request->XMLResponse( 200, $response );
186: }
187: 
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];
195: 
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
208:       }
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.
213:         }
214:         else if ( $attendee_inbox->WriteCalendarMember($ic, false) !== false ) {
215:           $response = '2.0;delivered'; // Scheduling invitation delivered successfully
216:         }
217:       }
218:       $session = $oldSession;
219:     }
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;
224:   }
225: 
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;
231:   }
232: 
233:   $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
234:   $request->XMLResponse( 200, $response );
235: }
236: 
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];
243: 
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
255:       }
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.
260:         }
261:         else if ( $attendee_inbox->WriteCalendarMember($ic, false) !== false ) {
262:           $response = '2.0;delivered'; // Scheduling invitation delivered successfully
263:         }
264:       }
265:     }
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;
270:   }
271: 
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;
277:   }
278: 
279:   $response = $reply->NewXMLElement( "schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:ischedule' );
280:   $request->XMLResponse( 200, $response );
281: }
282: 
283: 
DAViCal API documentation generated by ApiGen 2.8.0