diff --git a/dba/caldav_functions.sql b/dba/caldav_functions.sql index 8f6be8d1..f583afcb 100644 --- a/dba/caldav_functions.sql +++ b/dba/caldav_functions.sql @@ -1206,16 +1206,21 @@ DECLARE in_old_sync_token ALIAS FOR $1; in_collection_id ALIAS FOR $2; tmp_int INT8; + old_modification_time sync_tokens.modification_time%TYPE; BEGIN IF in_old_sync_token > 0 THEN - SELECT 1 INTO tmp_int FROM sync_changes - WHERE collection_id = in_collection_id - AND sync_time > (SELECT modification_time FROM sync_tokens WHERE sync_token = in_old_sync_token) - LIMIT 1; + SELECT modification_time INTO old_modification_time FROM sync_tokens WHERE sync_token = in_old_sync_token; IF NOT FOUND THEN -- They are in an inconsistent state: we return NULL so they can re-start the process RETURN NULL; END IF; + SELECT 1 INTO tmp_int FROM sync_changes WHERE collection_id = in_collection_id AND sync_time > old_modification_time LIMIT 1; + IF NOT FOUND THEN + -- Ensure we return the latest sync_token we have for this collection, since there are + -- no changes. + SELECT sync_token INTO tmp_int FROM sync_tokens WHERE collection_id = in_collection_id ORDER BY modification_time DESC LIMIT 1; + RETURN tmp_int; + END IF; END IF; SELECT nextval('sync_tokens_sync_token_seq') INTO tmp_int; INSERT INTO sync_tokens(collection_id, sync_token) VALUES( in_collection_id, tmp_int ); diff --git a/inc/caldav-REPORT-sync-collection.php b/inc/caldav-REPORT-sync-collection.php index 2bdb8e41..19f0820b 100644 --- a/inc/caldav-REPORT-sync-collection.php +++ b/inc/caldav-REPORT-sync-collection.php @@ -34,7 +34,10 @@ function display_status( $status_code ) { } $collection = new DAVResource( $request->path ); - +if ( !$collection->Exists() ) { + $request->DoResponse( 404 ); +} + $params = array( ':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token ); $sql = "SELECT new_sync_token( :sync_token, :collection_id)"; $qry = new AwlQuery($sql, $params); @@ -45,7 +48,7 @@ $row = $qry->Fetch(); if ( !isset($row->new_sync_token) ) { /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */ - $sync_token =0; + $sync_token = 0; $params[':sync_token'] = $sync_token; if ( !$qry->QDo($sql, $params) || $qry->rows() <= 0 ) { $request->DoResponse( 500, translate("Database error") ); @@ -54,85 +57,92 @@ if ( !isset($row->new_sync_token) ) { } $new_token = $row->new_sync_token; -if ( $sync_token == 0 ) { - $sql = << (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token) - ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time -EOSQL; -} -$qry = new AwlQuery($sql, $params ); - -$last_dav_name = ''; -$first_status = 0; - -if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) { - while( $object = $qry->Fetch() ) { - if ( $object->dav_name == $last_dav_name ) { - /** The complex case: this is the second or subsequent for this dav_id */ - if ( $object->sync_status == 404 ) { - array_pop($responses); - $resultset = array( - new XMLElement( 'href', ConstructURL($object->dav_name) ), - new XMLElement( 'status', display_status($object->sync_status) ) - ); - $responses[] = new XMLElement( 'sync-response', $resultset ); - $first_status = 404; - } - else if ( $object->sync_status == 201 && $first_status == 404 ) { - // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete - array_pop($responses); - $resultset = array( - new XMLElement( 'href', ConstructURL($object->dav_name) ), - new XMLElement( 'status', display_status($object->sync_status) ) - ); - $dav_resource = new DAVResource($object); - $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) ); - $responses[] = new XMLElement( 'sync-response', $resultset ); - } - /** Else: - * the object existed at start and we have multiple modifications, - * or, - * the object didn't exist at start and we have subsequent modifications, - * but: - * in either case we simply stick with our first report. - */ - } - else { - /** The simple case: this is the first one for this dav_id */ - $resultset = array( - new XMLElement( 'href', ConstructURL($object->dav_name) ), - new XMLElement( 'status', display_status($object->sync_status) ) - ); - $dav_resource = new DAVResource($object); - $resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) ); - $response_tag = 'response'; - if ( isset($c->use_old_sync_response_tag) && $c->use_old_sync_response_tag ) $response_tag = 'sync-response'; - $responses[] = new XMLElement( $response_tag, $resultset ); - $first_status = $object->sync_status; - $last_dav_name = $object->dav_name; - } - } +if ( $sync_token == $new_token ) { + // No change, so we just re-send the old token. $responses[] = new XMLElement( 'sync-token', $new_token ); } else { - $request->DoResponse( 500, translate("Database error") ); + if ( $sync_token == 0 ) { + $sql = << (SELECT modification_time FROM sync_tokens WHERE sync_token = :sync_token) + ORDER BY collection.collection_id, sync_changes.dav_name, sync_changes.sync_time +EOSQL; + } + $qry = new AwlQuery($sql, $params ); + + $last_dav_name = ''; + $first_status = 0; + + if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) { + while( $object = $qry->Fetch() ) { + if ( $object->dav_name == $last_dav_name ) { + /** The complex case: this is the second or subsequent for this dav_id */ + if ( $object->sync_status == 404 ) { + array_pop($responses); + $resultset = array( + new XMLElement( 'href', ConstructURL($object->dav_name) ), + new XMLElement( 'status', display_status($object->sync_status) ) + ); + $responses[] = new XMLElement( 'sync-response', $resultset ); + $first_status = 404; + } + else if ( $object->sync_status == 201 && $first_status == 404 ) { + // ... Delete ... Create ... is indicated as a create, but don't forget we started with a delete + array_pop($responses); + $dav_resource = new DAVResource($object); + $resultset = $dav_resource->GetPropStat($proplist,$reply); + array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name))); + $responses[] = new XMLElement( 'response', $resultset ); + } + /** Else: + * the object existed at start and we have multiple modifications, + * or, + * the object didn't exist at start and we have subsequent modifications, + * but: + * in either case we simply stick with our existing report. + */ + } + else { + /** The simple case: this is the first one for this dav_id */ + if ( $object->sync_status == 404 ) { + $resultset = array( + new XMLElement( 'href', ConstructURL($object->dav_name) ), + new XMLElement( 'status', display_status($object->sync_status) ) + ); + $first_status = 404; + } + else { + $dav_resource = new DAVResource($object); + $resultset = $dav_resource->GetPropStat($proplist,$reply); + array_unshift($resultset, new XMLElement( 'href', ConstructURL($object->dav_name))); + $first_status = $object->sync_status; + } + $responses[] = new XMLElement( 'response', $resultset ); + $last_dav_name = $object->dav_name; + } + } + $responses[] = new XMLElement( 'sync-token', $new_token ); + } + else { + $request->DoResponse( 500, translate("Database error") ); + } } $multistatus = new XMLElement( "multistatus", $responses, $reply->GetXmlNsArray() );