DAViCal
caldav-POST.php
1 <?php
11 dbg_error_log("POST", "method handler");
12 
13 require_once("XMLDocument.php");
14 include_once('caldav-PUT-functions.php');
15 include_once('freebusy-functions.php');
16 include_once('iSchedule.php');
17 
18 if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || isset($c->dbg['post'])) ) {
19  $fh = fopen('/var/log/davical/POST.debug','w');
20  if ( $fh ) {
21  fwrite($fh,$request->raw_post);
22  fclose($fh);
23  }
24 }
25 
26 
27 function handle_freebusy_request( $ic ) {
28  global $c, $session, $request, $ical;
29 
30  $request->NeedPrivilege('urn:ietf:params:xml:ns:caldav:schedule-send-freebusy');
31  $reply = new XMLDocument( array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C" ) );
32  $responses = array();
33 
34  $fbq_start = $ic->GetPValue('DTSTART');
35  $fbq_end = $ic->GetPValue('DTEND');
36  if ( ! ( isset($fbq_start) || isset($fbq_end) ) ) {
37  $request->DoResponse( 400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND' );
38  }
39 
40  $range_start = new RepeatRuleDateTime($fbq_start);
41  $range_end = new RepeatRuleDateTime($fbq_end);
42 
43  $attendees = $ic->GetProperties('ATTENDEE');
44  if ( preg_match( '# iCal/\d#', $_SERVER['HTTP_USER_AGENT']) ) {
45  dbg_error_log( "POST", "Non-compliant iCal request. Using X-WR-ATTENDEE property" );
46  $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
47  foreach( $wr_attendees AS $k => $v ) {
48  $attendees[] = $v;
49  }
50  }
51  dbg_error_log( "POST", "Responding with free/busy for %d attendees", count($attendees) );
52 
53  if (isset($c->enable_attendee_group_resolution) && $c->enable_attendee_group_resolution) {
54  $new_attendees = array();
55  foreach( $attendees AS $attendee ) {
56  $v = $attendee->Value();
57  unset($localname);
58  if ($v == "invalid:nomail") {
59  $localname = $attendee->GetParameterValue("CN");
60  } else if ((preg_match('/^@/', $v) == 1) || (preg_match('/mailto:@/',$v) == 1)) {
61  $localname = preg_replace('/^.*@/', '', $v);
62  } else if (preg_match('/@/', $v) != 1) {
63  $localname = $v;
64  }
65  if ($localname) {
66  dbg_error_log( 'POST', 'try to resolve local attendee %s', $localname);
67  $qry = new AwlQuery('SELECT fullname, email FROM usr WHERE user_no = (SELECT user_no FROM principal WHERE type_id = 1 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username)))) UNION SELECT fullname, email FROM usr WHERE user_no IN (SELECT user_no FROM principal WHERE principal_id IN (SELECT member_id FROM group_member WHERE group_id = (SELECT principal_id FROM principal WHERE type_id = 3 AND user_no = (SELECT user_no FROM usr WHERE lower(username) = (text(:username))))))', array(':username' => strtolower($localname)));
68  if ( $qry->Exec('POST',__LINE__,__FILE__) && $qry->rows() >= 1 ) {
69  dbg_error_log( 'POST', 'resolved local name %s to %d individual attendees', $localname, $qry->rows());
70  while ($row = $qry->Fetch()) {
71  dbg_error_log( 'POST', 'adding individual attendee %s <%s>', $row->fullname, $row->email);
72  $new_attendees[] = new vProperty("ATTENDEE:mailto:" . $row->email);
73  }
74  }
75  } else {
76  $new_attendees[] = clone($attendee);
77  }
78  }
79  $attendees = $new_attendees;
80  }
81 
82  foreach( $attendees AS $k => $attendee ) {
83  $attendee_email = preg_replace( '/^mailto:/', '', $attendee->Value() );
84  dbg_error_log( "POST", "Calculating free/busy for %s", $attendee_email );
85 
87  $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email );
88  $qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
89  if ( !$qry->Exec('POST',__LINE__,__FILE__) ) $request->DoResponse( 501, 'Database error');
90  if ( $qry->rows() > 1 ) {
91  // Unlikely, but if we get more than one result we'll do an exact match instead.
92  if ( !$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params ) )
93  $request->DoResponse( 501, 'Database error');
94  if ( $qry->rows() == 0 ) {
96  $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params );
97  }
98  }
99 
100  $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
101  $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()) );
102 
103  if ( $qry->rows() == 0 ) {
104  $remote = new iSchedule ();
105  $answer = $remote->sendRequest ( $attendee->Value(), 'VFREEBUSY/REQUEST', $ical->Render() );
106  if ( $answer === false ) {
107  $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
108  $reply->CalDAVElement($response, "calendar-data" );
109  $responses[] = $response;
110  continue;
111  }
112 
113  foreach ( $answer as $a )
114  {
115  if ( $a === false ) {
116  $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User" );
117  $reply->CalDAVElement($response, "calendar-data" );
118  }
119  elseif ( substr( $a, 0, 1 ) >= 1 ) {
120  $reply->CalDAVElement($response, "request-status", $a );
121  $reply->CalDAVElement($response, "calendar-data" );
122  }
123  else {
124  $reply->CalDAVElement($response, "request-status", "2.0;Success" );
125  $reply->CalDAVElement($response, "calendar-data", $a );
126  }
127  $responses[] = $response;
128  }
129  continue;
130  }
131  if ( ! $attendee_usr = $qry->Fetch() ) $request->DoResponse( 501, 'Database error');
132  if ( (privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0 ) {
133  $reply->CalDAVElement($response, "request-status", "3.8;No authority" );
134  $reply->CalDAVElement($response, "calendar-data" );
135  $responses[] = $response;
136  continue;
137  }
138  $attendee_path_match = '^/'.$attendee_usr->username.'/';
139