When a bind is made to an existing bind, bind to the target of that.
[davical.git] / inc / caldav-REPORT-sync-collection.php
blobf5ddced9dd85ec5441387ed4b513b74eeae76572
1 <?php
2 /**
3 * CalDAV Server - handle sync-collection report (draft-daboo-webdav-sync-01)
5 * @package davical
6 * @subpackage caldav
7 * @author Andrew McMillan <andrew@mcmillan.net.nz>
8 * @copyright Morphoss Ltd - http://www.morphoss.com/
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
12 $responses = array();
14 /**
15 * Build the array of properties to include in the report output
17 $sync_level = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-level');
18 if ( empty($sync_level) ) {
19 $sync_level = $request->depth;
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;
29 if ( $sync_level == DEPTH_INFINITY ) {
30 $request->PreconditionFailed(403, 'DAV::sync-traversal-supported','This server does not support sync-traversal');
33 $sync_tokens = $xmltree->GetPath('/DAV::sync-collection/DAV::sync-token');
34 $sync_token = $sync_tokens[0]->GetContent();
35 if ( !isset($sync_token) ) $sync_token = 0;
36 $sync_token = intval(str_ireplace('data:,', '', $sync_token ));
37 dbg_error_log( 'sync', " sync-token: %s", $sync_token );
40 $props = $xmltree->GetElements('DAV::prop');
41 $v = $props[0];
42 $props = $v->GetContent();
43 $proplist = array();
44 foreach( $props AS $k => $v ) {
45 $proplist[] = $v->GetNSTag();
48 function display_status( $status_code ) {
49 return sprintf( 'HTTP/1.1 %03d %s', intval($status_code), getStatusMessage($status_code) );
52 $collection = new DAVResource( $request->path );
53 if ( !$collection->Exists() ) {
54 $request->DoResponse( 404 );
56 $bound_from = $collection->bound_from();
57 $collection_path = $collection->dav_name();
58 $request_via_binding = ($bound_from != $collection_path);
60 $params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token );
61 $sql = "SELECT new_sync_token( :sync_token, :collection_id)";
62 $qry = new AwlQuery($sql, $params);
63 if ( !$qry->Exec("REPORT",__LINE__,__FILE__) || $qry->rows() <= 0 ) {
64 $request->DoResponse( 500, translate("Database error") );
66 $row = $qry->Fetch();
68 if ( !isset($row->new_sync_token) ) {
69 /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */
70 $sync_token = 0;
71 $params[':sync_token'] = $sync_token;
72 if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) {
73 $request->DoResponse( 500, translate("Database error") );
75 $row = $qry->Fetch();
77 $new_token = $row->new_sync_token;
79 if ( $sync_token == $new_token ) {
80 // No change, so we just re-send the old token.
81 $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
83 else {
84 if ( $sync_token == 0 ) {
85 $sql = <<<EOSQL
86 SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, 201 AS sync_status FROM collection
87 LEFT JOIN caldav_data USING (collection_id)
88 LEFT JOIN calendar_item USING (dav_id)
89 LEFT JOIN addressbook_resource USING (dav_id)
90 WHERE collection.collection_id = :collection_id
91 ORDER BY collection.collection_id, caldav_data.dav_id
92 EOSQL;
93 unset($params[':sync_token']);
95 else {
96 $sql = <<<EOSQL
97 SELECT collection.*, calendar_item.*, caldav_data.*, addressbook_resource.*, sync_changes.*
98 FROM collection LEFT JOIN sync_changes USING(collection_id)
99 LEFT JOIN caldav_data USING (collection_id,dav_id)
100 LEFT JOIN calendar_item USING (collection_id,dav_id)
101 LEFT JOIN addressbook_resource USING (dav_id)
102 WHERE collection.collection_id = :collection_id
103 AND sync_time >= (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token)
104 EOSQL;
105 if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) {
106 $sql .= " ORDER BY collection.collection_id, lower(sync_changes.dav_name), sync_changes.sync_time";
108 else {
109 $sql .= " ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time";
112 $qry = new AwlQuery($sql, $params );
114 $last_dav_name = '';
115 $first_status = 0;
117 if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
118 if ( $qry->rows() > 50 ) {
119 // If there are more than 50 rows to send we should not send full data in response ...
120 $c->sync_resource_data_ok = false;
122 while( $object = $qry->Fetch() ) {
123 if ( $request_via_binding )
124 $object->dav_name = str_replace( $bound_from, $collection_path, $object->dav_name);
126 if ( $object->dav_name == $last_dav_name ) {
127 /** The complex case: this is the second or subsequent for this dav_id */
128 if ( $object->sync_status == 404 ) {
129 array_pop($responses);
130 $resultset = array(
131 new XMLElement( 'href', ConstructURL($object->dav_name) ),
132 new XMLElement( 'status', display_status($object->sync_status) )
134 $responses[] = new XMLElement( 'response', $resultset );
135 $first_status = 404;
137 else if ( $object->sync_status == 201 && $first_status == 404 ) {
138 // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete
139 array_pop($responses);
140 $dav_resource = new DAVResource($object);
141 $resultset = $dav_resource->GetPropStat($proplist,$reply);
142 array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
143 $responses[] = new XMLElement( 'response', $resultset );
145 /** Else:
146 * the object existed at start and we have multiple modifications,
147 * or,
148 * the object didn't exist at start and we have subsequent modifications,
149 * but:
150 * in either case we simply stick with our existing report.
153 else {
154 /** The simple case: this is the first one for this dav_id */
155 if ( $object->sync_status == 404 ) {
156 $resultset = array(
157 new XMLElement( 'href', ConstructURL($object->dav_name) ),
158 new XMLElement( 'status', display_status($object->sync_status) )
160 $first_status = 404;
162 else {
163 $dav_resource = new DAVResource($object);
164 $resultset = $dav_resource->GetPropStat($proplist,$reply);
165 array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name)));
166 $first_status = $object->sync_status;
168 $responses[] = new XMLElement( 'response', $resultset );
169 $last_dav_name = $object->dav_name;
172 $responses[] = new XMLElement( 'sync-token', 'data:,'.$new_token );
174 else {
175 $request->DoResponse( 500, translate("Database error") );
179 $multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );
181 $request->XMLResponse( 207, $multistatus );