DAViCal
caldav-REPORT-sync-collection.php
1 <?php
12 $responses = array();
13 
17 $sync_level = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-level');
18 if ( empty($sync_level) ) {
19  $sync_level = $request->depth;
20 }
21 else {
22  $sync_level = $sync_level[0]->GetContent();
23  if ( $sync_level == 'infinity' )
24  $sync_level = DEPTH_INFINITY;
25  else
26  $sync_level = 1;
27 }
28 
29 if ( $sync_level == DEPTH_INFINITY ) {
30  $request->PreconditionFailed(403, 'DAV::sync-traversal-supported','This server does not support sync-traversal');
31 }
32 
33 $sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
34 if ( isset($sync_tokens[0]) ) $sync_token = $sync_tokens[0]->GetContent();
35 if ( !isset($sync_token) ) $sync_token = 0;
36 $sync_token = intval(str_replace('data:,', '', strtolower($sync_token) ));
37 dbg_error_log( 'sync', " sync-token: %s", $sync_token );
38 
39 $proplist = array();
40 $props = $xmltree->GetPath('/DAV::sync-collection/DAV::prop/*');
41 if ( !empty($props) ) {
42  foreach( $props AS $k => $v ) {
43  $proplist[] = $v->GetNSTag();
44  }
45 }
46 
47 function display_status( $status_code ) {
48  return sprintf( 'HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code) );
49 }
50 
51 $collection = new DAVResource( $request->path );
52 if ( !$collection->Exists() ) {
53  $request->DoResponse( 404 );
54 }
55 $bound_from = $collection->bound_from();
56 $collection_path = $collection->dav_name();
57 $request_via_binding = ($bound_from != $collection_path);
58 
59 $params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
60 $sql = "SELECT new_sync_token( :sync_token, :collection_id)";
61 $qry = new AwlQuery($sql, $params);
62 if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
63  $request->DoResponse( 500, translate("Database error") );
64 }
65 $row = $qry->Fetch();
66 
67 if ( !isset($row->new_sync_token) ) {
69  $sync_token = 0;
70  $params[':sync_token'] = $sync_token;
71  if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
72  $request->DoResponse( 500, translate("Database error") );
73  }
74  $row = $qry->Fetch();
75 }
76 $new_token = $row->new_sync_token;
77 
78 if ( $sync_token == $new_token ) {
79  // No change, so we just re-send the old token.
80  $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
81 }
82 else {
83  $hide_older = '';
84  if ( isset($c->hide_older_than) && intval($c->hide_older_than) > 0 )
85  $hide_older = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL OR calendar_item.rrule IS NOT NULL THEN true ELSE calendar_item.dtstart > (now() - interval '".intval($c->hide_older_than)." days') END)";
86 
87  $hide_todo = '';
88  if ( isset($c->hide_TODO) && ($c->hide_TODO === true || (is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT']))) && ! $collection->HavePrivilegeTo('all') )
89  $hide_todo = " AND (caldav_data.caldav_type NOT IN ('VTODO') OR caldav_data.caldav_type IS NULL) ";
90 
91  if ( $sync_token == 0 ) {
92  $sql = <<<EOSQL
93  SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, 201 AS sync_status FROM collection
94  LEFT JOIN caldav_data USING (collection_id)
95  LEFT JOIN calendar_item USING (dav_id)
96  LEFT JOIN addressbook_resource USING (dav_id)
97  WHERE collection.collection_id = :collection_id $hide_older $hide_todo
98  ORDER BY collection.collection_id, caldav_data.dav_id
99 EOSQL;
100  unset($params[':sync_token']);
101  }
102  else {
103  $sql = <<<EOSQL
104  SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, sync_changes.*
105  FROM collection LEFT JOIN sync_changes USING(collection_id)
106  LEFT JOIN caldav_data USING (collection_id,dav_id)
107  LEFT JOIN calendar_item USING (collection_id,dav_id)
108  LEFT JOIN addressbook_resource USING (dav_id)
109  WHERE collection.collection_id = :collection_id $hide_older $hide_todo
110  AND sync_time >= (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token)
111 EOSQL;
112  if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) {
113  $sql .= " ORDER BY collection.collection_id, lower(sync_changes.dav_name), sync_changes.sync_time";
114  }
115  else {
116  $sql .= " ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time";
117  }
118  }
119  $qry = new AwlQuery($sql, $params );
120 
121  $last_dav_name = '';
122  $first_status = 0;
123 
124  if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
125  if ( $qry->rows() > 50 ) {
126  // If there are more than 50 rows to send we should not send full data in response ...
127  $c->sync_resource_data_ok = false;
128  }
129  while( $object = $qry->Fetch() ) {
130  if ( $request_via_binding )
131  $object->dav_name = str_replace( $bound_from, $collection_path, $object->dav_name);
132 
133  if ( $object->dav_name == $last_dav_name ) {
135  if ( $object->sync_status == 404 ) {
136  array_pop($responses);
137  $resultset = array(
138  new XMLElement( 'href', ConstructURL($object->dav_name) ),
139  new XMLElement( 'status', display_status($object->sync_status) )
140  );
141  $responses[] = new XMLElement( 'response', $resultset );
142  $first_status = 404;
143  }
144  else if ( $object->sync_status == 201 && $first_status == 404 ) {
145  // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete
146  array_pop($responses);
147  $dav_resource = new DAVResource($object, $collection);
148  $resultset = $dav_resource->GetPropStat($proplist,$reply);
149  array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
150  $responses[] = new XMLElement( 'response', $resultset );
151  }
159  }
160  else {
162  if ( $object->sync_status == 404 ) {
163  $resultset = array(
164  new XMLElement( 'href', ConstructURL($object->dav_name) ),
165  new XMLElement( 'status', display_status($object->sync_status) )
166  );
167  $first_status = 404;
168  }
169  else {
170  $dav_resource = new DAVResource($object, $collection);
171  $resultset = $dav_resource->GetPropStat($proplist,$reply);
172  array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
173  $first_status = $object->sync_status;
174  }
175  $responses[] = new XMLElement( 'response', $resultset );
176  $last_dav_name = $object->dav_name;
177  }
178  }
179  $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
180  }
181  else {
182  $request->DoResponse( 500, translate("Database error") );
183  }
184 }
185 
186 $multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
187 
188 $request->XMLResponse( 207, $multistatus );