44 * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
55 */
66
7- use crate :: api:: auth:: JmapAuthorization ;
7+ use crate :: { api:: auth:: JmapAuthorization , changes :: state :: JmapCacheState } ;
88use common:: { Server , auth:: AccessToken } ;
9+ use email:: cache:: MessageCacheFetch ;
10+ use groupware:: cache:: GroupwareCache ;
911use jmap_proto:: {
1012 method:: changes:: { ChangesRequest , ChangesResponse } ,
1113 object:: { JmapObject , NullObject , mailbox:: MailboxProperty } ,
@@ -15,6 +17,7 @@ use jmap_proto::{
1517} ;
1618use std:: future:: Future ;
1719use store:: query:: log:: { Change , Query } ;
20+ use trc:: AddContext ;
1821use types:: collection:: { Collection , SyncCollection } ;
1922
2023pub trait ChangesLookup : Sync + Send {
@@ -139,12 +142,41 @@ impl ChangesLookup for Server {
139142
140143 ( 0 , changelog)
141144 }
142- State :: Exact ( change_id) => (
143- 0 ,
144- self . store ( )
145- . changes ( account_id, collection. into ( ) , Query :: Since ( * change_id) )
146- . await ?,
147- ) ,
145+ State :: Exact ( change_id) => {
146+ let last_state = match collection {
147+ SyncCollection :: Calendar | SyncCollection :: AddressBook => self
148+ . fetch_dav_resources ( access_token, account_id, collection)
149+ . await
150+ . caused_by ( trc:: location!( ) ) ?
151+ . get_state ( is_container)
152+ . into ( ) ,
153+ SyncCollection :: Email => self
154+ . get_cached_messages ( account_id)
155+ . await ?
156+ . get_state ( is_container)
157+ . into ( ) ,
158+ _ => None ,
159+ } ;
160+
161+ if let Some ( last_state) = last_state {
162+ response. new_state = last_state;
163+
164+ if response. new_state == State :: Exact ( * change_id) {
165+ return Ok ( IntermediateChangesResponse {
166+ response,
167+ object,
168+ only_container_changes : false ,
169+ } ) ;
170+ }
171+ }
172+
173+ (
174+ 0 ,
175+ self . store ( )
176+ . changes ( account_id, collection. into ( ) , Query :: Since ( * change_id) )
177+ . await ?,
178+ )
179+ }
148180 State :: Intermediate ( intermediate_state) => {
149181 let changelog = self
150182 . store ( )
@@ -175,10 +207,16 @@ impl ChangesLookup for Server {
175207 }
176208 } ;
177209
178- if changelog. is_truncated && request. since_state != State :: Initial {
179- return Err ( trc:: JmapEvent :: CannotCalculateChanges
180- . into_err ( )
181- . details ( "Changelog has been truncated" ) ) ;
210+ if ( changelog. is_truncated || changelog. from_change_id == 0 )
211+ && request. since_state != State :: Initial
212+ {
213+ return Err ( trc:: JmapEvent :: CannotCalculateChanges . into_err ( ) . details (
214+ if changelog. is_truncated {
215+ "Change log is truncated"
216+ } else {
217+ "Since state is invalid"
218+ } ,
219+ ) ) ;
182220 }
183221
184222 let mut changes = changelog
@@ -218,15 +256,15 @@ impl ChangesLookup for Server {
218256 . unwrap_or ( changelog. to_change_id ) ;
219257
220258 response. has_more_changes = changes. peek ( ) . is_some ( ) ;
221- response . new_state = if response. has_more_changes {
222- State :: new_intermediate (
259+ if response. has_more_changes {
260+ response . new_state = State :: new_intermediate (
223261 changelog. from_change_id ,
224262 change_id,
225263 items_sent + max_changes,
226- )
227- } else {
228- State :: new_exact ( change_id)
229- } ;
264+ ) ;
265+ } else if response . new_state == State :: Initial {
266+ response . new_state = State :: new_exact ( change_id)
267+ }
230268
231269 Ok ( IntermediateChangesResponse {
232270 only_container_changes : is_container && !response. updated . is_empty ( ) && !items_changed,
0 commit comments