File: | libsynthesis/src/sysync/localengineds.cpp |
Warning: | line 2757, column 9 Called C++ object pointer is null |
1 | /** | |||
2 | * @File localengineds.cpp | |||
3 | * | |||
4 | * @Author Lukas Zeller (luz@plan44.ch) | |||
5 | * | |||
6 | * @brief TLocalEngineDS | |||
7 | * Abstraction of the local datastore - interface class to the | |||
8 | * sync engine. | |||
9 | * | |||
10 | * Copyright (c) 2001-2011 by Synthesis AG + plan44.ch | |||
11 | * | |||
12 | * @Date 2005-09-15 : luz : created from localdatastore | |||
13 | */ | |||
14 | ||||
15 | // includes | |||
16 | #include "prefix_file.h" | |||
17 | #include "sysync.h" | |||
18 | #include "localengineds.h" | |||
19 | #include "syncappbase.h" | |||
20 | #include "scriptcontext.h" | |||
21 | #include "superdatastore.h" | |||
22 | #include "syncagent.h" | |||
23 | ||||
24 | ||||
25 | using namespace sysync; | |||
26 | ||||
27 | namespace sysync { | |||
28 | ||||
29 | #ifdef SYDEBUG2 | |||
30 | ||||
31 | cAppCharP const LocalDSStateNames[numDSStates] = { | |||
32 | "idle", | |||
33 | "client_initialized", | |||
34 | "admin_ready", | |||
35 | "client_sent_alert", | |||
36 | "server_alerted", | |||
37 | "server_answered_alert", | |||
38 | "client_alert_statused", | |||
39 | "client_alerted", | |||
40 | "sync_mode_stable", | |||
41 | "data_access_started", | |||
42 | "sync_set_ready", | |||
43 | "client_sync_gen_started", | |||
44 | "server_seen_client_mods", | |||
45 | "server_sync_gen_started", | |||
46 | "sync_gen_done", | |||
47 | "data_access_done", | |||
48 | "client_maps_sent", | |||
49 | "admin_done", | |||
50 | "completed" | |||
51 | }; | |||
52 | #endif | |||
53 | ||||
54 | ||||
55 | #ifdef OBJECT_FILTERING1 | |||
56 | ||||
57 | // add a new expression to an existing filter | |||
58 | static void addToFilter(const char *aNewFilter, string &aFilter, bool aORChain=false) | |||
59 | { | |||
60 | if (aNewFilter && *aNewFilter) { | |||
61 | // just assign if current filter expression is empty | |||
62 | if (aFilter.empty()) | |||
63 | aFilter = aNewFilter; | |||
64 | else { | |||
65 | // construct new filter | |||
66 | string newFilter; | |||
67 | StringObjPrintf(newFilter,"(%s)%c(%s)",aFilter.c_str(),aORChain ? '|' : '&',aNewFilter); | |||
68 | aFilter = newFilter; | |||
69 | } | |||
70 | } | |||
71 | } // addToFilter | |||
72 | ||||
73 | #endif | |||
74 | ||||
75 | #ifdef SCRIPT_SUPPORT1 | |||
76 | ||||
77 | // Script functions | |||
78 | // ================ | |||
79 | ||||
80 | class TLDSfuncs { | |||
81 | public: | |||
82 | ||||
83 | #ifdef OBJECT_FILTERING1 | |||
84 | #ifdef SYNCML_TAF_SUPPORT | |||
85 | ||||
86 | // string GETCGITARGETFILTER() | |||
87 | // returns current CGI-specified target address filter expression | |||
88 | static void func_GetCGITargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
89 | { | |||
90 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
91 | aTermP->setAsString(dsP->fTargetAddressFilter.c_str()); | |||
92 | }; // func_GetCGITargetFilter | |||
93 | ||||
94 | ||||
95 | // string GETTARGETFILTER() | |||
96 | // returns current internal target address filter expression | |||
97 | static void func_GetTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
98 | { | |||
99 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
100 | aTermP->setAsString(dsP->fIntTargetAddressFilter.c_str()); | |||
101 | }; // func_GetTargetFilter | |||
102 | ||||
103 | ||||
104 | // SETTARGETFILTER(string filter) | |||
105 | // sets (overwrites) the internal target address filter | |||
106 | static void func_SetTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
107 | { | |||
108 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
109 | aFuncContextP->getLocalVar(0)->getAsString(dsP->fIntTargetAddressFilter); | |||
110 | }; // func_SetTargetFilter | |||
111 | ||||
112 | ||||
113 | // ADDTARGETFILTER(string filter) | |||
114 | // adds a filter expression to the existing internal targetfilter (automatically paranthesizing and adding AND) | |||
115 | static void func_AddTargetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
116 | { | |||
117 | string f; | |||
118 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
119 | aFuncContextP->getLocalVar(0)->getAsString(f); | |||
120 | addToFilter(f.c_str(),dsP->fIntTargetAddressFilter,false); // AND-chaining | |||
121 | }; // func_AddTargetFilter | |||
122 | ||||
123 | #endif // SYNCML_TAF_SUPPORT | |||
124 | ||||
125 | ||||
126 | // string GETFILTER() | |||
127 | // returns current sync set filter expression | |||
128 | static void func_GetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
129 | { | |||
130 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
131 | aTermP->setAsString(dsP->fSyncSetFilter.c_str()); | |||
132 | }; // func_GetFilter | |||
133 | ||||
134 | ||||
135 | // SETFILTER(string filter) | |||
136 | // sets (overwrites) the current sync set filter | |||
137 | static void func_SetFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
138 | { | |||
139 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
140 | aFuncContextP->getLocalVar(0)->getAsString(dsP->fSyncSetFilter); | |||
141 | dsP->engFilteredFetchesFromDB(true); // update filter dependencies | |||
142 | }; // func_SetFilter | |||
143 | ||||
144 | ||||
145 | // ADDFILTER(string filter) | |||
146 | // adds a filter expression to the existing (dynamic) targetfilter (automatically paranthesizing and adding AND) | |||
147 | static void func_AddFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
148 | { | |||
149 | string f; | |||
150 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
151 | aFuncContextP->getLocalVar(0)->getAsString(f); | |||
152 | addToFilter(f.c_str(),dsP->fSyncSetFilter,false); // AND-chaining | |||
153 | dsP->engFilteredFetchesFromDB(true); // update filter dependencies | |||
154 | }; // func_AddFilter | |||
155 | ||||
156 | ||||
157 | // ADDSTATICFILTER(string filter) | |||
158 | // adds a filter expression to the existing (static) localdbfilter (automatically paranthesizing and adding AND) | |||
159 | static void func_AddStaticFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
160 | { | |||
161 | string f; | |||
162 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
163 | aFuncContextP->getLocalVar(0)->getAsString(f); | |||
164 | addToFilter(f.c_str(),dsP->fLocalDBFilter,false); // AND-chaining | |||
165 | dsP->engFilteredFetchesFromDB(true); // update filter dependencies | |||
166 | }; // func_AddStaticFilter | |||
167 | ||||
168 | #endif // OBJECT_FILTERING | |||
169 | ||||
170 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
171 | ||||
172 | // string DBOPTIONS() | |||
173 | // returns current DB options | |||
174 | static void func_DBOptions(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
175 | { | |||
176 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
177 | aTermP->setAsString(dsP->fDBOptions.c_str()); | |||
178 | }; // func_DBOptions | |||
179 | ||||
180 | ||||
181 | // integer DBHANDLESOPTS() | |||
182 | // returns true if database can completely handle options like /dr() and /li during fetching | |||
183 | static void func_DBHandlesOpts(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
184 | { | |||
185 | aTermP->setAsBoolean( | |||
186 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->dsOptionFilterFetchesFromDB() | |||
187 | ); | |||
188 | }; // func_DBHandlesOpts | |||
189 | ||||
190 | ||||
191 | // timestamp STARTDATE() | |||
192 | // returns startdate if one is set in datastore | |||
193 | static void func_StartDate(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
194 | { | |||
195 | lineartime_t d = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeStart; | |||
196 | TTimestampField *resP = static_cast<TTimestampField *>(aTermP); | |||
197 | if (d==noLinearTime) | |||
198 | resP->assignEmpty(); | |||
199 | else | |||
200 | resP->setTimestampAndContext(d,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))); | |||
201 | }; // func_StartDate | |||
202 | ||||
203 | ||||
204 | // SETSTARTDATE(timestamp startdate) | |||
205 | // sets startdate for datastore | |||
206 | static void func_SetStartDate(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
207 | { | |||
208 | TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0)); | |||
209 | timecontext_t tctx; | |||
210 | ||||
211 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeStart = | |||
212 | tsP->getTimestampAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),&tctx); // floating will also be treated as UTC | |||
213 | }; // func_SetStartDate | |||
214 | ||||
215 | ||||
216 | // timestamp ENDDATE() | |||
217 | // returns enddate if one is set in datastore | |||
218 | static void func_EndDate(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
219 | { | |||
220 | lineartime_t d = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeEnd; | |||
221 | TTimestampField *resP = static_cast<TTimestampField *>(aTermP); | |||
222 | if (d==noLinearTime) | |||
223 | resP->assignEmpty(); | |||
224 | else | |||
225 | resP->setTimestampAndContext(d,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))); | |||
226 | }; // func_EndDate | |||
227 | ||||
228 | ||||
229 | // SETENDDATE(timestamp startdate) | |||
230 | // sets enddate for datastore | |||
231 | static void func_SetEndDate(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
232 | { | |||
233 | TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0)); | |||
234 | timecontext_t tctx; | |||
235 | ||||
236 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fDateRangeEnd = | |||
237 | tsP->getTimestampAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),&tctx); // floating will also be treated as UTC | |||
238 | }; // func_SetEndDate | |||
239 | ||||
240 | ||||
241 | // integer DEFAULTSIZELIMIT() | |||
242 | // returns limit set for all items in this datastore (the /li(xxx) CGI option value) | |||
243 | static void func_DefaultLimit(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
244 | { | |||
245 | fieldinteger_t i = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSizeLimit; | |||
246 | if (i<0) | |||
247 | aTermP->unAssign(); // no limit | |||
248 | else | |||
249 | aTermP->setAsInteger(i); | |||
250 | }; // func_DefaultLimit | |||
251 | ||||
252 | ||||
253 | // SETDEFAULTSIZELIMIT(integer limit) | |||
254 | // sets limit for all items in this datastore (the /li(xxx) CGI option value) | |||
255 | static void func_SetDefaultLimit(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
256 | { | |||
257 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSizeLimit = | |||
258 | aFuncContextP->getLocalVar(0)->getAsInteger(); | |||
259 | }; // func_SetDefaultLimit | |||
260 | ||||
261 | ||||
262 | // integer NOATTACHMENTS() | |||
263 | // returns true if attachments should be suppressed (/na CGI option) | |||
264 | static void func_NoAttachments(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
265 | { | |||
266 | aTermP->setAsBoolean( | |||
267 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fNoAttachments | |||
268 | ); | |||
269 | }; // func_NoAttachments | |||
270 | ||||
271 | ||||
272 | // SETNOATTACHMENTS(integer flag) | |||
273 | // if true, attachments will be suppressed (/na CGI option) | |||
274 | static void func_SetNoAttachments(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
275 | { | |||
276 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fNoAttachments = | |||
277 | aFuncContextP->getLocalVar(0)->getAsBoolean(); | |||
278 | }; // func_SetNoAttachments | |||
279 | ||||
280 | ||||
281 | // integer MAXITEMCOUNT() | |||
282 | // returns item count limit (0=none) as set by /max(n) CGI option | |||
283 | static void func_MaxItemCount(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
284 | { | |||
285 | aTermP->setAsInteger( | |||
286 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fMaxItemCount | |||
287 | ); | |||
288 | }; // func_MaxItemCount | |||
289 | ||||
290 | ||||
291 | // SETMAXITEMCOUNT(integer maxcount) | |||
292 | // set item count limit (0=none) as set by /max(n) CGI option | |||
293 | static void func_SetMaxItemCount(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
294 | { | |||
295 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fMaxItemCount = | |||
296 | aFuncContextP->getLocalVar(0)->getAsInteger(); | |||
297 | }; // func_SetMaxItemCount | |||
298 | ||||
299 | #endif // SYSYNC_TARGET_OPTIONS | |||
300 | ||||
301 | ||||
302 | // integer SLOWSYNC() | |||
303 | // returns true if we are in slow sync | |||
304 | static void func_SlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
305 | { | |||
306 | aTermP->setAsBoolean( | |||
307 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isSlowSync() | |||
308 | ); | |||
309 | }; // func_SlowSync | |||
310 | ||||
311 | ||||
312 | // FORCESLOWSYNC() | |||
313 | // force a slow sync (like with /na CGI option) | |||
314 | static void func_ForceSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
315 | { | |||
316 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engForceSlowSync(); | |||
317 | }; // func_ForceSlowSync | |||
318 | ||||
319 | ||||
320 | ||||
321 | // integer ALERTCODE() | |||
322 | // returns the alert code as currently know by datastore (might change from normal to slow while processing) | |||
323 | static void func_AlertCode(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
324 | { | |||
325 | aTermP->setAsInteger( | |||
326 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fAlertCode | |||
327 | ); | |||
328 | }; // func_AlertCode | |||
329 | ||||
330 | ||||
331 | // SETALERTCODE(integer maxcount) | |||
332 | // set the alert code (makes sense in alertscript to modify the incoming code to something different) | |||
333 | static void func_SetAlertCode(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
334 | { | |||
335 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fAlertCode = | |||
336 | aFuncContextP->getLocalVar(0)->getAsInteger(); | |||
337 | }; // func_SetAlertCode | |||
338 | ||||
339 | ||||
340 | ||||
341 | // integer REFRESHONLY() | |||
342 | // returns true if sync is only refreshing local (note that alert code might be different, as local | |||
343 | // refresh can take place without telling the remote so, for compatibility with clients that do not support the mode) | |||
344 | static void func_RefreshOnly(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
345 | { | |||
346 | aTermP->setAsBoolean( | |||
347 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isRefreshOnly() | |||
348 | ); | |||
349 | }; // func_RefreshOnly | |||
350 | ||||
351 | ||||
352 | // SETREFRESHONLY(integer flag) | |||
353 | // modifies the refresh only flag (one way sync from remote to local only) | |||
354 | // Note that clearing this flag when a client has alerted one-way will probably lead to an error | |||
355 | static void func_SetRefreshOnly(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
356 | { | |||
357 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engSetRefreshOnly( | |||
358 | aFuncContextP->getLocalVar(0)->getAsBoolean() | |||
359 | ); | |||
360 | }; // func_SetRefreshOnly | |||
361 | ||||
362 | ||||
363 | // integer CACHEDATA() | |||
364 | // returns true if sync is refreshing local data in caching mode (without deleting everything beforehand) | |||
365 | static void func_CacheData(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
366 | { | |||
367 | aTermP->setAsBoolean( | |||
368 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isCacheData() | |||
369 | ); | |||
370 | }; // func_CacheData | |||
371 | static void func_SetCacheData(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
372 | { | |||
373 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engSetCacheData( | |||
374 | aFuncContextP->getLocalVar(0)->getAsBoolean() | |||
375 | ); | |||
376 | }; // func_SetCacheData | |||
377 | ||||
378 | ||||
379 | // integer READONLY() | |||
380 | // returns true if sync is read-only (only reading from local datastore) | |||
381 | static void func_ReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
382 | { | |||
383 | aTermP->setAsBoolean( | |||
384 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isReadOnly() | |||
385 | ); | |||
386 | }; // func_ReadOnly | |||
387 | ||||
388 | ||||
389 | // SETREADONLY(integer flag) | |||
390 | // modifies the read only flag (only reading from local datastore) | |||
391 | static void func_SetReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
392 | { | |||
393 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engSetReadOnly( | |||
394 | aFuncContextP->getLocalVar(0)->getAsBoolean() | |||
395 | ); | |||
396 | }; // func_SetReadOnly | |||
397 | ||||
398 | ||||
399 | ||||
400 | // integer FIRSTTIMESYNC() | |||
401 | // returns true if we are in first time slow sync | |||
402 | static void func_FirstTimeSync(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
403 | { | |||
404 | aTermP->setAsBoolean( | |||
405 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->isFirstTimeSync() | |||
406 | ); | |||
407 | }; // func_FirstTimeSync | |||
408 | ||||
409 | ||||
410 | // void SETCONFLICTSTRATEGY(string strategy) | |||
411 | // sets conflict strategy for this session | |||
412 | static void func_SetConflictStrategy(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
413 | { | |||
414 | // convert to syncop | |||
415 | string s; | |||
416 | aFuncContextP->getLocalVar(0)->getAsString(s); | |||
417 | sInt16 strategy; | |||
418 | StrToEnum(conflictStrategyNames,numConflictStrategies, strategy, s.c_str()); | |||
419 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->fSessionConflictStrategy = | |||
420 | (TConflictResolution) strategy; | |||
421 | }; // func_SetConflictStrategy | |||
422 | ||||
423 | ||||
424 | // string DBNAME() | |||
425 | // returns name of DB | |||
426 | static void func_DBName(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
427 | { | |||
428 | aTermP->setAsString( | |||
429 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->getName() | |||
430 | ); | |||
431 | }; // func_DBName | |||
432 | ||||
433 | // void ABORTDATASTORE(integer statuscode) | |||
434 | static void func_AbortDatastore(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
435 | { | |||
436 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->engAbortDataStoreSync(aFuncContextP->getLocalVar(0)->getAsInteger(),true); // we cause the abort locally | |||
437 | } // func_AbortDatastore | |||
438 | ||||
439 | // string LOCALDBNAME() | |||
440 | // returns name of local DB with which it was identified for the sync | |||
441 | static void func_LocalDBName(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
442 | { | |||
443 | aTermP->setAsString( | |||
444 | static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext())->getIdentifyingName() | |||
445 | ); | |||
446 | }; // func_LocalDBName | |||
447 | ||||
448 | ||||
449 | // string REMOTEDBNAME() | |||
450 | // returns remote datastore's full name (as used by the remote in <sync> command, may contain subpath and CGI) | |||
451 | static void func_RemoteDBName(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
452 | { | |||
453 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
454 | aTermP->setAsString(dsP->getRemoteDatastore()->getFullName()); | |||
455 | }; // func_RemoteDBName | |||
456 | ||||
457 | ||||
458 | #ifdef SYSYNC_CLIENT1 | |||
459 | ||||
460 | // ADDTARGETCGI(string cgi) | |||
461 | // adds CGI to the target URI. If target URI already contains a ?, string will be just | |||
462 | // appended, otherwise a ? is added, then the new CGI. | |||
463 | // Note: if string to be added is already contained, it will not be added again | |||
464 | static void func_AddTargetCGI(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
465 | { | |||
466 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
467 | // Add extra CGI specified | |||
468 | string cgi; | |||
469 | aFuncContextP->getLocalVar(0)->getAsString(cgi); | |||
470 | addCGItoString(dsP->fRemoteDBPath,cgi.c_str(),true); | |||
471 | }; // func_AddTargetCGI | |||
472 | ||||
473 | ||||
474 | // SETRECORDFILTER(string filter, boolean inclusive) | |||
475 | // Sets record level filter expression for remote | |||
476 | static void func_SetRecordFilter(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
477 | { | |||
478 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
479 | // put into record level filter | |||
480 | if (dsP->getSession()->getSyncMLVersion()>=syncml_vers_1_2) { | |||
481 | // DS 1.2: use <filter> | |||
482 | aFuncContextP->getLocalVar(0)->getAsString(dsP->fRemoteRecordFilterQuery); | |||
483 | dsP->fRemoteFilterInclusive = aFuncContextP->getLocalVar(1)->getAsBoolean(); | |||
484 | } | |||
485 | else if (!aFuncContextP->getLocalVar(0)->isEmpty()) { | |||
486 | // DS 1.1 and below and not empty filter: add as cgi | |||
487 | string filtercgi; | |||
488 | if (aFuncContextP->getLocalVar(1)->getAsBoolean()) | |||
489 | filtercgi = "/tf("; // exclusive, use TAF | |||
490 | else | |||
491 | filtercgi = "/fi("; // inclusive, use sync set filter | |||
492 | aFuncContextP->getLocalVar(0)->appendToString(filtercgi); | |||
493 | filtercgi += ')'; | |||
494 | addCGItoString(dsP->fRemoteDBPath,filtercgi.c_str(),true); | |||
495 | } | |||
496 | }; // func_SetRecordFilter | |||
497 | ||||
498 | ||||
499 | // SETDAYSRANGE(integer daysbefore, integer daysafter) | |||
500 | // Sets type of record filter | |||
501 | static void func_SetDaysRange(TItemField *&aTermP, TScriptContext *aFuncContextP) | |||
502 | { | |||
503 | TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext()); | |||
504 | // get params | |||
505 | int daysbefore = aFuncContextP->getLocalVar(0)->getAsInteger(); | |||
506 | int daysafter = aFuncContextP->getLocalVar(1)->getAsInteger(); | |||
507 | // depending on SyncML version, create a SINCE/BEFORE filter or use the /dr(x,y) syntax | |||
508 | if (dsP->getSession()->getSyncMLVersion()>=syncml_vers_1_2 && static_cast<TSyncAgent *>(dsP->getSession())->fServerHasSINCEBEFORE) { | |||
509 | // use the SINCE/BEFORE syntax | |||
510 | // BEFORE&EQ;20070808T000000Z&AND;SINCE&EQ;20070807T000000Z | |||
511 | lineartime_t now = getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),aFuncContextP->getSessionZones()); | |||
512 | string ts; | |||
513 | // AND-chain with possibly existing filter | |||
514 | cAppCharP sep = ""; | |||
515 | if (!dsP->fRemoteRecordFilterQuery.empty()) | |||
516 | sep = "&AND;"; | |||
517 | if (daysbefore>=0) { | |||
518 | dsP->fRemoteRecordFilterQuery += sep; | |||
519 | dsP->fRemoteRecordFilterQuery += "SINCE&EQ;"; | |||
520 | TimestampToISO8601Str(ts,now-daysbefore*linearDateToTimeFactor,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),false,false); | |||
521 | dsP->fRemoteRecordFilterQuery += ts; | |||
522 | sep = "&AND;"; | |||
523 | } | |||
524 | if (daysafter>=0) { | |||
525 | dsP->fRemoteRecordFilterQuery += sep; | |||
526 | dsP->fRemoteRecordFilterQuery += "BEFORE&EQ;"; | |||
527 | TimestampToISO8601Str(ts,now+daysafter*linearDateToTimeFactor,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),false,false); | |||
528 | dsP->fRemoteRecordFilterQuery += ts; | |||
529 | } | |||
530 | } | |||
531 | else { | |||
532 | // use the /dr(-x,y) syntax | |||
533 | string rangecgi; | |||
534 | StringObjPrintf(rangecgi,"/dr(%ld,%ld)",(long int)(-daysbefore),(long int)(daysafter)); | |||
535 | addCGItoString(dsP->fRemoteDBPath,rangecgi.c_str(),true); | |||
536 | } | |||
537 | }; // func_SetDaysRange | |||
538 | ||||
539 | ||||
540 | #endif // SYSYNC_CLIENT | |||
541 | ||||
542 | }; // TLDSfuncs | |||
543 | ||||
544 | const uInt8 param_FilterArg[] = { VAL(fty_string)( (uInt8)fty_string) }; | |||
545 | const uInt8 param_DateArg[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp) }; | |||
546 | const uInt8 param_IntArg[] = { VAL(fty_integer)( (uInt8)fty_integer) }; | |||
547 | const uInt8 param_StrArg[] = { VAL(fty_string)( (uInt8)fty_string) }; | |||
548 | const uInt8 param_OneInteger[] = { VAL(fty_integer)( (uInt8)fty_integer) }; | |||
549 | ||||
550 | const TBuiltInFuncDef DBFuncDefs[] = { | |||
551 | #ifdef OBJECT_FILTERING1 | |||
552 | #ifdef SYNCML_TAF_SUPPORT | |||
553 | { "GETCGITARGETFILTER", TLDSfuncs::func_GetCGITargetFilter, fty_string, 0, NULL__null }, | |||
554 | { "GETTARGETFILTER", TLDSfuncs::func_GetTargetFilter, fty_string, 0, NULL__null }, | |||
555 | { "SETTARGETFILTER", TLDSfuncs::func_SetTargetFilter, fty_none, 1, param_FilterArg }, | |||
556 | { "ADDTARGETFILTER", TLDSfuncs::func_AddTargetFilter, fty_none, 1, param_FilterArg }, | |||
557 | #endif | |||
558 | { "GETFILTER", TLDSfuncs::func_GetFilter, fty_string, 0, NULL__null }, | |||
559 | { "SETFILTER", TLDSfuncs::func_SetFilter, fty_none, 1, param_FilterArg }, | |||
560 | { "ADDFILTER", TLDSfuncs::func_AddFilter, fty_none, 1, param_FilterArg }, | |||
561 | { "ADDSTATICFILTER", TLDSfuncs::func_AddStaticFilter, fty_none, 1, param_FilterArg }, | |||
562 | #endif | |||
563 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
564 | { "DBOPTIONS", TLDSfuncs::func_DBOptions, fty_string, 0, NULL__null }, | |||
565 | { "STARTDATE", TLDSfuncs::func_StartDate, fty_timestamp, 0, NULL__null }, | |||
566 | { "ENDDATE", TLDSfuncs::func_EndDate, fty_timestamp, 0, NULL__null }, | |||
567 | { "SETSTARTDATE", TLDSfuncs::func_SetStartDate, fty_none, 1, param_DateArg }, | |||
568 | { "SETENDDATE", TLDSfuncs::func_SetEndDate, fty_none, 1, param_DateArg }, | |||
569 | { "MAXITEMCOUNT", TLDSfuncs::func_MaxItemCount, fty_integer, 0, NULL__null }, | |||
570 | { "SETMAXITEMCOUNT", TLDSfuncs::func_SetMaxItemCount, fty_none, 1, param_IntArg }, | |||
571 | { "NOATTACHMENTS", TLDSfuncs::func_NoAttachments, fty_integer, 0, NULL__null }, | |||
572 | { "SETNOATTACHMENTS", TLDSfuncs::func_SetNoAttachments, fty_none, 1, param_IntArg }, | |||
573 | { "DEFAULTSIZELIMIT", TLDSfuncs::func_DefaultLimit, fty_integer, 0, NULL__null }, | |||
574 | { "SETDEFAULTSIZELIMIT", TLDSfuncs::func_SetDefaultLimit, fty_none, 1, param_IntArg }, | |||
575 | { "DBHANDLESOPTS", TLDSfuncs::func_DBHandlesOpts, fty_integer, 0, NULL__null }, | |||
576 | #endif | |||
577 | { "ALERTCODE", TLDSfuncs::func_AlertCode, fty_integer, 0, NULL__null }, | |||
578 | { "SETALERTCODE", TLDSfuncs::func_SetAlertCode, fty_none, 1, param_IntArg }, | |||
579 | { "SLOWSYNC", TLDSfuncs::func_SlowSync, fty_integer, 0, NULL__null }, | |||
580 | { "FORCESLOWSYNC", TLDSfuncs::func_ForceSlowSync, fty_none, 0, NULL__null }, | |||
581 | { "REFRESHONLY", TLDSfuncs::func_RefreshOnly, fty_integer, 0, NULL__null }, | |||
582 | { "SETREFRESHONLY", TLDSfuncs::func_SetRefreshOnly, fty_none, 1, param_IntArg }, | |||
583 | { "CACHEDATA", TLDSfuncs::func_CacheData, fty_integer, 0, NULL__null }, | |||
584 | { "SETCACHEDATA", TLDSfuncs::func_SetCacheData, fty_none, 1, param_IntArg }, | |||
585 | { "READONLY", TLDSfuncs::func_ReadOnly, fty_integer, 0, NULL__null }, | |||
586 | { "SETREADONLY", TLDSfuncs::func_SetReadOnly, fty_none, 1, param_IntArg }, | |||
587 | { "FIRSTTIMESYNC", TLDSfuncs::func_FirstTimeSync, fty_integer, 0, NULL__null }, | |||
588 | { "SETCONFLICTSTRATEGY", TLDSfuncs::func_SetConflictStrategy, fty_none, 1, param_StrArg }, | |||
589 | { "DBNAME", TLDSfuncs::func_DBName, fty_string, 0, NULL__null }, | |||
590 | { "LOCALDBNAME", TLDSfuncs::func_LocalDBName, fty_string, 0, NULL__null }, | |||
591 | { "REMOTEDBNAME", TLDSfuncs::func_RemoteDBName, fty_string, 0, NULL__null }, | |||
592 | { "ABORTDATASTORE", TLDSfuncs::func_AbortDatastore, fty_none, 1, param_OneInteger }, | |||
593 | }; | |||
594 | ||||
595 | // functions for all datastores | |||
596 | const TFuncTable DBFuncTable = { | |||
597 | sizeof(DBFuncDefs) / sizeof(TBuiltInFuncDef), // size of table | |||
598 | DBFuncDefs, // table pointer | |||
599 | NULL__null // no chain func | |||
600 | }; | |||
601 | ||||
602 | ||||
603 | #ifdef SYSYNC_CLIENT1 | |||
604 | ||||
605 | const uInt8 param_OneStr[] = { VAL(fty_string)( (uInt8)fty_string) }; | |||
606 | // const uInt8 param_OneInt[] = { VAL(fty_integer) }; | |||
607 | const uInt8 param_TwoInt[] = { VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer) }; | |||
608 | const uInt8 param_SetRecordFilter[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer) }; | |||
609 | ||||
610 | const TBuiltInFuncDef ClientDBFuncDefs[] = { | |||
611 | { "ADDTARGETCGI", TLDSfuncs::func_AddTargetCGI, fty_none, 1, param_OneStr }, | |||
612 | { "SETRECORDFILTER", TLDSfuncs::func_SetRecordFilter, fty_none, 2, param_SetRecordFilter }, | |||
613 | { "SETDAYSRANGE", TLDSfuncs::func_SetDaysRange, fty_none, 2, param_TwoInt }, | |||
614 | }; | |||
615 | ||||
616 | ||||
617 | // chain to general DB functions | |||
618 | static void *ClientDBChainFunc(void *&aCtx) | |||
619 | { | |||
620 | // caller context remains unchanged | |||
621 | // -> no change needed | |||
622 | // next table is general DS func table | |||
623 | return (void *)&DBFuncTable; | |||
624 | } // ClientDBChainFunc | |||
625 | ||||
626 | ||||
627 | // function table for client-only script functions | |||
628 | const TFuncTable ClientDBFuncTable = { | |||
629 | sizeof(ClientDBFuncDefs) / sizeof(TBuiltInFuncDef), // size of table | |||
630 | ClientDBFuncDefs, // table pointer | |||
631 | ClientDBChainFunc // chain to general agent funcs. | |||
632 | }; | |||
633 | ||||
634 | ||||
635 | #endif // SYSYNC_CLIENT | |||
636 | ||||
637 | #endif // SCRIPT_SUPPORT | |||
638 | ||||
639 | ||||
640 | ||||
641 | ||||
642 | // config | |||
643 | // ====== | |||
644 | ||||
645 | // conflict strategy names | |||
646 | // bfo: Problems with XCode (expicit qualification), already within namespace ? | |||
647 | //const char * const sysync::conflictStrategyNames[numConflictStrategies] = { | |||
648 | const char * const conflictStrategyNames[numConflictStrategies] = { | |||
649 | "duplicate", // add conflicting counterpart to both databases | |||
650 | "newer-wins", // newer version wins (if date/version comparison is possible, like sst_duplicate otherwise) | |||
651 | "server-wins", // server version wins (and is written to client) | |||
652 | "client-wins" // client version wins (and is written to server) | |||
653 | }; | |||
654 | ||||
655 | ||||
656 | // type support config | |||
657 | TTypeSupportConfig::TTypeSupportConfig(const char* aName, TConfigElement *aParentElement) : | |||
658 | TConfigElement(aName,aParentElement) | |||
659 | { | |||
660 | clear(); | |||
661 | } // TTypeSupportConfig::TTypeSupportConfig | |||
662 | ||||
663 | ||||
664 | TTypeSupportConfig::~TTypeSupportConfig() | |||
665 | { | |||
666 | clear(); | |||
667 | } // TTypeSupportConfig::~TTypeSupportConfig | |||
668 | ||||
669 | ||||
670 | // init defaults | |||
671 | void TTypeSupportConfig::clear(void) | |||
672 | { | |||
673 | // init defaults | |||
674 | fPreferredTx = NULL__null; | |||
675 | fPreferredRx = NULL__null; | |||
676 | fPreferredLegacy = NULL__null; | |||
677 | fAdditionalTypes.clear(); | |||
678 | #ifndef NO_REMOTE_RULES | |||
679 | fRuleMatchTypes.clear(); | |||
680 | #endif | |||
681 | // clear inherited | |||
682 | inherited::clear(); | |||
683 | } // TTypeSupportConfig::clear | |||
684 | ||||
685 | ||||
686 | #ifdef HARDCODED_CONFIG | |||
687 | ||||
688 | // add type support | |||
689 | bool TTypeSupportConfig::addTypeSupport( | |||
690 | cAppCharP aTypeName, | |||
691 | bool aForRead, | |||
692 | bool aForWrite, | |||
693 | bool aPreferred, | |||
694 | cAppCharP aVariant, | |||
695 | cAppCharP aRuleMatch | |||
696 | ) { | |||
697 | // search datatype | |||
698 | TDataTypeConfig *typecfgP = | |||
699 | static_cast<TRootConfig *>(getRootElement())->fDatatypesConfigP->getDataType(aTypeName); | |||
700 | if (!typecfgP) return false; | |||
701 | // search variant | |||
702 | TTypeVariantDescriptor variantDescP = NULL__null; | |||
703 | if (aVariant && *aVariant) | |||
704 | variantDescP = typecfgP->getVariantDescriptor(aVariant); | |||
705 | // now add datatype | |||
706 | if (aPreferred) { | |||
707 | // - preferred | |||
708 | if (aForRead) { | |||
709 | if (!fPreferredRx) { | |||
710 | fPreferredRx=typecfgP; // set it | |||
711 | fPrefRxVariantDescP=variantDescP; | |||
712 | } | |||
713 | } | |||
714 | if (aForWrite) { | |||
715 | if (!fPreferredTx) { | |||
716 | fPreferredTx=typecfgP; // set it | |||
717 | fPrefTxVariantDescP=variantDescP; | |||
718 | } | |||
719 | } | |||
720 | } // if preferred | |||
721 | else { | |||
722 | // - additional | |||
723 | TAdditionalDataType adt; | |||
724 | adt.datatypeconfig=typecfgP; | |||
725 | adt.forRead=aForRead; | |||
726 | adt.forWrite=aForWrite; | |||
727 | adt.variantDescP=variantDescP; // variant of that type | |||
728 | #ifndef NO_REMOTE_RULES | |||
729 | if (aRuleMatch) { | |||
730 | // this is a rulematch type (which overrides normal type selection mechanism) | |||
731 | AssignString(atd.remoteRuleMatch,aRuleMatch); // remote rule match string | |||
732 | fRuleMatchTypes.push_back(adt); // save it in the list | |||
733 | } | |||
734 | else | |||
735 | #endif | |||
736 | { | |||
737 | // standard type | |||
738 | fAdditionalTypes.push_back(adt); // save it in the list | |||
739 | } | |||
740 | } | |||
741 | return true; | |||
742 | } // TTypeSupportConfig::addTypeSupport | |||
743 | ||||
744 | #else | |||
745 | ||||
746 | // config element parsing | |||
747 | bool TTypeSupportConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) | |||
748 | { | |||
749 | // checking the elements | |||
750 | if (strucmp(aElementName,"use")==0) { | |||
751 | expectEmpty(); // datatype usage specs may not have | |||
752 | // process arguments | |||
753 | const char* nam = getAttr(aAttributes,"datatype"); | |||
754 | if (!nam) | |||
755 | return fail("use must have 'datatype' attribute"); | |||
756 | // search datatype | |||
757 | TDataTypeConfig *typecfgP = | |||
758 | static_cast<TRootConfig *>(getRootElement())->fDatatypesConfigP->getDataType(nam); | |||
759 | if (!typecfgP) | |||
760 | return fail("unknown datatype '%s' specified",nam); | |||
761 | #ifndef NO_REMOTE_RULES | |||
762 | // get rulematch string, if any | |||
763 | cAppCharP ruleMatch = getAttr(aAttributes,"rulematch"); | |||
764 | #endif | |||
765 | // convert variant | |||
766 | TTypeVariantDescriptor variantDescP=NULL__null; // no variant descriptor by default | |||
767 | cAppCharP variant = getAttr(aAttributes,"variant"); | |||
768 | if (variant) { | |||
769 | // get a type-specific descriptor which describes the variant of a type to be used with this datastore | |||
770 | variantDescP = typecfgP->getVariantDescriptor(variant); | |||
771 | if (!variantDescP) | |||
772 | return fail("unknown variant '%s' specified",variant); | |||
773 | } | |||
774 | // convert mode | |||
775 | bool rd=true,wr=true; | |||
776 | const char* mode = getAttr(aAttributes,"mode"); | |||
777 | if (mode) { | |||
778 | rd=false; | |||
779 | wr=false; | |||
780 | while (*mode) { | |||
781 | if (tolower(*mode)=='r') rd=true; | |||
782 | else if (tolower(*mode)=='w') wr=true; | |||
783 | else { | |||
784 | ReportError(true,"invalid mode '%c'",*mode); | |||
785 | return true; | |||
786 | } | |||
787 | // next char | |||
788 | mode++; | |||
789 | } | |||
790 | if (!rd && !wr) | |||
791 | return fail("mode must specify 'r', 'w' or 'rw' at least"); | |||
792 | } | |||
793 | // get preferred | |||
794 | bool preferred=false; | |||
795 | const char* pref = getAttr(aAttributes,"preferred"); | |||
796 | if (pref) { | |||
797 | if (!StrToBool(pref, preferred)) { | |||
798 | if (strucmp(pref,"legacy")==0) { | |||
799 | // this is the preferred type for blind and legacy mode sync attempts | |||
800 | fPreferredLegacy=typecfgP; // remember (note that there is only ONE preferred type, mode is ignored) | |||
801 | preferred=false; // not officially preferred | |||
802 | } | |||
803 | else | |||
804 | return fail("bad value for 'preferred'"); | |||
805 | } | |||
806 | } | |||
807 | // now add datatype | |||
808 | if (preferred) { | |||
809 | // - preferred | |||
810 | if (rd) { | |||
811 | if (fPreferredRx) | |||
812 | return fail("preferred read type already defined"); | |||
813 | else { | |||
814 | fPreferredRx=typecfgP; // set it | |||
815 | fPrefRxVariantDescP=variantDescP; | |||
816 | } | |||
817 | } | |||
818 | if (wr) { | |||
819 | if (fPreferredTx) | |||
820 | return fail("preferred write type already defined"); | |||
821 | else { | |||
822 | fPreferredTx=typecfgP; // set it | |||
823 | fPrefTxVariantDescP=variantDescP; | |||
824 | } | |||
825 | } | |||
826 | } // if preferred | |||
827 | else { | |||
828 | // - additional | |||
829 | TAdditionalDataType adt; | |||
830 | adt.datatypeconfig=typecfgP; | |||
831 | adt.forRead=rd; | |||
832 | adt.forWrite=wr; | |||
833 | adt.variantDescP=variantDescP; | |||
834 | #ifndef NO_REMOTE_RULES | |||
835 | if (ruleMatch) { | |||
836 | // this is a rulematch type (which overrides normal type selection mechanism) | |||
837 | AssignString(adt.remoteRuleMatch,ruleMatch); // remote rule match string | |||
838 | fRuleMatchTypes.push_back(adt); // save it in the list | |||
839 | } | |||
840 | else | |||
841 | #endif | |||
842 | { | |||
843 | // standard type | |||
844 | fAdditionalTypes.push_back(adt); // save it in the list | |||
845 | } | |||
846 | } | |||
847 | } | |||
848 | // - none known here | |||
849 | else | |||
850 | return inherited::localStartElement(aElementName,aAttributes,aLine); | |||
851 | // ok | |||
852 | return true; | |||
853 | } // TTypeSupportConfig::localStartElement | |||
854 | ||||
855 | #endif | |||
856 | ||||
857 | ||||
858 | // resolve | |||
859 | void TTypeSupportConfig::localResolve(bool aLastPass) | |||
860 | { | |||
861 | #ifndef HARDCODED_CONFIG | |||
862 | if (aLastPass) { | |||
863 | // check for required settings | |||
864 | if (!fPreferredTx || !fPreferredRx) | |||
865 | SYSYNC_THROW(TConfigParseException("'typesupport' must contain at least one preferred type for read and write"))throw TConfigParseException("'typesupport' must contain at least one preferred type for read and write" ); | |||
866 | } | |||
867 | #endif | |||
868 | // resolve inherited | |||
869 | inherited::localResolve(aLastPass); | |||
870 | } // TTypeSupportConfig::localResolve | |||
871 | ||||
872 | ||||
873 | ||||
874 | ||||
875 | // datastore config | |||
876 | TLocalDSConfig::TLocalDSConfig(const char* aName, TConfigElement *aParentElement) : | |||
877 | TConfigElement(aName,aParentElement), | |||
878 | fTypeSupport("typesupport",this) | |||
879 | { | |||
880 | clear(); | |||
881 | } // TLocalDSConfig::TLocalDSConfig | |||
882 | ||||
883 | ||||
884 | TLocalDSConfig::~TLocalDSConfig() | |||
885 | { | |||
886 | // nop so far | |||
887 | } // TLocalDSConfig::~TLocalDSConfig | |||
888 | ||||
889 | ||||
890 | // init defaults | |||
891 | void TLocalDSConfig::clear(void) | |||
892 | { | |||
893 | // init defaults | |||
894 | // - conflict resolution strategy | |||
895 | fConflictStrategy=cr_newer_wins; | |||
896 | fSlowSyncStrategy=cr_newer_wins; | |||
897 | fFirstTimeStrategy=cr_newer_wins; | |||
898 | // options | |||
899 | fLocalDBTypeID=0; | |||
900 | fReadOnly=false; | |||
901 | fCanRestart=false; | |||
902 | fReportUpdates=true; | |||
903 | fDeleteWins=false; // replace wins over delete by default | |||
904 | fResendFailing=true; // resend failing items in next session by default | |||
905 | #ifdef SYSYNC_SERVER1 | |||
906 | fTryUpdateDeleted=false; // no attempt to update already deleted items (assuming they are invisible only) | |||
907 | fAlwaysSendLocalID=false; // off as it used to be not SCTS conformant (but would give clients chances to remap IDs) | |||
908 | #endif | |||
909 | fMaxItemsPerMessage=0; // no limit | |||
910 | #ifdef OBJECT_FILTERING1 | |||
911 | // - filters | |||
912 | fRemoteAcceptFilter.erase(); | |||
913 | fSilentlyDiscardUnaccepted=false; | |||
914 | fLocalDBFilterConf.erase(); | |||
915 | fMakePassFilter.erase(); | |||
916 | fInvisibleFilter.erase(); | |||
917 | fMakeVisibleFilter.erase(); | |||
918 | // - DS 1.2 Filter support (<filter> allowed in Alert, <filter-rx>/<filterCap> shown in devInf) | |||
919 | fDS12FilterSupport=false; // off by default, as clients usually don't have it | |||
920 | // - Set if date range support is available in this datastore | |||
921 | fDateRangeSupported=false; | |||
922 | #endif | |||
923 | #ifdef SCRIPT_SUPPORT1 | |||
924 | fDBInitScript.erase(); | |||
925 | fSentItemStatusScript.erase(); | |||
926 | fReceivedItemStatusScript.erase(); | |||
927 | fAlertScript.erase(); | |||
928 | #ifdef SYSYNC_CLIENT1 | |||
929 | fAlertPrepScript.erase(); | |||
930 | #endif | |||
931 | fDBFinishScript.erase(); | |||
932 | #endif | |||
933 | // clear embedded | |||
934 | fTypeSupport.clear(); | |||
935 | // clear inherited | |||
936 | inherited::clear(); | |||
937 | } // TLocalDSConfig::clear | |||
938 | ||||
939 | ||||
940 | #ifndef HARDCODED_CONFIG | |||
941 | ||||
942 | // config element parsing | |||
943 | bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) | |||
944 | { | |||
945 | // checking the elements | |||
946 | if (strucmp(aElementName,"dbtypeid")==0) | |||
947 | expectUInt32(fLocalDBTypeID); | |||
948 | else if (strucmp(aElementName,"typesupport")==0) | |||
949 | expectChildParsing(fTypeSupport); | |||
950 | else if (strucmp(aElementName,"conflictstrategy")==0) | |||
951 | expectEnum(sizeof(fConflictStrategy),&fConflictStrategy,conflictStrategyNames,numConflictStrategies); | |||
952 | else if (strucmp(aElementName,"slowsyncstrategy")==0) | |||
953 | expectEnum(sizeof(fSlowSyncStrategy),&fSlowSyncStrategy,conflictStrategyNames,numConflictStrategies); | |||
954 | else if (strucmp(aElementName,"firsttimestrategy")==0) | |||
955 | expectEnum(sizeof(fFirstTimeStrategy),&fFirstTimeStrategy,conflictStrategyNames,numConflictStrategies); | |||
956 | else if (strucmp(aElementName,"readonly")==0) | |||
957 | expectBool(fReadOnly); | |||
958 | else if (strucmp(aElementName,"canrestart")==0) | |||
959 | expectBool(fCanRestart); | |||
960 | else if (strucmp(aElementName,"syncmode")==0) { | |||
961 | if (!fSyncModeBuffer.empty()) { | |||
962 | fSyncModes.insert(fSyncModeBuffer); | |||
963 | fSyncModeBuffer.clear(); | |||
964 | } | |||
965 | expectString(fSyncModeBuffer); | |||
966 | } else if (strucmp(aElementName,"reportupdates")==0) | |||
967 | expectBool(fReportUpdates); | |||
968 | else if (strucmp(aElementName,"deletewins")==0) | |||
969 | expectBool(fDeleteWins); | |||
970 | else if (strucmp(aElementName,"resendfailing")==0) | |||
971 | expectBool(fResendFailing); | |||
972 | #ifdef SYSYNC_SERVER1 | |||
973 | else if (strucmp(aElementName,"tryupdatedeleted")==0) | |||
974 | expectBool(fTryUpdateDeleted); | |||
975 | else if (strucmp(aElementName,"alwayssendlocalid")==0) | |||
976 | expectBool(fAlwaysSendLocalID); | |||
977 | else if (strucmp(aElementName,"alias")==0) { | |||
978 | // get a name | |||
979 | string name; | |||
980 | if (!getAttrExpanded(aAttributes, "name", name, true)) | |||
981 | return fail("Missing 'name' attribute in 'alias'"); | |||
982 | fAliasNames.push_back(name); | |||
983 | expectEmpty(); | |||
984 | } | |||
985 | #endif | |||
986 | else if (strucmp(aElementName,"maxitemspermessage")==0) | |||
987 | expectUInt32(fMaxItemsPerMessage); | |||
988 | #ifdef OBJECT_FILTERING1 | |||
989 | // filtering | |||
990 | else if (strucmp(aElementName,"acceptfilter")==0) | |||
991 | expectString(fRemoteAcceptFilter); | |||
992 | else if (strucmp(aElementName,"silentdiscard")==0) | |||
993 | expectBool(fSilentlyDiscardUnaccepted); | |||
994 | else if (strucmp(aElementName,"localdbfilter")==0) | |||
995 | expectString(fLocalDBFilterConf); | |||
996 | else if (strucmp(aElementName,"makepassfilter")==0) | |||
997 | expectString(fMakePassFilter); | |||
998 | else if (strucmp(aElementName,"invisiblefilter")==0) | |||
999 | expectString(fInvisibleFilter); | |||
1000 | else if (strucmp(aElementName,"makevisiblefilter")==0) | |||
1001 | expectString(fMakeVisibleFilter); | |||
1002 | else if (strucmp(aElementName,"ds12filters")==0) | |||
1003 | expectBool(fDS12FilterSupport); | |||
1004 | else if (strucmp(aElementName,"daterangesupport")==0) | |||
1005 | expectBool(fDateRangeSupported); | |||
1006 | #endif | |||
1007 | #ifdef SCRIPT_SUPPORT1 | |||
1008 | else if (strucmp(aElementName,"datastoreinitscript")==0) | |||
1009 | expectScript(fDBInitScript,aLine,&DBFuncTable); | |||
1010 | else if (strucmp(aElementName,"sentitemstatusscript")==0) | |||
1011 | expectScript(fSentItemStatusScript,aLine,&ErrorFuncTable); | |||
1012 | else if (strucmp(aElementName,"receiveditemstatusscript")==0) | |||
1013 | expectScript(fReceivedItemStatusScript,aLine,&ErrorFuncTable); | |||
1014 | else if (strucmp(aElementName,"alertscript")==0) | |||
1015 | expectScript(fAlertScript,aLine,&DBFuncTable); | |||
1016 | #ifdef SYSYNC_CLIENT1 | |||
1017 | else if (strucmp(aElementName,"alertprepscript")==0) | |||
1018 | expectScript(fAlertPrepScript,aLine,getClientDBFuncTable()); | |||
1019 | #endif | |||
1020 | else if (strucmp(aElementName,"datastorefinishscript")==0) | |||
1021 | expectScript(fDBFinishScript,aLine,&DBFuncTable); | |||
1022 | #endif | |||
1023 | #ifndef MINIMAL_CODE | |||
1024 | else if (strucmp(aElementName,"displayname")==0) | |||
1025 | expectString(fDisplayName); | |||
1026 | #endif | |||
1027 | // - none known here | |||
1028 | else | |||
1029 | return inherited::localStartElement(aElementName,aAttributes,aLine); | |||
1030 | // ok | |||
1031 | return true; | |||
1032 | } // TLocalDSConfig::localStartElement | |||
1033 | ||||
1034 | #endif | |||
1035 | ||||
1036 | // resolve | |||
1037 | void TLocalDSConfig::localResolve(bool aLastPass) | |||
1038 | { | |||
1039 | if (!fSyncModeBuffer.empty()) { | |||
1040 | fSyncModes.insert(fSyncModeBuffer); | |||
1041 | fSyncModeBuffer.clear(); | |||
1042 | } | |||
1043 | ||||
1044 | if (aLastPass) { | |||
1045 | #ifdef SCRIPT_SUPPORT1 | |||
1046 | TScriptContext *sccP = NULL__null; | |||
1047 | SYSYNC_TRYtry { | |||
1048 | // resolve all scripts in same context | |||
1049 | // - first script needed (when alert is created) | |||
1050 | #ifdef SYSYNC_CLIENT1 | |||
1051 | TScriptContext::resolveScript(getSyncAppBase(),fAlertPrepScript,sccP,NULL__null); | |||
1052 | #endif | |||
1053 | // - scripts needed when database is made ready | |||
1054 | TScriptContext::resolveScript(getSyncAppBase(),fDBInitScript,sccP,NULL__null); | |||
1055 | TScriptContext::resolveScript(getSyncAppBase(),fSentItemStatusScript,sccP,NULL__null); | |||
1056 | TScriptContext::resolveScript(getSyncAppBase(),fReceivedItemStatusScript,sccP,NULL__null); | |||
1057 | TScriptContext::resolveScript(getSyncAppBase(),fAlertScript,sccP,NULL__null); | |||
1058 | TScriptContext::resolveScript(getSyncAppBase(),fDBFinishScript,sccP,NULL__null); | |||
1059 | // - forget this context | |||
1060 | if (sccP) delete sccP; | |||
1061 | } | |||
1062 | SYSYNC_CATCH (...)catch(...) { | |||
1063 | if (sccP) delete sccP; | |||
1064 | SYSYNC_RETHROWthrow; | |||
1065 | SYSYNC_ENDCATCH} | |||
1066 | #endif | |||
1067 | } | |||
1068 | // resolve embedded | |||
1069 | fTypeSupport.Resolve(aLastPass); | |||
1070 | // resolve inherited | |||
1071 | inherited::localResolve(aLastPass); | |||
1072 | } // TLocalDSConfig::localResolve | |||
1073 | ||||
1074 | ||||
1075 | // - add type support to datatstore from config | |||
1076 | void TLocalDSConfig::addTypes(TLocalEngineDS *aDatastore, TSyncSession *aSessionP) | |||
1077 | { | |||
1078 | TSyncItemType *typeP; | |||
1079 | TSyncItemType *writetypeP; | |||
1080 | ||||
1081 | // preferred types, create instances only if not already existing | |||
1082 | // - preferred receive | |||
1083 | typeP = aSessionP->findLocalType(fTypeSupport.fPreferredRx); | |||
1084 | if (!typeP) { | |||
1085 | typeP = fTypeSupport.fPreferredRx->newSyncItemType(aSessionP,NULL__null); // local types are never exclusively related to a datastore | |||
1086 | aSessionP->addLocalItemType(typeP); // add to session | |||
1087 | } | |||
1088 | // - preferred send | |||
1089 | writetypeP = aSessionP->findLocalType(fTypeSupport.fPreferredTx); | |||
1090 | if (!writetypeP) { | |||
1091 | writetypeP = fTypeSupport.fPreferredTx->newSyncItemType(aSessionP,NULL__null); // local types are never exclusively related to a datastore | |||
1092 | aSessionP->addLocalItemType(writetypeP); | |||
1093 | } | |||
1094 | // - set preferred types | |||
1095 | aDatastore->setPreferredTypes(typeP,writetypeP); | |||
1096 | // additional types | |||
1097 | TAdditionalTypesList::iterator pos; | |||
1098 | for (pos=fTypeSupport.fAdditionalTypes.begin(); pos!=fTypeSupport.fAdditionalTypes.end(); pos++) { | |||
1099 | // - search for type already created from same config item | |||
1100 | typeP = aSessionP->findLocalType((*pos).datatypeconfig); | |||
1101 | if (!typeP) { | |||
1102 | // - does not exist yet, create the type | |||
1103 | typeP = (*pos).datatypeconfig->newSyncItemType(aSessionP,NULL__null); // local types are never exclusively related to a datastore | |||
1104 | // - add type to session types | |||
1105 | aSessionP->addLocalItemType(typeP); | |||
1106 | } | |||
1107 | // - add type to datastore's supported types | |||
1108 | aDatastore->addTypeSupport(typeP,(*pos).forRead,(*pos).forWrite); | |||
1109 | } | |||
1110 | #ifndef NO_REMOTE_RULES | |||
1111 | // rulematch types | |||
1112 | for (pos=fTypeSupport.fRuleMatchTypes.begin(); pos!=fTypeSupport.fRuleMatchTypes.end(); pos++) { | |||
1113 | // - search for type already created from same config item | |||
1114 | typeP = aSessionP->findLocalType((*pos).datatypeconfig); | |||
1115 | if (!typeP) { | |||
1116 | // - does not exist yet, create the type | |||
1117 | typeP = (*pos).datatypeconfig->newSyncItemType(aSessionP,NULL__null); // local types are never exclusively related to a datastore | |||
1118 | // - add type to session types | |||
1119 | aSessionP->addLocalItemType(typeP); | |||
1120 | } | |||
1121 | // - add type to datastore's rulematch types | |||
1122 | aDatastore->addRuleMatchTypeSupport(typeP,(*pos).remoteRuleMatch.c_str()); | |||
1123 | } | |||
1124 | #endif | |||
1125 | // now apply type limits | |||
1126 | // Note: this is usually derived, as limits are often defined within the datastore, | |||
1127 | // not the type itself (however, for hardcoded template-based fieldlists, | |||
1128 | // the limits are taken from the template, see TLocalDSConfig::addTypeLimits() | |||
1129 | addTypeLimits(aDatastore, aSessionP); | |||
1130 | } // TLocalDSConfig::addTypes | |||
1131 | ||||
1132 | ||||
1133 | // Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types | |||
1134 | void TLocalDSConfig::addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP) | |||
1135 | { | |||
1136 | // add field size limitations from map to all types | |||
1137 | TSyncItemTypePContainer::iterator pos; | |||
1138 | TSyncItemTypePContainer *typesP = &(aLocalDatastoreP->fRxItemTypes); | |||
1139 | for (uInt8 i=0; i<2; i++) { | |||
1140 | for (pos=typesP->begin(); pos!=typesP->end(); pos++) { | |||
1141 | // apply default limits to type (e.g. from hard-coded template in config) | |||
1142 | (*pos)->addDefaultTypeLimits(); | |||
1143 | } | |||
1144 | typesP = &(aLocalDatastoreP->fTxItemTypes); | |||
1145 | } | |||
1146 | } // TLocalDSConfig::addTypeLimits | |||
1147 | ||||
1148 | ||||
1149 | // Check for alias names | |||
1150 | uInt16 TLocalDSConfig::isDatastoreAlias(cAppCharP aDatastoreURI) | |||
1151 | { | |||
1152 | // only servers have (and may need) aliases | |||
1153 | #ifdef SYSYNC_SERVER1 | |||
1154 | for (TStringList::iterator pos = fAliasNames.begin(); pos!=fAliasNames.end(); pos++) { | |||
1155 | if (*pos == aDatastoreURI) | |||
1156 | return (*pos).size(); // return size of match | |||
1157 | } | |||
1158 | #endif | |||
1159 | return 0; | |||
1160 | } // TLocalDSConfig::isDatastoreAlias | |||
1161 | ||||
1162 | ||||
1163 | ||||
1164 | ||||
1165 | /* | |||
1166 | * Implementation of TLocalEngineDS | |||
1167 | */ | |||
1168 | ||||
1169 | ||||
1170 | /// @Note InternalResetDataStore() must also be callable from destructor | |||
1171 | /// (care not to call other objects which will refer to the already | |||
1172 | /// half-destructed datastore!) | |||
1173 | void TLocalEngineDS::InternalResetDataStore(void) | |||
1174 | { | |||
1175 | // possibly complete, if not already done (should be, by engFinishDataStoreSync() !) | |||
1176 | if (fLocalDSState>dssta_idle) | |||
1177 | changeState(dssta_completed); // complete NOW, opportunity to show stats, etc. | |||
1178 | // switch down to idle | |||
1179 | changeState(dssta_idle); | |||
1180 | /// @todo obsolete: fState=dss_idle; | |||
1181 | fAbortStatusCode=LOCERR_OK; // not aborted yet | |||
1182 | fLocalAbortCause=true; // assume local cause | |||
1183 | fRemoteAddingStopped=false; | |||
1184 | fAlertCode=0; // not yet alerted | |||
1185 | ||||
1186 | /// Init Sync mode @ref dsSyncMode | |||
1187 | fSyncMode=smo_twoway; // default to twoway | |||
1188 | fForceSlowSync=false; | |||
1189 | fSlowSync=false; | |||
1190 | fRefreshOnly=false; | |||
1191 | fCacheData=false; | |||
1192 | fReadOnly=false; | |||
1193 | fReportUpdates=fDSConfigP->fReportUpdates; // update reporting according to what is configured | |||
1194 | fCanRestart=fDSConfigP->fCanRestart; | |||
1195 | fSyncModes=fDSConfigP->fSyncModes; | |||
1196 | fServerAlerted=false; | |||
1197 | fResuming=false; | |||
1198 | #ifdef SUPERDATASTORES1 | |||
1199 | fAsSubDatastoreOf=NULL__null; // is not a subdatastore | |||
1200 | #endif | |||
1201 | ||||
1202 | ||||
1203 | /// Init administrative data to defaults @ref dsAdminData | |||
1204 | // - last | |||
1205 | fLastRemoteAnchor.erase(); | |||
1206 | fLastLocalAnchor.erase(); | |||
1207 | // - current | |||
1208 | fNextRemoteAnchor.erase(); | |||
1209 | fNextLocalAnchor.erase(); | |||
1210 | // suspend state | |||
1211 | fResumeAlertCode=0; // none | |||
1212 | fPreventResuming=false; | |||
1213 | // suspend of chunked items | |||
1214 | fPartialItemState=pi_state_none; | |||
1215 | fLastSourceURI.erase(); | |||
1216 | fLastTargetURI.erase(); | |||
1217 | fLastItemStatus=0; | |||
1218 | fPITotalSize=0; | |||
1219 | fPIStoredSize=0; | |||
1220 | fPIUnconfirmedSize=0; | |||
1221 | if (fPIStoredDataAllocated) { | |||
1222 | smlLibFree(fPIStoredDataP); | |||
1223 | fPIStoredDataAllocated=false; | |||
1224 | } | |||
1225 | fPIStoredDataP=NULL__null; | |||
1226 | ||||
1227 | // other state info | |||
1228 | fFirstTimeSync=false; // not first sync by default | |||
1229 | ||||
1230 | #ifdef SYSYNC_CLIENT1 | |||
1231 | // - maps for add commands | |||
1232 | fPendingAddMaps.clear(); | |||
1233 | fUnconfirmedMaps.clear(); | |||
1234 | fLastSessionMaps.clear(); | |||
1235 | #endif | |||
1236 | #ifdef SYSYNC_SERVER1 | |||
1237 | PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: removing %ld items", ( long)fTempGUIDMap.size() ); } | |||
1238 | "fTempGUIDMap: removing %ld items", (long)fTempGUIDMap.size(){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: removing %ld items", ( long)fTempGUIDMap.size() ); } | |||
1239 | )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: removing %ld items", ( long)fTempGUIDMap.size() ); }; | |||
1240 | fTempGUIDMap.clear(); | |||
1241 | #endif | |||
1242 | ||||
1243 | /// Init type negotiation | |||
1244 | /// - for sending data | |||
1245 | fLocalSendToRemoteTypeP = NULL__null; | |||
1246 | fRemoteReceiveFromLocalTypeP = NULL__null; | |||
1247 | /// - for receiving data | |||
1248 | fLocalReceiveFromRemoteTypeP = NULL__null; | |||
1249 | fRemoteSendToLocalTypeP = NULL__null; | |||
1250 | ||||
1251 | /// Init Filtering @ref dsFiltering | |||
1252 | resetFiltering(); | |||
1253 | ||||
1254 | /// Init item processing @ref dsItemProcessing | |||
1255 | fSessionConflictStrategy=cr_duplicate; // will be updated later when sync mode is known | |||
1256 | fItemSizeLimit=-1; // no limit yet | |||
1257 | fCurrentSyncOp = sop_none; // will be set at engProcessItem() | |||
1258 | fEchoItemOp = sop_none; // will be set at engProcessItem() | |||
1259 | fItemConflictStrategy=fSessionConflictStrategy; // will be set at engProcessItem() | |||
1260 | fForceConflict = false; // will be set at engProcessItem() | |||
1261 | fDeleteWins = false; // will be set at engProcessItem() | |||
1262 | fRejectStatus = -1; // will be set at engProcessItem() | |||
1263 | #ifdef SCRIPT_SUPPORT1 | |||
1264 | // - delete the script context if any | |||
1265 | if (fSendingTypeScriptContextP) { | |||
1266 | delete fSendingTypeScriptContextP; | |||
1267 | fSendingTypeScriptContextP=NULL__null; | |||
1268 | } | |||
1269 | if (fReceivingTypeScriptContextP) { | |||
1270 | delete fReceivingTypeScriptContextP; | |||
1271 | fReceivingTypeScriptContextP=NULL__null; | |||
1272 | } | |||
1273 | if (fDataStoreScriptContextP) { | |||
1274 | delete fDataStoreScriptContextP; | |||
1275 | fDataStoreScriptContextP=NULL__null; | |||
1276 | } | |||
1277 | #endif | |||
1278 | ||||
1279 | /// Init other vars @ref dsOther | |||
1280 | ||||
1281 | ||||
1282 | /// Init Counters and statistics @ref dsCountStats | |||
1283 | // - NOC from remote | |||
1284 | fRemoteNumberOfChanges=-1; // none known yet | |||
1285 | // - data transferred | |||
1286 | fIncomingDataBytes=0; | |||
1287 | fOutgoingDataBytes=0; | |||
1288 | // - locally performed ops | |||
1289 | fLocalItemsAdded=0; | |||
1290 | fLocalItemsUpdated=0; | |||
1291 | fLocalItemsDeleted=0; | |||
1292 | fLocalItemsError=0; | |||
1293 | // - remotely performed ops | |||
1294 | fRemoteItemsAdded=0; | |||
1295 | fRemoteItemsUpdated=0; | |||
1296 | fRemoteItemsDeleted=0; | |||
1297 | fRemoteItemsError=0; | |||
1298 | #ifdef SYSYNC_SERVER1 | |||
1299 | // - conflicts | |||
1300 | fConflictsServerWins=0; | |||
1301 | fConflictsClientWins=0; | |||
1302 | fConflictsDuplicated=0; | |||
1303 | // - slow sync matches | |||
1304 | fSlowSyncMatches=0; | |||
1305 | #endif | |||
1306 | ||||
1307 | // Override defaults from ancestor | |||
1308 | // - generally, limit GUID size to reasonable size (even if we can | |||
1309 | // theoretically handle unlimited GUIDs in client, except for | |||
1310 | // some DEBUGPRINTF statements that will crash for example with | |||
1311 | // the brain-damaged GUIDs that Exchange server uses. | |||
1312 | fMaxGUIDSize = 64; | |||
1313 | } // TLocalEngineDS::InternalResetDataStore | |||
1314 | ||||
1315 | ||||
1316 | /// constructor | |||
1317 | TLocalEngineDS::TLocalEngineDS(TLocalDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask) : | |||
1318 | TSyncDataStore(aSessionP, aName, aCommonSyncCapMask) | |||
1319 | ,fPIStoredDataP(NULL__null) | |||
1320 | ,fPIStoredDataAllocated(false) | |||
1321 | #ifdef SCRIPT_SUPPORT1 | |||
1322 | ,fSendingTypeScriptContextP(NULL__null) // no associated script context | |||
1323 | ,fReceivingTypeScriptContextP(NULL__null) // no associated script context | |||
1324 | ,fDataStoreScriptContextP(NULL__null) // no datastore level context | |||
1325 | #endif | |||
1326 | ,fRemoteDatastoreP(NULL__null) // no associated remote | |||
1327 | { | |||
1328 | // set config ptr | |||
1329 | fDSConfigP = aDSConfigP; | |||
1330 | if (!fDSConfigP) | |||
1331 | SYSYNC_THROW(TSyncException(DEBUGTEXT("TLocalEngineDS::TLocalEngineDS called with NULL config","lds1")))throw TSyncException("TLocalEngineDS::TLocalEngineDS called with NULL config" ); | |||
1332 | /// Init Sync state @ref dsSyncState | |||
1333 | fLocalDSState=dssta_idle; // idle to begin with | |||
1334 | // now reset | |||
1335 | InternalResetDataStore(); | |||
1336 | } // TLocalEngineDS::TLocalEngineDS | |||
1337 | ||||
1338 | ||||
1339 | ||||
1340 | ||||
1341 | TLocalEngineDS::~TLocalEngineDS() | |||
1342 | { | |||
1343 | // reset everything | |||
1344 | InternalResetDataStore(); | |||
1345 | } // TLocalEngineDS::~TLocalEngineDS | |||
1346 | ||||
1347 | ||||
1348 | #ifdef SYDEBUG2 | |||
1349 | ||||
1350 | // return datastore state name | |||
1351 | cAppCharP TLocalEngineDS::getDSStateName(void) | |||
1352 | { | |||
1353 | return LocalDSStateNames[fLocalDSState]; | |||
1354 | } // TLocalEngineDS::getDSStateName | |||
1355 | ||||
1356 | ||||
1357 | // return datastore state name | |||
1358 | cAppCharP TLocalEngineDS::getDSStateName(TLocalEngineDSState aState) | |||
1359 | { | |||
1360 | return LocalDSStateNames[aState]; | |||
1361 | } // TLocalEngineDS::getDSStateName | |||
1362 | ||||
1363 | #endif | |||
1364 | ||||
1365 | // reset datastore (after use) | |||
1366 | void TLocalEngineDS::engResetDataStore(void) | |||
1367 | { | |||
1368 | // now reset | |||
1369 | // - logic layer and above | |||
1370 | dsResetDataStore(); | |||
1371 | // - myself | |||
1372 | InternalResetDataStore(); | |||
1373 | // - anchestors | |||
1374 | inherited::engResetDataStore(); | |||
1375 | } // TLocalEngineDS::engResetDataStore | |||
1376 | ||||
1377 | ||||
1378 | ||||
1379 | // check if this datastore is accessible with given URI | |||
1380 | // NOTE: By default, local datastore type is addressed with | |||
1381 | // first path element of URI, rest of path might be used | |||
1382 | // by derivates to subselect data folders etc. | |||
1383 | uInt16 TLocalEngineDS::isDatastore(const char *aDatastoreURI) | |||
1384 | { | |||
1385 | // extract base name | |||
1386 | string basename; | |||
1387 | analyzeName(aDatastoreURI,&basename); | |||
1388 | // compare only base name | |||
1389 | // - compare with main name | |||
1390 | int res = inherited::isDatastore(basename.c_str()); | |||
1391 | if (res==0) { | |||
1392 | // Not main name: compare with aliases | |||
1393 | res = fDSConfigP->isDatastoreAlias(basename.c_str()); | |||
1394 | } | |||
1395 | return res; | |||
1396 | } // TLocalEngineDS::isDatastore | |||
1397 | ||||
1398 | ||||
1399 | ||||
1400 | /// get DB specific error message text for dbg log, or empty string if none | |||
1401 | /// @return platform specific DB error text | |||
1402 | string TLocalEngineDS::lastDBErrorText(void) | |||
1403 | { | |||
1404 | string s; | |||
1405 | s.erase(); | |||
1406 | uInt32 err = lastDBError(); | |||
1407 | if (isDBError(err)) { | |||
1408 | StringObjPrintf(s," (DB specific error code = %ld)",(long)lastDBError()); | |||
1409 | } | |||
1410 | return s; | |||
1411 | } // TLocalEngineDS::lastDBErrorText | |||
1412 | ||||
1413 | ||||
1414 | #ifdef SYSYNC_CLIENT1 | |||
1415 | ||||
1416 | // - init Sync Parameters (client case) | |||
1417 | // Derivates might override this to pre-process and modify parameters | |||
1418 | // (such as adding client settings as CGI to remoteDBPath) | |||
1419 | bool TLocalEngineDS::dsSetClientSyncParams( | |||
1420 | TSyncModes aSyncMode, | |||
1421 | bool aSlowSync, | |||
1422 | const char *aRemoteDBPath, | |||
1423 | const char *aDBUser, | |||
1424 | const char *aDBPassword, | |||
1425 | const char *aLocalPathExtension, | |||
1426 | const char *aRecordFilterQuery, | |||
1427 | bool aFilterInclusive | |||
1428 | ) | |||
1429 | { | |||
1430 | // - set remote params | |||
1431 | fRemoteDBPath=aRemoteDBPath; | |||
1432 | AssignString(fDBUser,aDBUser); | |||
1433 | AssignString(fDBPassword,aDBPassword); | |||
1434 | // check for running under control of a superdatastore | |||
1435 | // - aRemoteDBPath might contain a special prefix: "<super>remote", with "super" specifying the | |||
1436 | // name of a local superdatastore to run the sync with | |||
1437 | string opts; | |||
1438 | if (!fRemoteDBPath.empty() && fRemoteDBPath.at(0)=='<') { | |||
1439 | // we have an option prefix | |||
1440 | size_t pfxe = fRemoteDBPath.find('>', 1); | |||
1441 | if (pfxe!=string::npos) { | |||
1442 | // extract options | |||
1443 | opts.assign(fRemoteDBPath, 1, pfxe-1); | |||
1444 | // store remote path cleaned from options | |||
1445 | fRemoteDBPath.erase(0,pfxe+1); | |||
1446 | } | |||
1447 | } | |||
1448 | if (!opts.empty()) { | |||
1449 | #ifdef SUPERDATASTORES1 | |||
1450 | // For now, the only option withing angle brackets is the name of the superdatastore, so opts==superdatastorename | |||
1451 | // - look for superdatastore having the specified name | |||
1452 | TSuperDSConfig *superdscfgP = static_cast<TSuperDSConfig *>(getSession()->getSessionConfig()->getLocalDS(opts.c_str())); | |||
1453 | if (superdscfgP && superdscfgP->isAbstractDatastore()) { | |||
1454 | // see if we have an instance of this already | |||
1455 | fAsSubDatastoreOf = static_cast<TSuperDataStore *>(getSession()->findLocalDataStore(superdscfgP)); | |||
1456 | if (fAsSubDatastoreOf) { | |||
1457 | // that superdatastore already exists, just override client sync params with those already set | |||
1458 | aSyncMode = fAsSubDatastoreOf->fSyncMode; | |||
1459 | aSlowSync = fAsSubDatastoreOf->fSlowSync; | |||
1460 | aRecordFilterQuery = fAsSubDatastoreOf->fRemoteRecordFilterQuery.c_str(); | |||
1461 | } | |||
1462 | else { | |||
1463 | // instantiate new superdatastore | |||
1464 | fAsSubDatastoreOf = static_cast<TSuperDataStore *>(superdscfgP->newLocalDataStore(getSession())); | |||
1465 | if (fAsSubDatastoreOf) { | |||
1466 | fSessionP->fLocalDataStores.push_back(fAsSubDatastoreOf); | |||
1467 | // configure it with the same parameters as the subdatastore | |||
1468 | if (!fAsSubDatastoreOf->dsSetClientSyncParams( | |||
1469 | aSyncMode, | |||
1470 | aSlowSync, | |||
1471 | fRemoteDBPath.c_str(), // already cleaned from <xxx> prefix | |||
1472 | aDBUser, | |||
1473 | aDBPassword, | |||
1474 | aLocalPathExtension, | |||
1475 | aRecordFilterQuery, | |||
1476 | aFilterInclusive | |||
1477 | )) | |||
1478 | return false; // failed | |||
1479 | } | |||
1480 | } | |||
1481 | if (fAsSubDatastoreOf) { | |||
1482 | // find link config for this superdatastore | |||
1483 | TSubDSLinkConfig *lcfgP = NULL__null; | |||
1484 | TSubDSConfigList::iterator pos; | |||
1485 | for(pos=superdscfgP->fSubDatastores.begin();pos!=superdscfgP->fSubDatastores.end();pos++) { | |||
1486 | if ((*pos)->fLinkedDSConfigP==fDSConfigP) { | |||
1487 | // this is the link | |||
1488 | lcfgP = *pos; | |||
1489 | break; | |||
1490 | } | |||
1491 | } | |||
1492 | if (lcfgP) { | |||
1493 | // now link into superdatastore | |||
1494 | fAsSubDatastoreOf->addSubDatastoreLink(lcfgP,this); | |||
1495 | } | |||
1496 | else { | |||
1497 | PDEBUGPRINTFX(DBG_ERROR,("Warning: '%s' is not a subdatastore of '%s'", getName(), opts.c_str())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: '%s' is not a subdatastore of '%s'" , getName(), opts.c_str()); }; | |||
1498 | return false; // failed | |||
1499 | } | |||
1500 | } | |||
1501 | } | |||
1502 | else { | |||
1503 | PDEBUGPRINTFX(DBG_ERROR,("Warning: No superdatastore name '%s' exists -> can't run '%s' under superdatastore control", opts.c_str(), getName())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: No superdatastore name '%s' exists -> can't run '%s' under superdatastore control" , opts.c_str(), getName()); }; | |||
1504 | return false; // failed | |||
1505 | } | |||
1506 | #endif // SUPERDATASTORES | |||
1507 | } | |||
1508 | // sync mode | |||
1509 | fSyncMode=aSyncMode; | |||
1510 | fSlowSync=aSlowSync; | |||
1511 | fRefreshOnly=fSyncMode==smo_fromserver; // here to make sure, should be set by setSyncMode(FromAlertCode) later anyway | |||
1512 | // DS 1.2 filters | |||
1513 | AssignString(fRemoteRecordFilterQuery,aRecordFilterQuery); | |||
1514 | fRemoteFilterInclusive=aFilterInclusive; | |||
1515 | // params | |||
1516 | // - build local path | |||
1517 | fLocalDBPath=URI_RELPREFIX"./"; | |||
1518 | fLocalDBPath+=getName(); | |||
1519 | if (aLocalPathExtension && *aLocalPathExtension) { | |||
1520 | fLocalDBPath+='/'; | |||
1521 | fLocalDBPath+=aLocalPathExtension; | |||
1522 | } | |||
1523 | // - we have the params for syncing now | |||
1524 | return changeState(dssta_clientparamset)==LOCERR_OK; | |||
1525 | } // TLocalEngineDS::dsSetClientSyncParams | |||
1526 | ||||
1527 | #endif // SYSYNC_CLIENT | |||
1528 | ||||
1529 | ||||
1530 | ||||
1531 | // add support for more data types | |||
1532 | // (for programatically creating local datastores from specialized TSyncSession derivates) | |||
1533 | void TLocalEngineDS::addTypeSupport(TSyncItemType *aItemTypeP,bool aForRx, bool aForTx) | |||
1534 | { | |||
1535 | if (aForRx) fRxItemTypes.push_back(aItemTypeP); | |||
1536 | if (aForTx) fTxItemTypes.push_back(aItemTypeP); | |||
1537 | } // TLocalEngineDS::addTypeSupport | |||
1538 | ||||
1539 | ||||
1540 | #ifndef NO_REMOTE_RULES | |||
1541 | // add data type that overrides normal type selection if string matches active remote rule | |||
1542 | void TLocalEngineDS::addRuleMatchTypeSupport(TSyncItemType *aItemTypeP,cAppCharP aRuleMatchString) | |||
1543 | { | |||
1544 | TRuleMatchTypeEntry rme; | |||
1545 | rme.itemTypeP = aItemTypeP; | |||
1546 | rme.ruleMatchString = aRuleMatchString; | |||
1547 | fRuleMatchItemTypes.push_back(rme); | |||
1548 | } // TLocalEngineDS::addRuleMatchTypeSupport | |||
1549 | #endif | |||
1550 | ||||
1551 | ||||
1552 | TTypeVariantDescriptor TLocalEngineDS::getVariantDescForType(TSyncItemType *aItemTypeP) | |||
1553 | { | |||
1554 | // search in config for specific variant descriptor | |||
1555 | // - first check preferred rx | |||
1556 | if (fDSConfigP->fTypeSupport.fPreferredRx == aItemTypeP->getTypeConfig()) | |||
1557 | return fDSConfigP->fTypeSupport.fPrefRxVariantDescP; | |||
1558 | // - then check preferred tx | |||
1559 | if (fDSConfigP->fTypeSupport.fPreferredTx == aItemTypeP->getTypeConfig()) | |||
1560 | return fDSConfigP->fTypeSupport.fPrefTxVariantDescP; | |||
1561 | // - then check additional types | |||
1562 | TAdditionalTypesList::iterator pos; | |||
1563 | for (pos=fDSConfigP->fTypeSupport.fAdditionalTypes.begin(); pos!=fDSConfigP->fTypeSupport.fAdditionalTypes.end(); pos++) { | |||
1564 | if ((*pos).datatypeconfig == aItemTypeP->getTypeConfig()) | |||
1565 | return (*pos).variantDescP; | |||
1566 | } | |||
1567 | // none found | |||
1568 | return NULL__null; | |||
1569 | } // TLocalEngineDS::getVariantDescForType | |||
1570 | ||||
1571 | ||||
1572 | ||||
1573 | ||||
1574 | // - called when a item in the sync set changes its localID (due to local DB internals) | |||
1575 | void TLocalEngineDS::dsLocalIdHasChanged(const char *aOldID, const char *aNewID) | |||
1576 | { | |||
1577 | #ifdef SYSYNC_SERVER1 | |||
1578 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
1579 | // make sure remapped localIDs get updated as well | |||
1580 | TStringToStringMap::iterator pos; | |||
1581 | for (pos=fTempGUIDMap.begin(); pos!=fTempGUIDMap.end(); pos++) { | |||
1582 | if (pos->second == aOldID) { | |||
1583 | // update ID | |||
1584 | PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); } | |||
1585 | "fTempGUIDMap: updating mapping of %s from %s to %s",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); } | |||
1586 | pos->first.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); } | |||
1587 | aOldID,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); } | |||
1588 | aNewID{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); } | |||
1589 | )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: updating mapping of %s from %s to %s" , pos->first.c_str(), aOldID, aNewID ); }; | |||
1590 | pos->second = aNewID; | |||
1591 | break; | |||
1592 | } | |||
1593 | } | |||
1594 | } | |||
1595 | #endif // SYSYNC_SERVER | |||
1596 | } // TLocalEngineDS::dsLocalIdHasChanged | |||
1597 | ||||
1598 | ||||
1599 | #ifdef SYSYNC_SERVER1 | |||
1600 | ||||
1601 | // for received GUIDs (Map command), obtain real GUID (might be temp GUID due to maxguidsize restrictions) | |||
1602 | void TLocalEngineDS::obtainRealLocalID(string &aLocalID) | |||
1603 | { | |||
1604 | if (aLocalID.size()>0 && aLocalID[0]=='#') { | |||
1605 | // seems to be a temp GUID | |||
1606 | TStringToStringMap::iterator pos = | |||
1607 | fTempGUIDMap.find(aLocalID); | |||
1608 | if (pos!=fTempGUIDMap.end()) { | |||
1609 | // found temp GUID mapping, replace it | |||
1610 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "translated tempLocalID='%s' back to real localID='%s'" , aLocalID.c_str(), (*pos).second.c_str() ); } | |||
1611 | "translated tempLocalID='%s' back to real localID='%s'",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "translated tempLocalID='%s' back to real localID='%s'" , aLocalID.c_str(), (*pos).second.c_str() ); } | |||
1612 | aLocalID.c_str(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "translated tempLocalID='%s' back to real localID='%s'" , aLocalID.c_str(), (*pos).second.c_str() ); } | |||
1613 | (*pos).second.c_str(){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "translated tempLocalID='%s' back to real localID='%s'" , aLocalID.c_str(), (*pos).second.c_str() ); } | |||
1614 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "translated tempLocalID='%s' back to real localID='%s'" , aLocalID.c_str(), (*pos).second.c_str() ); }; | |||
1615 | aLocalID = (*pos).second; | |||
1616 | } | |||
1617 | else { | |||
1618 | PDEBUGPRINTFX(DBG_ERROR,("No realLocalID found for tempLocalID='%s'",aLocalID.c_str())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("No realLocalID found for tempLocalID='%s'" ,aLocalID.c_str()); }; | |||
1619 | } | |||
1620 | } | |||
1621 | } // TLocalEngineDS::obtainRealLocalID | |||
1622 | ||||
1623 | ||||
1624 | // for sending GUIDs (Add command), generate temp GUID which conforms to maxguidsize of remote datastore if needed | |||
1625 | void TLocalEngineDS::adjustLocalIDforSize(string &aLocalID, sInt32 maxguidsize, sInt32 prefixsize) | |||
1626 | { | |||
1627 | if (maxguidsize>0) { | |||
1628 | if (aLocalID.length()+prefixsize>(uInt32)maxguidsize) { //BCPPB needed unsigned cast | |||
1629 | // real GUID is too long, we need to create a temp | |||
1630 | #if SYDEBUG2>1 | |||
1631 | // first check if there is already a mapping for it, | |||
1632 | // because on-disk storage can only hold one; also | |||
1633 | // saves space | |||
1634 | // TODO: implement this more efficiently than this O(N) search | |||
1635 | for (TStringToStringMap::const_iterator it = fTempGUIDMap.begin(); | |||
1636 | it != fTempGUIDMap.end(); | |||
1637 | ++it) { | |||
1638 | if (it->second == aLocalID) { | |||
1639 | // found an existing mapping! | |||
1640 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)" , aLocalID.c_str(), it->first.c_str() ); } | |||
1641 | "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)" , aLocalID.c_str(), it->first.c_str() ); } | |||
1642 | aLocalID.c_str(),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)" , aLocalID.c_str(), it->first.c_str() ); } | |||
1643 | it->first.c_str(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)" , aLocalID.c_str(), it->first.c_str() ); } | |||
1644 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s' (reused?!)" , aLocalID.c_str(), it->first.c_str() ); }; | |||
1645 | aLocalID = it->first; | |||
1646 | return; | |||
1647 | } | |||
1648 | } | |||
1649 | string tempguid; | |||
1650 | long counter = fTempGUIDMap.size(); // as list only grows, we have unique tempuids for sure | |||
1651 | while (true) { | |||
1652 | counter++; | |||
1653 | StringObjPrintf(tempguid,"#%ld",counter); | |||
1654 | if (fTempGUIDMap.find(tempguid) != fTempGUIDMap.end()) { | |||
1655 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: '%s' not new?!" , tempguid.c_str() ); } | |||
1656 | "fTempGUIDMap: '%s' not new?!",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: '%s' not new?!" , tempguid.c_str() ); } | |||
1657 | tempguid.c_str(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: '%s' not new?!" , tempguid.c_str() ); } | |||
1658 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "fTempGUIDMap: '%s' not new?!" , tempguid.c_str() ); }; | |||
1659 | } else { | |||
1660 | break; | |||
1661 | } | |||
1662 | } | |||
1663 | #else | |||
1664 | // rely on tempguid list only growing (which still holds true) | |||
1665 | string tempguid; | |||
1666 | StringObjPrintf(tempguid,"#%ld",(long)fTempGUIDMap.size()+1); // as list only grows, we have unique tempuids for sure | |||
1667 | #endif | |||
1668 | fTempGUIDMap[tempguid]=aLocalID; | |||
1669 | PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'" , aLocalID.c_str(), tempguid.c_str() ); } | |||
1670 | "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'" , aLocalID.c_str(), tempguid.c_str() ); } | |||
1671 | aLocalID.c_str(),{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'" , aLocalID.c_str(), tempguid.c_str() ); } | |||
1672 | tempguid.c_str(){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'" , aLocalID.c_str(), tempguid.c_str() ); } | |||
1673 | )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "fTempGUIDMap: translated realLocalID='%s' to tempLocalID='%s'" , aLocalID.c_str(), tempguid.c_str() ); }; | |||
1674 | aLocalID=tempguid; | |||
1675 | } | |||
1676 | } | |||
1677 | } // TLocalEngineDS::adjustLocalIDforSize | |||
1678 | ||||
1679 | #endif // SYSYNC_SERVER | |||
1680 | ||||
1681 | ||||
1682 | // set Sync types needed for sending local data to remote DB | |||
1683 | void TLocalEngineDS::setSendTypeInfo( | |||
1684 | TSyncItemType *aLocalSendToRemoteTypeP, | |||
1685 | TSyncItemType *aRemoteReceiveFromLocalTypeP | |||
1686 | ) | |||
1687 | { | |||
1688 | fLocalSendToRemoteTypeP=aLocalSendToRemoteTypeP; | |||
1689 | fRemoteReceiveFromLocalTypeP=aRemoteReceiveFromLocalTypeP; | |||
1690 | } // TLocalEngineDS::setSendTypeInfo | |||
1691 | ||||
1692 | ||||
1693 | // set Sync types needed for receiving remote data in local DB | |||
1694 | void TLocalEngineDS::setReceiveTypeInfo( | |||
1695 | TSyncItemType *aLocalReceiveFromRemoteTypeP, | |||
1696 | TSyncItemType *aRemoteSendToLocalTypeP | |||
1697 | ) | |||
1698 | { | |||
1699 | fLocalReceiveFromRemoteTypeP=aLocalReceiveFromRemoteTypeP; | |||
1700 | fRemoteSendToLocalTypeP=aRemoteSendToLocalTypeP; | |||
1701 | } // TLocalEngineDS::setReceiveTypeInfo | |||
1702 | ||||
1703 | ||||
1704 | // - init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo | |||
1705 | localstatus TLocalEngineDS::initDataTypeUse(void) | |||
1706 | { | |||
1707 | localstatus sta = LOCERR_OK; | |||
1708 | ||||
1709 | // check compatibility | |||
1710 | if ( | |||
1711 | !fLocalSendToRemoteTypeP || !fLocalReceiveFromRemoteTypeP || | |||
1712 | !fLocalSendToRemoteTypeP->isCompatibleWith(fLocalReceiveFromRemoteTypeP) | |||
1713 | ) { | |||
1714 | // send and receive types not compatible | |||
1715 | sta = 415; | |||
1716 | PDEBUGPRINTFX(DBG_ERROR,("Incompatible send and receive types -> cannot sync (415)")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Incompatible send and receive types -> cannot sync (415)" ); }; | |||
1717 | engAbortDataStoreSync(sta,true,false); // do not proceed with sync of this datastore, local problem, not resumable | |||
1718 | return sta; | |||
1719 | } | |||
1720 | #ifdef SCRIPT_SUPPORT1 | |||
1721 | // let types initialize themselves for being used by this datastore | |||
1722 | // - optimization: if both types are same, initialize only once | |||
1723 | if (fLocalSendToRemoteTypeP == fLocalReceiveFromRemoteTypeP) | |||
1724 | fLocalReceiveFromRemoteTypeP->initDataTypeUse(this,true,true); // send and receive | |||
1725 | else { | |||
1726 | fLocalSendToRemoteTypeP->initDataTypeUse(this,true,false); // for sending, not receiving | |||
1727 | fLocalReceiveFromRemoteTypeP->initDataTypeUse(this,false,true); // not sending, for receiving | |||
1728 | } | |||
1729 | #endif | |||
1730 | // ok | |||
1731 | return sta; | |||
1732 | } // TLocalEngineDS::initDataTypeUse | |||
1733 | ||||
1734 | ||||
1735 | ||||
1736 | // conflict resolution strategy. Conservative here | |||
1737 | TConflictResolution TLocalEngineDS::getConflictStrategy(bool aForSlowSync, bool aForFirstTime) | |||
1738 | { | |||
1739 | return aForSlowSync ? | |||
1740 | (aForFirstTime ? fDSConfigP->fFirstTimeStrategy : fDSConfigP->fSlowSyncStrategy) : | |||
1741 | fDSConfigP->fConflictStrategy; | |||
1742 | } // TLocalEngineDS::getConflictStrategy | |||
1743 | ||||
1744 | ||||
1745 | ||||
1746 | // add filter keywords and property names to filterCap | |||
1747 | void TLocalEngineDS::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps) | |||
1748 | { | |||
1749 | #ifdef OBJECT_FILTERING1 | |||
1750 | // add my own properties | |||
1751 | if (fDSConfigP->fDateRangeSupported) { | |||
1752 | addPCDataStringToList("BEFORE", &aFilterKeywords); | |||
1753 | addPCDataStringToList("SINCE", &aFilterKeywords); | |||
1754 | } | |||
1755 | // get default send type | |||
1756 | TSyncItemType *itemTypeP = fSessionP->findLocalType(fDSConfigP->fTypeSupport.fPreferredTx); | |||
1757 | TTypeVariantDescriptor variantDesc = NULL__null; | |||
1758 | doesUseType(itemTypeP, &variantDesc); | |||
1759 | // have it add it's keywords and properties | |||
1760 | itemTypeP->addFilterCapPropsAndKeywords(aFilterKeywords,aFilterProps,variantDesc); | |||
1761 | #endif | |||
1762 | } // TLocalEngineDS::addFilterCapPropsAndKeywords | |||
1763 | ||||
1764 | ||||
1765 | ||||
1766 | ||||
1767 | ||||
1768 | // reset all filter settings | |||
1769 | void TLocalEngineDS::resetFiltering(void) | |||
1770 | { | |||
1771 | #ifdef OBJECT_FILTERING1 | |||
1772 | // - dynamic sync set filter | |||
1773 | fSyncSetFilter.erase(); | |||
1774 | // - static filter | |||
1775 | fLocalDBFilter=fDSConfigP->fLocalDBFilterConf; // use configured localDB filter | |||
1776 | // - TAF filters | |||
1777 | #ifdef SYNCML_TAF_SUPPORT | |||
1778 | fTargetAddressFilter.erase(); // none from <sync> yet | |||
1779 | fIntTargetAddressFilter.erase(); // none from internal source (script) | |||
1780 | #endif | |||
1781 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
1782 | // - Other filtering options | |||
1783 | fDateRangeStart=0; // no date range | |||
1784 | fDateRangeEnd=0; | |||
1785 | fSizeLimit=-1; // no size limit | |||
1786 | fMaxItemCount=0; // no item count limit | |||
1787 | fNoAttachments=false; // attachments not suppressed | |||
1788 | fDBOptions.erase(); // no options | |||
1789 | #endif | |||
1790 | // - Filtering requirements | |||
1791 | fTypeFilteringNeeded=false; | |||
1792 | fFilteringNeededForAll=false; | |||
1793 | fFilteringNeeded=false; | |||
1794 | #endif // OBJECT_FILTERING | |||
1795 | } // TLocalEngineDS::resetFiltering | |||
1796 | ||||
1797 | ||||
1798 | ||||
1799 | #ifdef OBJECT_FILTERING1 | |||
1800 | ||||
1801 | /// @brief check single filter term for DS 1.2 filterkeywords. | |||
1802 | /// @return true if term still needs to be added to normal filter expression, false if term will be handled otherwise | |||
1803 | bool TLocalEngineDS::checkFilterkeywordTerm( | |||
1804 | cAppCharP aIdent, bool aAssignToMakeTrue, | |||
1805 | cAppCharP aOp, bool aCaseInsensitive, | |||
1806 | cAppCharP aVal, bool aSpecialValue, | |||
1807 | TSyncItemType *aItemTypeP | |||
1808 | ) | |||
1809 | { | |||
1810 | // show it to the datatype (if any) | |||
1811 | if (aItemTypeP) { | |||
1812 | if (!aItemTypeP->checkFilterkeywordTerm(aIdent, aAssignToMakeTrue, aOp, aCaseInsensitive, aVal, aSpecialValue)) | |||
1813 | return false; // type fully handles it, no need to check it further or add it to the filter expression | |||
1814 | } | |||
1815 | // we generally implement BEFORE and SINCE on the datastore level | |||
1816 | // This might not make sense depending on the actual datatype, but does not harm either | |||
1817 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
1818 | timecontext_t tctx; | |||
1819 | ||||
1820 | if (strucmp(aIdent,"BEFORE")==0) { | |||
1821 | if (ISO8601StrToTimestamp(aVal,fDateRangeEnd,tctx)) { | |||
1822 | TzConvertTimestamp(fDateRangeEnd,tctx,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),getSessionZones(),fSessionP->fUserTimeContext); | |||
1823 | } | |||
1824 | else { | |||
1825 | PDEBUGPRINTFX(DBG_ERROR,("invalid ISO datetime for BEFORE: '%s'",aVal)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("invalid ISO datetime for BEFORE: '%s'" ,aVal); }; | |||
1826 | return true; // add it to filter, possibly this is not meant to be a filterkeyword | |||
1827 | } | |||
1828 | } | |||
1829 | else if (strucmp(aIdent,"SINCE")==0) { | |||
1830 | if (ISO8601StrToTimestamp(aVal,fDateRangeStart,tctx)) { | |||
1831 | TzConvertTimestamp(fDateRangeStart,tctx,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),getSessionZones(),fSessionP->fUserTimeContext); | |||
1832 | } | |||
1833 | else { | |||
1834 | PDEBUGPRINTFX(DBG_ERROR,("invalid ISO datetime for SINCE: '%s'",aVal)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("invalid ISO datetime for SINCE: '%s'" ,aVal); }; | |||
1835 | return true; // add it to filter, possibly this is not meant to be a filterkeyword | |||
1836 | } | |||
1837 | } | |||
1838 | else if (strucmp(aIdent,"MAXSIZE")==0) { | |||
1839 | if (StrToFieldinteger(aVal,fSizeLimit)StrToLongLong(aVal,fSizeLimit)==0) { | |||
1840 | PDEBUGPRINTFX(DBG_ERROR,("invalid integer for MAXSIZE: '%s'",aVal)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("invalid integer for MAXSIZE: '%s'" ,aVal); }; | |||
1841 | return true; // add it to filter, possibly this is not meant to be a filterkeyword | |||
1842 | } | |||
1843 | } | |||
1844 | else if (strucmp(aIdent,"MAXCOUNT")==0) { | |||
1845 | if (StrToULong(aVal,fMaxItemCount)==0) { | |||
1846 | PDEBUGPRINTFX(DBG_ERROR,("invalid integer for MAXSIZE: '%s'",aVal)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("invalid integer for MAXSIZE: '%s'" ,aVal); }; | |||
1847 | return true; // add it to filter, possibly this is not meant to be a filterkeyword | |||
1848 | } | |||
1849 | } | |||
1850 | else if (strucmp(aIdent,"NOATT")==0) { | |||
1851 | if (!StrToBool(aVal,fNoAttachments)==0) { | |||
1852 | PDEBUGPRINTFX(DBG_ERROR,("invalid boolean for NOATT: '%s'",aVal)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("invalid boolean for NOATT: '%s'" ,aVal); }; | |||
1853 | return true; // add it to filter, possibly this is not meant to be a filterkeyword | |||
1854 | } | |||
1855 | } | |||
1856 | else if (strucmp(aIdent,"DBOPTIONS")==0) { | |||
1857 | fDBOptions = aVal; // just get DB options | |||
1858 | } | |||
1859 | #endif | |||
1860 | else { | |||
1861 | // unknown identifier, add to filter expression | |||
1862 | return true; | |||
1863 | } | |||
1864 | // this term will be processed by special mechanism like fDateRangeStart/fDateRangeEnd | |||
1865 | // or fSizeLimit, so there is no need for normal filtering | |||
1866 | return false; // do not include into filter | |||
1867 | } // TLocalEngineDS::checkFilterkeywordTerm | |||
1868 | ||||
1869 | ||||
1870 | /// @brief parse "syncml:filtertype-cgi" filter, convert into internal filter syntax | |||
1871 | /// and possibly sets some special filter options (fDateRangeStart, fDateRangeEnd) | |||
1872 | /// based on "filterkeywords" available for the type passed (DS 1.2). | |||
1873 | /// For parsing DS 1.1/1.0 TAF-style filters, aItemType can be NULL, no type-specific | |||
1874 | /// filterkeywords can be parsed then. | |||
1875 | /// @return pointer to next character after processing (usually points to terminator) | |||
1876 | /// @param[in] aCGI the NUL-terminated filter string | |||
1877 | /// @param[in] aItemTypeP if not NULL, this is the item type the filter applies to | |||
1878 | const char *TLocalEngineDS::parseFilterCGI(cAppCharP aCGI, TSyncItemType *aItemTypeP, string &aFilter) | |||
1879 | { | |||
1880 | const char *p=aCGI, *q; | |||
1881 | sInt16 paraNest=0; // nested paranthesis | |||
1882 | string ident; | |||
1883 | char op[3]; | |||
1884 | char logop; | |||
1885 | string val; | |||
1886 | bool termtofilter; | |||
1887 | bool assigntomaketrue; | |||
1888 | bool specialvalue; | |||
1889 | bool caseinsensitive; | |||
1890 | ||||
1891 | PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,("Parsing syncml:filtertype-cgi filter: %s",aCGI)){ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ("Parsing syncml:filtertype-cgi filter: %s" ,aCGI); }; | |||
1892 | aFilter.erase(); | |||
1893 | logop=0; | |||
1894 | while (p && *p) { | |||
1895 | if (aFilter.empty()) logop=0; // ignore logical operation that would be at beginning of an expression | |||
1896 | // skip spaces | |||
1897 | while (isspace(*p)) p++; | |||
1898 | // now we need an ident or paranthesis | |||
1899 | if (*p=='(') { | |||
1900 | if (logop) aFilter+=logop; // expression continues, we need the logop now | |||
1901 | logop=0; // now consumed | |||
1902 | paraNest++; | |||
1903 | aFilter+='('; | |||
1904 | p++; | |||
1905 | } | |||
1906 | else { | |||
1907 | // must be term: ident op val | |||
1908 | // - check special case pseudo-identifiers | |||
1909 | if (strucmp(p,"&LUID;",6)==0) { | |||
1910 | ident="LUID"; | |||
1911 | p+=6; | |||
1912 | } | |||
1913 | else { | |||
1914 | // normal identifier | |||
1915 | // - search end | |||
1916 | q=p; | |||
1917 | while (isalnum(*q) || *q=='[' || *q==']' || *q=='.' || *q=='_') q++; | |||
1918 | // - assign | |||
1919 | if (q==p) { | |||
1920 | PDEBUGPRINTFX(DBG_ERROR,("Expected identifier but found '%s'",p)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Expected identifier but found '%s'" ,p); }; | |||
1921 | break; | |||
1922 | } | |||
1923 | ident.assign(p,q-p); | |||
1924 | p=q; | |||
1925 | } | |||
1926 | // skip spaces | |||
1927 | while (isspace(*p)) p++; | |||
1928 | // next must be comparison operator, possibly preceeded by modifiers | |||
1929 | op[0]=0; op[1]=0; op[2]=0; | |||
1930 | assigntomaketrue=false; | |||
1931 | specialvalue=false; | |||
1932 | caseinsensitive=false; | |||
1933 | // - check modifiers first | |||
1934 | if (*p==':') { assigntomaketrue=true; p++; } | |||
1935 | if (*p=='*') { specialvalue=true; p++; } | |||
1936 | if (*p=='^') { caseinsensitive=true; p++; } | |||
1937 | // - now OP either in internal form or as pseudo-entity | |||
1938 | if (*p=='>' || *p=='<') { | |||
1939 | // possible two-char ops (>=, <=, <>) | |||
1940 | op[0]=*p++; | |||
1941 | if (*p=='>' || *p=='=') { | |||
1942 | op[1]=*p++; | |||
1943 | } | |||
1944 | } | |||
1945 | else if (*p=='=' || *p=='%' || *p=='$') { | |||
1946 | // single char ops, just use them as is | |||
1947 | op[0]=*p++; | |||
1948 | } | |||
1949 | else if (*p=='&') { | |||
1950 | p++; | |||
1951 | if (tolower(*p)=='i') { caseinsensitive=true; p++; } | |||
1952 | if (strucmp(p,"eq;",3)==0) { op[0]='='; p+=3; } | |||
1953 | else if (strucmp(p,"gt;",3)==0) { op[0]='>'; p+=3; } | |||
1954 | else if (strucmp(p,"ge;",3)==0) { op[0]='>'; op[1]='='; p+=3; } | |||
1955 | else if (strucmp(p,"lt;",3)==0) { op[0]='<'; p+=3; } | |||
1956 | else if (strucmp(p,"le;",3)==0) { op[0]='<'; op[1]='='; p+=3; } | |||
1957 | else if (strucmp(p,"ne;",3)==0) { op[0]='<'; op[1]='>'; p+=3; } | |||
1958 | else if (strucmp(p,"con;",4)==0) { op[0]='%'; p+=4; } | |||
1959 | else if (strucmp(p,"ncon;",5)==0) { op[0]='$'; p+=5; } | |||
1960 | else { | |||
1961 | PDEBUGPRINTFX(DBG_ERROR,("Expected comparison operator pseudo-entity but found '%s'",p-1)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Expected comparison operator pseudo-entity but found '%s'" ,p-1); }; | |||
1962 | break; | |||
1963 | } | |||
1964 | } | |||
1965 | else { | |||
1966 | PDEBUGPRINTFX(DBG_ERROR,("Expected comparison operator but found '%s'",p)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Expected comparison operator but found '%s'" ,p); }; | |||
1967 | break; | |||
1968 | } | |||
1969 | // next must be value | |||
1970 | // - check for special value cases | |||
1971 | if (strucmp(p,"&NULL;",6)==0) { | |||
1972 | // SyncML DS 1.2 | |||
1973 | p+=6; | |||
1974 | val='E'; | |||
1975 | specialvalue=true; | |||
1976 | } | |||
1977 | else if (strucmp(p,"&UNASSIGNED;",12)==0) { | |||
1978 | // Synthesis extension | |||
1979 | p+=12; | |||
1980 | val='U'; | |||
1981 | specialvalue=true; | |||
1982 | } | |||
1983 | else { | |||
1984 | val.erase(); | |||
1985 | } | |||
1986 | // - get value chars | |||
1987 | while (*p && *p!='&' && *p!='|' && *p!=')') { | |||
1988 | // value char, possibly hex escaped | |||
1989 | uInt16 c; | |||
1990 | if (*p=='%') { | |||
1991 | // convert from hex | |||
1992 | if (HexStrToUShort(p+1,c,2)==2) { | |||
1993 | p+=3; | |||
1994 | } | |||
1995 | else | |||
1996 | c=*p++; | |||
1997 | } | |||
1998 | else | |||
1999 | c=*p++; | |||
2000 | // add to value | |||
2001 | val += (char)c; | |||
2002 | } // value | |||
2003 | // now we have identifier, op and value | |||
2004 | // - check and possibly sort out filterkeyword terms | |||
2005 | termtofilter = checkFilterkeywordTerm(ident.c_str(),assigntomaketrue,op,caseinsensitive,val.c_str(),specialvalue,aItemTypeP); | |||
2006 | // - add to filter if not handled already by other mechanism | |||
2007 | if (termtofilter) { | |||
2008 | if (logop) aFilter+=logop; // if this is a continuation, add logical operator now | |||
2009 | aFilter += ident; | |||
2010 | if (assigntomaketrue) aFilter+=':'; | |||
2011 | if (specialvalue) aFilter+='*'; | |||
2012 | if (caseinsensitive) aFilter+='^'; | |||
2013 | aFilter += op; | |||
2014 | aFilter += val; | |||
2015 | } | |||
2016 | else { | |||
2017 | PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,({ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ( "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter" , ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive ,val.c_str(),(uInt16)specialvalue ); } | |||
2018 | "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter",{ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ( "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter" , ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive ,val.c_str(),(uInt16)specialvalue ); } | |||
2019 | ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive,val.c_str(),(uInt16)specialvalue{ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ( "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter" , ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive ,val.c_str(),(uInt16)specialvalue ); } | |||
2020 | )){ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ( "checkFilterkeywordTerm(%s,%hd,%s,%hd,%s,%hd) prevents adding to filter" , ident.c_str(),(uInt16)assigntomaketrue,op,(uInt16)caseinsensitive ,val.c_str(),(uInt16)specialvalue ); }; | |||
2021 | if (logop) { | |||
2022 | PDEBUGPRINTFX(DBG_FILTER,("Ignored logical operation '%c' due to always-ANDed filterkeyword",logop)){ if (((0x08000000) & getDbgMask()) == (0x08000000)) getDbgLogger ()->setNextMask(0x08000000).DebugPrintfLastMask ("Ignored logical operation '%c' due to always-ANDed filterkeyword" ,logop); }; | |||
2023 | } | |||
2024 | } | |||
2025 | // now check for continuation: optional closing paranthesis plus logical op | |||
2026 | // - closing paranthesis | |||
2027 | do { | |||
2028 | // skip spaces | |||
2029 | while (isspace(*p)) p++; | |||
2030 | if (*p!=')') break; | |||
2031 | if (paraNest==0) { | |||
2032 | // as we might parse filters as part of /fi() or /tf() options, | |||
2033 | // this is not an error but only means end of filter expression | |||
2034 | goto endFilter; | |||
2035 | } | |||
2036 | aFilter+=')'; | |||
2037 | paraNest--; | |||
2038 | p++; | |||
2039 | } while (true); | |||
2040 | // - logical op | |||
2041 | if (*p==0) | |||
2042 | break; // done | |||
2043 | else if (*p=='&') { | |||
2044 | logop=*p++; // if no entity matches, & by itself is treated as AND | |||
2045 | if (strucmp(p,"amp;",4)==0) { logop='&'; p+=4; } | |||
2046 | else if (strucmp(p,"and;",4)==0) { logop='&'; p+=4; } | |||
2047 | else if (strucmp(p,"or;",3)==0) { logop='|'; p+=3; } | |||
2048 | } | |||
2049 | else if (*p=='|') { | |||
2050 | logop='|'; | |||
2051 | } | |||
2052 | else { | |||
2053 | PDEBUGPRINTFX(DBG_ERROR,("Expected logical operator or end of filter but found '%s'",p)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Expected logical operator or end of filter but found '%s'" ,p); }; | |||
2054 | break; | |||
2055 | } | |||
2056 | } // not opening paranthesis | |||
2057 | } // while not end of filter | |||
2058 | endFilter: | |||
2059 | PDEBUGPRINTFX(DBG_FILTER+DBG_EXOTIC,("Resulting internal filter: %s",aFilter.c_str())){ if (((0x08000000 +0x80000000) & getDbgMask()) == (0x08000000 +0x80000000)) getDbgLogger()->setNextMask(0x08000000 +0x80000000 ).DebugPrintfLastMask ("Resulting internal filter: %s",aFilter .c_str()); }; | |||
2060 | // return pointer to terminating character | |||
2061 | return p; | |||
2062 | } // TLocalEngineDS::parseFilterCGI | |||
2063 | ||||
2064 | ||||
2065 | #endif | |||
2066 | ||||
2067 | ||||
2068 | // analyze database name | |||
2069 | void TLocalEngineDS::analyzeName( | |||
2070 | const char *aDatastoreURI, | |||
2071 | string *aBaseNameP, | |||
2072 | string *aTableNameP, | |||
2073 | string *aCGIP | |||
2074 | ) | |||
2075 | { | |||
2076 | const char *p,*q=NULL__null, *r; | |||
2077 | r=strchr(aDatastoreURI,'?'); | |||
2078 | p=strchr(aDatastoreURI,'/'); | |||
2079 | if (r && p>r) p=NULL__null; // if slash is in CGI, ignore it | |||
2080 | else q=p+1; // slash exclusive | |||
2081 | if (p!=NULL__null) { | |||
2082 | // we have more than just the first element | |||
2083 | if (aBaseNameP) aBaseNameP->assign(aDatastoreURI,p-aDatastoreURI); | |||
2084 | // rest is table name and probably CGI | |||
2085 | if (aTableNameP) { | |||
2086 | if (r) aTableNameP->assign(q,r-q); // we have CGI | |||
2087 | else *aTableNameP=q; // entire rest is tablename | |||
2088 | } | |||
2089 | } | |||
2090 | else { | |||
2091 | // no second path element, but possibly CGI | |||
2092 | // - assign base name | |||
2093 | if (aBaseNameP) { | |||
2094 | if (r) | |||
2095 | (*aBaseNameP).assign(aDatastoreURI,r-aDatastoreURI); // only up to CGI | |||
2096 | else | |||
2097 | (*aBaseNameP)=aDatastoreURI; // complete name | |||
2098 | } | |||
2099 | // - there is no table name | |||
2100 | if (aTableNameP) aTableNameP->erase(); | |||
2101 | } | |||
2102 | // return CGI (w/o question mark) if any | |||
2103 | if (aCGIP) { | |||
2104 | if (r) *aCGIP=r+1; else aCGIP->erase(); | |||
2105 | } | |||
2106 | } // TLocalEngineDS::analyzeName | |||
2107 | ||||
2108 | ||||
2109 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
2110 | ||||
2111 | // parses single option, returns pointer to terminating char of argument string | |||
2112 | // or NULL on error | |||
2113 | // Note: if aArguments is passed NULL, this is an option without arguments, | |||
2114 | // and an arbitrary non-NULL will be returned if parsing is ok | |||
2115 | const char *TLocalEngineDS::parseOption( | |||
2116 | const char *aOptName, | |||
2117 | const char *aArguments, | |||
2118 | bool aFromSyncCommand | |||
2119 | ) | |||
2120 | { | |||
2121 | #ifdef OBJECT_FILTERING1 | |||
2122 | if (strucmp(aOptName,"fi")==0) { | |||
2123 | if (!aArguments) return NULL__null; | |||
2124 | // make sync set filter expression | |||
2125 | string f; | |||
2126 | aArguments=parseFilterCGI(aArguments,fLocalSendToRemoteTypeP,f); // if type being used for sending to remote is known here, use it | |||
2127 | if (!aFromSyncCommand) { | |||
2128 | addToFilter(f.c_str(),fSyncSetFilter,false); // AND chaining | |||
2129 | // call this once to give derivate a chance to see if it can filter the now set fSyncSetFilter | |||
2130 | engFilteredFetchesFromDB(true); | |||
2131 | } | |||
2132 | return aArguments; // end of filter pattern | |||
2133 | } | |||
2134 | #ifdef SYNCML_TAF_SUPPORT | |||
2135 | else if (strucmp(aOptName,"tf")==0) { | |||
2136 | if (!aArguments) return NULL__null; | |||
2137 | // make temporary filter (or TAF) expression | |||
2138 | aArguments=parseFilterCGI(aArguments,fLocalSendToRemoteTypeP,fTargetAddressFilter); // if type being used for sending to remote is known here, use it | |||
2139 | // Note: TAF filters are always evaluated internally as we need all SyncSet records | |||
2140 | // regardless of possible TAF suppression (for slowsync matching etc.) | |||
2141 | return aArguments; // end of filter pattern | |||
2142 | } | |||
2143 | #endif | |||
2144 | else if (aArguments && strucmp(aOptName,"dr")==0) { | |||
2145 | // date range limit | |||
2146 | sInt16 dstart,dend; | |||
2147 | if (sscanf(aArguments,"%hd,%hd",&dstart,&dend)==2) { | |||
2148 | // - find end of arguments | |||
2149 | aArguments=strchr(aArguments,')'); | |||
2150 | // - calculate start and end | |||
2151 | fDateRangeStart=getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),getSessionZones()); | |||
2152 | fDateRangeEnd=fDateRangeStart; | |||
2153 | // - now use offsets | |||
2154 | fDateRangeStart+=dstart*linearDateToTimeFactor; | |||
2155 | fDateRangeEnd+=dend*linearDateToTimeFactor; | |||
2156 | return aArguments; | |||
2157 | } | |||
2158 | else return NULL__null; | |||
2159 | } | |||
2160 | else if (aArguments && strucmp(aOptName,"li")==0) { | |||
2161 | // size limit | |||
2162 | sInt16 n=StrToFieldinteger(aArguments,fSizeLimit)StrToLongLong(aArguments,fSizeLimit); | |||
2163 | if (n>0) { | |||
2164 | // - find end of arguments | |||
2165 | aArguments+=n; | |||
2166 | return aArguments; | |||
2167 | } | |||
2168 | else return NULL__null; | |||
2169 | } | |||
2170 | else | |||
2171 | if (!aArguments && strucmp(aOptName,"na")==0) { | |||
2172 | // no attachments | |||
2173 | fNoAttachments=true; | |||
2174 | return (const char *)1; // non-zero | |||
2175 | } | |||
2176 | else | |||
2177 | if (aArguments && strucmp(aOptName,"max")==0) { | |||
2178 | // maximum number of items (for email for example) | |||
2179 | sInt16 n=StrToULong(aArguments,fMaxItemCount); | |||
2180 | if (n>0) { | |||
2181 | // - find end of arguments | |||
2182 | aArguments+=n; | |||
2183 | return aArguments; | |||
2184 | } | |||
2185 | else return NULL__null; | |||
2186 | } | |||
2187 | else | |||
2188 | #endif | |||
2189 | #ifdef SYSYNC_SERVER1 | |||
2190 | if (IS_SERVER(getSyncAppBase()->isServer()) && !aArguments && strucmp(aOptName,"slow")==0) { | |||
2191 | // force a slow sync | |||
2192 | PDEBUGPRINTFX(DBG_HOT,("Slowsync forced by CGI-option in db path")){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Slowsync forced by CGI-option in db path" ); }; | |||
2193 | fForceSlowSync=true; | |||
2194 | return (const char *)1; // non-zero | |||
2195 | } | |||
2196 | else | |||
2197 | #endif // SYSYNC_SERVER | |||
2198 | if (aArguments && strucmp(aOptName,"o")==0) { | |||
2199 | // datastore options | |||
2200 | // - find end of arguments | |||
2201 | const char *p=strchr(aArguments,')'); | |||
2202 | if (p) fDBOptions.assign(aArguments,p-aArguments); | |||
2203 | return p; | |||
2204 | } | |||
2205 | else | |||
2206 | return NULL__null; // not parsed | |||
2207 | } // TLocalEngineDS::parseOption | |||
2208 | ||||
2209 | #endif | |||
2210 | ||||
2211 | // parse options | |||
2212 | localstatus TLocalEngineDS::engParseOptions( | |||
2213 | const char *aTargetURIOptions, // option string contained in target URI | |||
2214 | bool aFromSyncCommand // must be set when parsing options from <sync> target URI | |||
2215 | ) | |||
2216 | { | |||
2217 | localstatus sta=LOCERR_OK; | |||
2218 | if (aTargetURIOptions) { | |||
2219 | const char *p = aTargetURIOptions; | |||
2220 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
2221 | const char *q; | |||
2222 | #endif | |||
2223 | char c; | |||
2224 | string taf; // official TAF | |||
2225 | while ((c=*p)) { | |||
2226 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
2227 | if (c=='/') { | |||
2228 | // proprietary option lead-in | |||
2229 | // - get option name | |||
2230 | string optname; | |||
2231 | optname.erase(); | |||
2232 | while(isalnum(c=*(++p))) | |||
2233 | optname+=c; | |||
2234 | // - get arguments | |||
2235 | if (c=='(') { | |||
2236 | q=p; // save | |||
2237 | p++; // skip "(" | |||
2238 | p=parseOption(optname.c_str(),p,aFromSyncCommand); | |||
2239 | if (!p) { | |||
2240 | // unrecognized or badly formatted option, just add it to TAF | |||
2241 | taf+='/'; | |||
2242 | taf+=optname; | |||
2243 | p=q; // restart after option name | |||
2244 | continue; | |||
2245 | } | |||
2246 | if (*p!=')') { | |||
2247 | sta=406; | |||
2248 | PDEBUGPRINTFX(DBG_ERROR,("Syntax error in target options")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Syntax error in target options" ); }; | |||
2249 | break; | |||
2250 | } | |||
2251 | } | |||
2252 | else { | |||
2253 | // option without arguments | |||
2254 | if (!parseOption(optname.c_str(),NULL__null,aFromSyncCommand)) { | |||
2255 | sta=406; | |||
2256 | PDEBUGPRINTFX(DBG_ERROR,("Unknown target option")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Unknown target option" ); }; | |||
2257 | break; | |||
2258 | } // error, not parsed | |||
2259 | p--; // will be incremented once again below | |||
2260 | } | |||
2261 | } | |||
2262 | else | |||
2263 | #endif | |||
2264 | { | |||
2265 | // char not part of an option | |||
2266 | taf+=c; | |||
2267 | } | |||
2268 | // next | |||
2269 | p++; | |||
2270 | } | |||
2271 | // check if we have TAF | |||
2272 | if (taf.size()>0) { | |||
2273 | #if defined(TAF_AS_SYNCSETFILTER) && defined(SYSYNC_TARGET_OPTIONS1) | |||
2274 | // treat as "fi(<filterexpression>)" option like before 1.0.8.10 | |||
2275 | if (!parseOption("fi",taf.c_str(),aFromSyncCommand)) { sta=406; } // error, not parsed | |||
2276 | #else | |||
2277 | #ifdef SYNCML_TAF_SUPPORT | |||
2278 | // treat as "tf(<filterexpression>)" = real TAF | |||
2279 | if (!parseOption("tf",taf.c_str(),aFromSyncCommand)) { sta=406; } // error, not parsed | |||
2280 | #else | |||
2281 | sta=406; | |||
2282 | PDEBUGPRINTFX(DBG_ERROR,("TAF not supported")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("TAF not supported" ); }; | |||
2283 | #endif | |||
2284 | #endif | |||
2285 | } | |||
2286 | } | |||
2287 | // return status | |||
2288 | return sta; | |||
2289 | } // TLocalEngineDS::engParseOptions | |||
2290 | ||||
2291 | ||||
2292 | // process SyncML 1.2 style filter | |||
2293 | localstatus TLocalEngineDS::engProcessDS12Filter(SmlFilterPtr_t aTargetFilter) | |||
2294 | { | |||
2295 | localstatus sta=LOCERR_OK; | |||
2296 | ||||
2297 | if (aTargetFilter) { | |||
2298 | // check general availability | |||
2299 | #ifdef OBJECT_FILTERING1 | |||
2300 | if (!fDSConfigP->fDS12FilterSupport) | |||
2301 | #endif | |||
2302 | { | |||
2303 | PDEBUGPRINTFX(DBG_ERROR,("DS 1.2 style filtering is not available or disabled in config (<ds12filters>)")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("DS 1.2 style filtering is not available or disabled in config (<ds12filters>)" ); }; | |||
2304 | sta=406; | |||
2305 | goto error; | |||
2306 | } | |||
2307 | // check filter | |||
2308 | TSyncItemType *itemTypeP=NULL__null; // no associated type so far | |||
2309 | bool inclusiveFilter=false; // default is EXCLUSIVE | |||
2310 | // - meta | |||
2311 | if (aTargetFilter->meta) { | |||
2312 | SmlMetInfMetInfPtr_t metaP = smlPCDataToMetInfP(aTargetFilter->meta); | |||
2313 | const char *typestr = smlMetaTypeToCharP(metaP); | |||
2314 | // get sync item type for it | |||
2315 | // - filter mostly applies to items SENT, so we search these first | |||
2316 | itemTypeP = getSendType(typestr,NULL__null); | |||
2317 | if (!itemTypeP) | |||
2318 | itemTypeP = getReceiveType(typestr,NULL__null); | |||
2319 | PDEBUGPRINTFX(DBG_FILTER,("DS12 <Filter> <Type> is '%s' -> %sfound",typestr,itemTypeP ? "" : "NOT ")){ if (((0x08000000) & getDbgMask()) == (0x08000000)) getDbgLogger ()->setNextMask(0x08000000).DebugPrintfLastMask ("DS12 <Filter> <Type> is '%s' -> %sfound" ,typestr,itemTypeP ? "" : "NOT "); }; | |||
2320 | if (!itemTypeP) { | |||
2321 | sta=415; | |||
2322 | goto error; | |||
2323 | } | |||
2324 | } | |||
2325 | // - filtertype | |||
2326 | if (aTargetFilter->filtertype) { | |||
2327 | const char *ftystr = smlPCDataToCharP(aTargetFilter->filtertype); | |||
2328 | if (strucmp(ftystr,SYNCML_FILTERTYPE_INCLUSIVE"INCLUSIVE")==0) { | |||
2329 | inclusiveFilter=true; | |||
2330 | } | |||
2331 | else if (strucmp(ftystr,SYNCML_FILTERTYPE_EXCLUSIVE"EXCLUSIVE")==0) { | |||
2332 | inclusiveFilter=false; | |||
2333 | } | |||
2334 | else { | |||
2335 | PDEBUGPRINTFX(DBG_ERROR,("Invalid <FilterType> '%s'",ftystr)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Invalid <FilterType> '%s'" ,ftystr); }; | |||
2336 | sta=422; | |||
2337 | goto error; | |||
2338 | } | |||
2339 | } | |||
2340 | // - field level filter | |||
2341 | if (aTargetFilter->field) { | |||
2342 | /// @todo %%% to be implemented | |||
2343 | PDEBUGPRINTFX(DBG_ERROR,("Field-level filtering not supported")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Field-level filtering not supported" ); }; | |||
2344 | sta=406; | |||
2345 | goto error; | |||
2346 | } | |||
2347 | // - record level filter | |||
2348 | if (aTargetFilter->record) { | |||
2349 | #ifdef OBJECT_FILTERING1 | |||
2350 | SmlItemPtr_t recordItemP = aTargetFilter->record->item; | |||
2351 | if (recordItemP) { | |||
2352 | // - check grammar | |||
2353 | const char *grammarstr = smlMetaTypeToCharP(smlPCDataToMetInfP(recordItemP->meta)); | |||
2354 | if (strucmp(grammarstr,SYNCML_FILTERTYPE_CGI"syncml:filtertype-cgi")!=0) { | |||
2355 | PDEBUGPRINTFX(DBG_ERROR,("Invalid filter grammar '%s'",grammarstr)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Invalid filter grammar '%s'" ,grammarstr); }; | |||
2356 | sta=422; | |||
2357 | goto error; | |||
2358 | } | |||
2359 | // now get the actual filter string | |||
2360 | const char *filterstring = smlPCDataToCharP(recordItemP->data); | |||
2361 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Remote specified %sCLUSIVE filter query: '%s'" , inclusiveFilter ? "IN" : "EX", filterstring ); } | |||
2362 | "Remote specified %sCLUSIVE filter query: '%s'",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Remote specified %sCLUSIVE filter query: '%s'" , inclusiveFilter ? "IN" : "EX", filterstring ); } | |||
2363 | inclusiveFilter ? "IN" : "EX",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Remote specified %sCLUSIVE filter query: '%s'" , inclusiveFilter ? "IN" : "EX", filterstring ); } | |||
2364 | filterstring{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Remote specified %sCLUSIVE filter query: '%s'" , inclusiveFilter ? "IN" : "EX", filterstring ); } | |||
2365 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Remote specified %sCLUSIVE filter query: '%s'" , inclusiveFilter ? "IN" : "EX", filterstring ); }; | |||
2366 | if (*filterstring) { | |||
2367 | string f; | |||
2368 | // parse it | |||
2369 | filterstring = parseFilterCGI(filterstring,itemTypeP,f); | |||
2370 | if (*filterstring) { | |||
2371 | // not read to end | |||
2372 | PDEBUGPRINTFX(DBG_ERROR,("filter query syntax error at: '%s'",filterstring)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("filter query syntax error at: '%s'" ,filterstring); }; | |||
2373 | sta=422; | |||
2374 | goto error; | |||
2375 | } | |||
2376 | /// @todo: %%% check if this is correct interpretation | |||
2377 | // - exclusive is what we used to call "sync set" filtering | |||
2378 | // - inclusive seems to be former TAF | |||
2379 | if (inclusiveFilter) { | |||
2380 | // INCLUSIVE | |||
2381 | #ifdef SYNCML_TAF_SUPPORT | |||
2382 | fTargetAddressFilter=f; | |||
2383 | #else | |||
2384 | PDEBUGPRINTFX(DBG_ERROR,("This SyncML engine version has no INCLUSIVE filter support")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("This SyncML engine version has no INCLUSIVE filter support" ); }; | |||
2385 | #endif | |||
2386 | } | |||
2387 | else { | |||
2388 | // EXCLUSIVE | |||
2389 | addToFilter(f.c_str(),fSyncSetFilter,false); // AND chaining | |||
2390 | PDEBUGPRINTFX(DBG_FILTER,("complete sync set filter is now: '%s'",fSyncSetFilter.c_str())){ if (((0x08000000) & getDbgMask()) == (0x08000000)) getDbgLogger ()->setNextMask(0x08000000).DebugPrintfLastMask ("complete sync set filter is now: '%s'" ,fSyncSetFilter.c_str()); }; | |||
2391 | // call this once to give derivate a chance to see if it can filter the now set fSyncSetFilter | |||
2392 | engFilteredFetchesFromDB(true); | |||
2393 | } | |||
2394 | } | |||
2395 | } // if item | |||
2396 | #else | |||
2397 | // no object filtering | |||
2398 | PDEBUGPRINTFX(DBG_ERROR,("This SyncML engine version has no filter support (only PRO has)")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("This SyncML engine version has no filter support (only PRO has)" ); }; | |||
2399 | sta=406; | |||
2400 | goto error; | |||
2401 | #endif | |||
2402 | } // record | |||
2403 | } // filter at all | |||
2404 | error: | |||
2405 | return sta; | |||
2406 | } // TLocalEngineDS::engProcessDS12Filter | |||
2407 | ||||
2408 | ||||
2409 | // process Sync alert from remote party: check if alert code is supported, | |||
2410 | // check if slow sync is needed due to anchor mismatch | |||
2411 | // - server case: also generate appropriate Alert acknowledge command | |||
2412 | TAlertCommand *TLocalEngineDS::engProcessSyncAlert( | |||
2413 | TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore | |||
2414 | uInt16 aAlertCode, // the alert code | |||
2415 | const char *aLastRemoteAnchor, // last anchor of remote | |||
2416 | const char *aNextRemoteAnchor, // next anchor of remote | |||
2417 | const char *aTargetURI, // target URI as sent by remote, no processing at all | |||
2418 | const char *aIdentifyingTargetURI, // target URI that was used to identify datastore | |||
2419 | const char *aTargetURIOptions, // option string contained in target URI | |||
2420 | SmlFilterPtr_t aTargetFilter, // DS 1.2 filter, NULL if none | |||
2421 | const char *aSourceURI, // source URI | |||
2422 | TStatusCommand &aStatusCommand // status that might be modified | |||
2423 | ) | |||
2424 | { | |||
2425 | TAlertCommand *alertcmdP=NULL__null; | |||
| ||||
2426 | localstatus sta=LOCERR_OK; | |||
2427 | ||||
2428 | SYSYNC_TRYtry { | |||
2429 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2430 | // save the identifying URI | |||
2431 | fIdentifyingDBName = aIdentifyingTargetURI; | |||
2432 | } | |||
2433 | // determine status of read-only option | |||
2434 | fReadOnly= | |||
2435 | fSessionP->getReadOnly() || // session level read-only flag (probably set by login) | |||
2436 | fDSConfigP->fReadOnly; // or datastore config | |||
2437 | #ifdef SUPERDATASTORES1 | |||
2438 | // if running as subdatastore of a superdatastore already, this call mus be from a superdatastore as well (aAsSubDatastoreOf!=NULL) | |||
2439 | // Note: On a client, fAsSubDatastoreOf is set earlier in dsSetClientSyncParams() | |||
2440 | // On a server, fAsSubDatastoreOf will be set now to avoid alerting as sub- and normal datastore at the same time. | |||
2441 | if (fAsSubDatastoreOf && !aAsSubDatastoreOf) { | |||
2442 | // bad, cannot be alerted directly AND as subdatastore | |||
2443 | aStatusCommand.setStatusCode(400); | |||
2444 | ADDDEBUGITEM(aStatusCommand,"trying to alert already alerted subdatastore"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("trying to alert already alerted subdatastore" ); }; | |||
2445 | PDEBUGPRINTFX(DBG_ERROR,("Already alerted as subdatastore of '%s'",fAsSubDatastoreOf->getName())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Already alerted as subdatastore of '%s'" ,fAsSubDatastoreOf->getName()); }; | |||
2446 | return NULL__null; | |||
2447 | } | |||
2448 | // set subdatastore mode | |||
2449 | fAsSubDatastoreOf = aAsSubDatastoreOf; | |||
2450 | #endif | |||
2451 | // reset type info | |||
2452 | fLocalSendToRemoteTypeP = NULL__null; | |||
2453 | fLocalReceiveFromRemoteTypeP = NULL__null; | |||
2454 | fRemoteReceiveFromLocalTypeP=NULL__null; | |||
2455 | fRemoteSendToLocalTypeP=NULL__null; | |||
2456 | // prepare database-level scripts | |||
2457 | // NOTE: in client case, alertprepscript is already rebuilt here! | |||
2458 | #ifdef SCRIPT_SUPPORT1 | |||
2459 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fDBInitScript,fDataStoreScriptContextP,fSessionP); | |||
2460 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fSentItemStatusScript,fDataStoreScriptContextP,fSessionP); | |||
2461 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fReceivedItemStatusScript,fDataStoreScriptContextP,fSessionP); | |||
2462 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fAlertScript,fDataStoreScriptContextP,fSessionP); | |||
2463 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fDBFinishScript,fDataStoreScriptContextP,fSessionP,true); // now instantiate vars | |||
2464 | #endif | |||
2465 | // NOTE for client case: | |||
2466 | // ALL instantiated datastores have already sent an Alert to the server by now here | |||
2467 | ||||
2468 | // check DS 1.2 <filter> | |||
2469 | sta = engProcessDS12Filter(aTargetFilter); | |||
2470 | if (sta != LOCERR_OK) { | |||
2471 | aStatusCommand.setStatusCode(sta); | |||
2472 | ADDDEBUGITEM(aStatusCommand,"Invalid <Filter> in target options"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Invalid <Filter> in target options"); }; | |||
2473 | return NULL__null; // error in options | |||
2474 | } | |||
2475 | ||||
2476 | // Filter CGI is now a combination of TAF and Synthesis-Style | |||
2477 | // extras (options). | |||
2478 | if (aTargetURIOptions && *aTargetURIOptions) { | |||
2479 | // there are target address options (such as filter CGI and TAF) | |||
2480 | sta = engParseOptions(aTargetURIOptions,false); | |||
2481 | if (sta != LOCERR_OK) { | |||
2482 | aStatusCommand.setStatusCode(sta); | |||
2483 | ADDDEBUGITEM(aStatusCommand,"Invalid CGI target URI options"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Invalid CGI target URI options"); }; | |||
2484 | return NULL__null; // error in options | |||
2485 | } | |||
2486 | } | |||
2487 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2488 | // server case: initially we are not in refresh only mode. Alert code or alert script could change this | |||
2489 | fRefreshOnly=false; | |||
2490 | fCacheData=false; | |||
2491 | } | |||
2492 | ||||
2493 | // save it for suspend and reference in scripts | |||
2494 | fAlertCode=aAlertCode; | |||
2495 | #ifdef SCRIPT_SUPPORT1 | |||
2496 | // call the alert script, which might want to force a slow sync and/or a server sync set zap | |||
2497 | TScriptContext::execute( | |||
2498 | fDataStoreScriptContextP, | |||
2499 | fDSConfigP->fAlertScript, | |||
2500 | &DBFuncTable, | |||
2501 | this // caller context | |||
2502 | ); | |||
2503 | aAlertCode=fAlertCode; // get possibly modified version back (SETALERTCODE) | |||
2504 | #endif | |||
2505 | // if we process a sync alert now, we haven't started sync or map generation | |||
2506 | #ifdef SYSYNC_SERVER1 | |||
2507 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2508 | // server case: forget Temp GUID mapping | |||
2509 | // make sure we are not carrying forward any left-overs. Last sessions's tempGUID mappings that are | |||
2510 | // needed for "early map" resolution might be loaded by the call to engInitSyncAnchors below. | |||
2511 | // IMPORTANT NOTE: the tempGUIDs that might get loaded will become invalid as soon as <Sync> | |||
2512 | // starts - so fTempGUIDMap needs to be cleared again as soon as the first <Sync> command arrives from the client. | |||
2513 | fTempGUIDMap.clear(); | |||
2514 | } | |||
2515 | #endif | |||
2516 | // save remote's next anchor for saving at end of session | |||
2517 | fNextRemoteAnchor = aNextRemoteAnchor; | |||
2518 | // get target info in case we are server | |||
2519 | #ifdef SYSYNC_SERVER1 | |||
2520 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2521 | // now get anchor info out of database | |||
2522 | // - make sure other anchor variables are set | |||
2523 | sta = engInitSyncAnchors( | |||
2524 | aIdentifyingTargetURI, // use processed form, not as sent by remote | |||
2525 | aSourceURI | |||
2526 | ); | |||
2527 | if (sta!=LOCERR_OK) { | |||
2528 | // error getting anchors | |||
2529 | aStatusCommand.setStatusCode(syncmlError(sta)); | |||
2530 | PDEBUGPRINTFX(DBG_ERROR,("Could not get Sync Anchor info, status=%hd",sta)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Could not get Sync Anchor info, status=%hd" ,sta); }; | |||
2531 | return NULL__null; // no alert to send back | |||
2532 | } | |||
2533 | // Server ok until here | |||
2534 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2535 | "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2536 | fLastRemoteAnchor.c_str(),{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2537 | aLastRemoteAnchor{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2538 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Client Anchor='%s', received <last> Remote Client Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); }; | |||
2539 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2540 | "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2541 | fNextRemoteAnchor.c_str(){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2542 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Client Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); }; | |||
2543 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
2544 | "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
2545 | fLastLocalAnchor.c_str(),{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
2546 | fNextLocalAnchor.c_str(){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
2547 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Server Anchor='%s', (generated) Next Local Server Anchor='%s' (sent to client as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); }; | |||
2548 | } | |||
2549 | #endif | |||
2550 | #ifdef SYSYNC_CLIENT1 | |||
2551 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
2552 | // Client ok until here | |||
2553 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2554 | "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2555 | fLastRemoteAnchor.c_str(),{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2556 | aLastRemoteAnchor{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); } | |||
2557 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Saved Last Remote Server Anchor='%s', received <last> Remote Server Anchor='%s' (must match for normal sync)" , fLastRemoteAnchor.c_str(), aLastRemoteAnchor ); }; | |||
2558 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2559 | "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2560 | fNextRemoteAnchor.c_str(){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); } | |||
2561 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Received <next> Remote Server Anchor='%s' (to be compared with <last> in NEXT session)" , fNextRemoteAnchor.c_str() ); }; | |||
2562 | } | |||
2563 | #endif | |||
2564 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)" , fResumeAlertCode ); } | |||
2565 | "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)" , fResumeAlertCode ); } | |||
2566 | fResumeAlertCode{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)" , fResumeAlertCode ); } | |||
2567 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) fResumeAlertCode = %hd (valid for >DS 1.2 only)" , fResumeAlertCode ); }; | |||
2568 | // Now check for resume | |||
2569 | // - default to what was actually alerted | |||
2570 | uInt16 effectiveAlertCode=aAlertCode; | |||
2571 | #ifdef SYSYNC_SERVER1 | |||
2572 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2573 | // - check if resuming server session | |||
2574 | fResuming=false; | |||
2575 | if (aAlertCode==225) { | |||
2576 | if (fSessionP->getSyncMLVersion()<syncml_vers_1_2) { | |||
2577 | aStatusCommand.setStatusCode(406); | |||
2578 | ADDDEBUGITEM(aStatusCommand,"Resume not supported in SyncML prior to 1.2"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Resume not supported in SyncML prior to 1.2") ; }; | |||
2579 | PDEBUGPRINTFX(DBG_ERROR,("Resume not supported in SyncML prior to 1.2")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Resume not supported in SyncML prior to 1.2" ); }; | |||
2580 | return NULL__null; | |||
2581 | } | |||
2582 | // Resume requested | |||
2583 | if (fResumeAlertCode==0 || !dsResumeSupportedInDB()) { | |||
2584 | // cannot resume, suggest a normal sync (in case anchors do not match, this will become a 508 below) | |||
2585 | aStatusCommand.setStatusCode(509); // cannot resume, override | |||
2586 | effectiveAlertCode=200; // suggest normal sync | |||
2587 | ADDDEBUGITEM(aStatusCommand,"Cannot resume, suggesting a normal sync"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Cannot resume, suggesting a normal sync"); }; | |||
2588 | PDEBUGPRINTFX(DBG_ERROR,("Cannot resume, suggesting a normal sync")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Cannot resume, suggesting a normal sync" ); }; | |||
2589 | } | |||
2590 | else { | |||
2591 | // we can resume, use the saved alert code | |||
2592 | effectiveAlertCode=fResumeAlertCode; | |||
2593 | PDEBUGPRINTFX(DBG_HOT,("Alerted to resume previous session, Switching to alert Code = %hd",fResumeAlertCode)){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Alerted to resume previous session, Switching to alert Code = %hd" ,fResumeAlertCode); }; | |||
2594 | fResuming=true; | |||
2595 | } | |||
2596 | } | |||
2597 | } | |||
2598 | #endif | |||
2599 | // now do the actual alert internally | |||
2600 | if (sta==LOCERR_OK) { | |||
2601 | // check if we can process the alert | |||
2602 | // NOTE: for client this might cause a change in Sync mode, if server | |||
2603 | // alerts something different than client alerted before. | |||
2604 | // Note that a client will keep fromserver mode even if server | |||
2605 | // changes to two-way, as it might be that we have sent the server | |||
2606 | // a two-way alert even if we want fromserver due to compatibility with | |||
2607 | // servers that cannot do fromserver. | |||
2608 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2609 | // - Server always obeys what client requests (that is, if alertscript does not modify it) | |||
2610 | sta = setSyncModeFromAlertCode(effectiveAlertCode,false); // as server | |||
2611 | } // server | |||
2612 | else { | |||
2613 | // - for client, check that server can't switch to a client writing mode | |||
2614 | // (we had a case when mobical did that for a user and erased all his data) | |||
2615 | TSyncModes prevMode = fSyncMode; // remember previous mode | |||
2616 | sta = setSyncModeFromAlertCode(effectiveAlertCode,true); // as client | |||
2617 | if (prevMode==smo_fromclient && fSyncMode!=smo_fromclient) { | |||
2618 | // server tries to switch to a mode that could be writing data to the client | |||
2619 | // - forbidden | |||
2620 | sta=403; | |||
2621 | aStatusCommand.setStatusCode(syncmlError(sta)); | |||
2622 | ADDDEBUGITEM(aStatusCommand,"Server may not write to client"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Server may not write to client"); }; | |||
2623 | PDEBUGPRINTFX(DBG_ERROR,("From-Client only: Server may not alert mode that writes client data (%hd) - blocked",effectiveAlertCode)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("From-Client only: Server may not alert mode that writes client data (%hd) - blocked" ,effectiveAlertCode); }; | |||
2624 | // - also abort sync of this datastore without chance to resume, as this problem might | |||
2625 | // originate from a resume attempt, which the server | |||
2626 | // tried to convert to a normal or slow sync. With cancelling the resume here, we make sure | |||
2627 | // next sync will start over and sending the desired (one-way) sync mode again. | |||
2628 | engAbortDataStoreSync(sta, false, false); // remote problem, not resumable | |||
2629 | } | |||
2630 | } // client | |||
2631 | if (!isAborted()) { | |||
2632 | if (sta!=LOCERR_OK) { | |||
2633 | // Sync type not supported | |||
2634 | // - go back to idle | |||
2635 | changeState(dssta_idle,true); // force to idle | |||
2636 | aStatusCommand.setStatusCode(syncmlError(sta)); | |||
2637 | ADDDEBUGITEM(aStatusCommand,"Sync type not supported"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Sync type not supported"); }; | |||
2638 | PDEBUGPRINTFX(DBG_ERROR,("Sync type (Alert code %hd) not supported, err=%hd",effectiveAlertCode,sta)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Sync type (Alert code %hd) not supported, err=%hd" ,effectiveAlertCode,sta); }; | |||
2639 | } | |||
2640 | } | |||
2641 | } | |||
2642 | if (sta==LOCERR_OK) { | |||
2643 | // Sync type supported | |||
2644 | // - set new state to alerted | |||
2645 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
2646 | changeState(dssta_clientalerted,true); // force it | |||
2647 | } | |||
2648 | else { | |||
2649 | changeState(dssta_serveralerted,true); // force it | |||
2650 | } | |||
2651 | // - datastore state is now dss_alerted | |||
2652 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2653 | "Alerted (code=%hd) for %s%s %s%s%s%s ",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2654 | aAlertCode,{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2655 | fResuming ? "Resumed " : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2656 | SyncModeDescriptions[fSyncMode],{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2657 | fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2658 | fReadOnly ? " (Readonly)" : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2659 | fCacheData ? " (Cache)" : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2660 | fRefreshOnly ? " (Refreshonly)" : ""{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); } | |||
2661 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerted (code=%hd) for %s%s %s%s%s%s " , aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" ); }; | |||
2662 | CONSOLEPRINTF((SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2663 | "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')",SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2664 | aAlertCode,SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2665 | fResuming ? "Resumed " : "",SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2666 | SyncModeDescriptions[fSyncMode],SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2667 | fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync",SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2668 | fReadOnly ? " (Readonly)" : "",SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2669 | fRefreshOnly ? " (Refreshonly)" : "",SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2670 | aTargetURI,SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2671 | aSourceURISySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ) | |||
2672 | ))SySync_ConsolePrintf(stderr, "SYSYNC " "- Remote alerts (%hd): %s%s %s%s%s for '%s' (source='%s')" "\n", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions [fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "" , fRefreshOnly ? " (Refreshonly)" : "", aTargetURI, aSourceURI ); | |||
2673 | // - now test if the sync state is same in client & server | |||
2674 | // (if saved anchor is empty, slow sync is needed anyway) | |||
2675 | if ( | |||
2676 | ( | |||
2677 | ( | |||
2678 | (!fLastRemoteAnchor.empty() && | |||
2679 | ( (fLastRemoteAnchor==aLastRemoteAnchor) | |||
2680 | #ifdef SYSYNC_CLIENT1 | |||
2681 | || (fSessionP->fLenientMode && IS_CLIENT(!getSyncAppBase()->isServer())) | |||
2682 | #endif | |||
2683 | ) | |||
2684 | ) || // either anchors must match (or lenient mode for client)... | |||
2685 | (fResuming && *aLastRemoteAnchor==0) // ...or in case of resume, remote not sending anchor is ok as well | |||
2686 | ) | |||
2687 | && !fForceSlowSync // ...but no force for slowsync may be set internally | |||
2688 | ) | |||
2689 | || fSlowSync // if slow sync is requested by the remote anyway, we don't need to be in sync anyway, so just go on | |||
2690 | ) { | |||
2691 | if (!(fLastRemoteAnchor==aLastRemoteAnchor) && fSessionP->fLenientMode) { | |||
2692 | PDEBUGPRINTFX(DBG_ERROR,("Warning - remote anchor mismatch but tolerated in lenient mode")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning - remote anchor mismatch but tolerated in lenient mode" ); }; | |||
2693 | } | |||
2694 | // sync state ok or Slow sync requested anyway: | |||
2695 | #ifdef SYSYNC_SERVER1 | |||
2696 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2697 | // we can generate Alert with same code as sent | |||
2698 | // %%% Note: this is not entirely clear, as SCTS sends | |||
2699 | // corresponding SERVER ALERTED code back. | |||
2700 | // Specs suggest that we send the code back unmodified | |||
2701 | uInt16 alertCode = getSyncStateAlertCode(fServerAlerted); | |||
2702 | alertcmdP = new TAlertCommand(fSessionP,this,alertCode); | |||
2703 | fAlertCode=alertCode; // save it for reference in scripts and for suspend/resume | |||
2704 | } | |||
2705 | #endif | |||
2706 | } | |||
2707 | else { | |||
2708 | // switch to slow sync | |||
2709 | fSlowSync=true; | |||
2710 | PDEBUGPRINTFX(DBG_HOT,("Switched to SlowSync because of Anchor mismatch or server-side user option")){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Switched to SlowSync because of Anchor mismatch or server-side user option" ); }; | |||
2711 | CONSOLEPRINTF(("- switched to SlowSync because of Sync Anchor mismatch"))SySync_ConsolePrintf(stderr, "SYSYNC " "- switched to SlowSync because of Sync Anchor mismatch" "\n"); | |||
2712 | // sync state not ok, we need slow sync | |||
2713 | aStatusCommand.setStatusCode(508); // Refresh required | |||
2714 | // update effective alert code | |||
2715 | uInt16 alertCode = getSyncStateAlertCode(false); | |||
2716 | fAlertCode=alertCode; // save it for reference in scripts and for suspend | |||
2717 | // NOTE: if client detected slow-sync not before here, status 508 alone | |||
2718 | // (without another Alert 201 sent to the server) is sufficient for | |||
2719 | // server to switch to slow sync. | |||
2720 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2721 | // generate Alert for Slow sync | |||
2722 | alertcmdP = new TAlertCommand(fSessionP,this,alertCode); | |||
2723 | } | |||
2724 | } | |||
2725 | // Now we are alerted for a sync | |||
2726 | // - reset item counters | |||
2727 | fItemsSent = 0; | |||
2728 | fItemsReceived = 0; | |||
2729 | #ifdef SYSYNC_SERVER1 | |||
2730 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2731 | // Server case | |||
2732 | // - show info | |||
2733 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2734 | "ALERTED from client for %s%s%s Sync",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2735 | fResuming ? "resumed " : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2736 | fSlowSync ? "slow" : "normal",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2737 | fFirstTimeSync ? " first time" : ""{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2738 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from client for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); }; | |||
2739 | // server: add Item with Anchors and URIs | |||
2740 | SmlItemPtr_t itemP = newItem(); | |||
2741 | // - anchors | |||
2742 | itemP->meta=newMetaAnchor(fNextLocalAnchor.c_str(),fLastLocalAnchor.c_str()); | |||
2743 | // - MaxObjSize here again to make SCTS happy | |||
2744 | if ( | |||
2745 | (fSessionP->getRootConfig()->fLocalMaxObjSize>0) && | |||
2746 | (fSessionP->getSyncMLVersion()>=syncml_vers_1_1) | |||
2747 | ) { | |||
2748 | // SyncML 1.1 has object size and we need to put it here for SCTS | |||
2749 | smlPCDataToMetInfP(itemP->meta)->maxobjsize=newPCDataLong( | |||
2750 | fSessionP->getRootConfig()->fLocalMaxObjSize | |||
2751 | ); | |||
2752 | } | |||
2753 | // - URIs (reversed from what was received in Alert) | |||
2754 | itemP->source=newLocation(aTargetURI); // use unprocessed form as sent by remote | |||
2755 | itemP->target=newLocation(aSourceURI); | |||
2756 | // - add to alert command | |||
2757 | alertcmdP->addItem(itemP); | |||
| ||||
2758 | // - set new state, alert now answered | |||
2759 | changeState(dssta_serveransweredalert,true); // force it | |||
2760 | } // server case | |||
2761 | #endif // SYSYNC_SERVER | |||
2762 | #ifdef SYSYNC_CLIENT1 | |||
2763 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
2764 | // Client case | |||
2765 | // - now sync mode is stable (late switch to slowsync has now occurred if any) | |||
2766 | changeState(dssta_syncmodestable,true); | |||
2767 | // - show info | |||
2768 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2769 | "ALERTED from server for %s%s%s Sync",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2770 | fResuming ? "resumed " : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2771 | fSlowSync ? "slow" : "normal",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2772 | fFirstTimeSync ? " first time" : ""{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
2773 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "ALERTED from server for %s%s%s Sync" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); }; | |||
2774 | } // client Case | |||
2775 | #endif // SYSYNC_CLIENT | |||
2776 | } | |||
2777 | // clear partial item if we definitely know we are not resuming | |||
2778 | if (!fResuming) { | |||
2779 | // not resuming - prevent that partial item is used in TSyncOpCommand | |||
2780 | fPartialItemState=pi_state_none; | |||
2781 | // free this space early (would be freed at session end anyway, but we don't need it any more now) | |||
2782 | if (fPIStoredDataAllocated) { | |||
2783 | smlLibFree(fPIStoredDataP); | |||
2784 | fPIStoredDataAllocated=false; | |||
2785 | } | |||
2786 | fPIStoredDataP=NULL__null; | |||
2787 | } | |||
2788 | // save name how remote adresses local database | |||
2789 | // (for sending same URI back in own Sync) | |||
2790 | fRemoteViewOfLocalURI = aTargetURI; // save it | |||
2791 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
2792 | fRemoteDBPath = aSourceURI; | |||
2793 | } | |||
2794 | if (sta!=LOCERR_OK) { | |||
2795 | // no alert command | |||
2796 | if (alertcmdP) delete alertcmdP; | |||
2797 | alertcmdP=NULL__null; | |||
2798 | aStatusCommand.setStatusCode(syncmlError(sta)); | |||
2799 | PDEBUGPRINTFX(DBG_HOT,("engProcessSyncAlert failed with status=%hd",sta)){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("engProcessSyncAlert failed with status=%hd" ,sta); }; | |||
2800 | } | |||
2801 | } | |||
2802 | SYSYNC_CATCH (...)catch(...) { | |||
2803 | // clean up locally owned objects | |||
2804 | if (alertcmdP) delete alertcmdP; | |||
2805 | SYSYNC_RETHROWthrow; | |||
2806 | SYSYNC_ENDCATCH} | |||
2807 | // return alert command, if any | |||
2808 | return alertcmdP; | |||
2809 | } // TLocalEngineDS::engProcessSyncAlert | |||
2810 | ||||
2811 | ||||
2812 | // process status received for sync alert | |||
2813 | bool TLocalEngineDS::engHandleAlertStatus(TSyError aStatusCode) | |||
2814 | { | |||
2815 | bool handled=false; | |||
2816 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
2817 | // for client, make sure we have just sent the alert | |||
2818 | if (!testState(dssta_clientsentalert,true)) return false; // cannot switch if server not alerted | |||
2819 | // anyway, we have seen the status | |||
2820 | changeState(dssta_clientalertstatused,true); // force it | |||
2821 | } | |||
2822 | else { | |||
2823 | // for server, check if client did combined init&sync | |||
2824 | if (fLocalDSState>=dssta_syncmodestable) { | |||
2825 | // must be combined init&sync | |||
2826 | if (aStatusCode!=200) { | |||
2827 | // everything except ok is not allowed here | |||
2828 | PDEBUGPRINTFX(DBG_ERROR,("In combined init&sync, Alert status must be ok (but is %hd)",aStatusCode)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("In combined init&sync, Alert status must be ok (but is %hd)" ,aStatusCode); }; | |||
2829 | dsAbortDatastoreSync(400,false); // remote problem | |||
2830 | } | |||
2831 | // aborted or not, status is handled | |||
2832 | return true; | |||
2833 | } | |||
2834 | // normal case with separate init: we need to have answered the alert here | |||
2835 | if (!testState(dssta_serveransweredalert,true)) return false; // cannot switch if server not alerted | |||
2836 | } // server case | |||
2837 | // now check status code | |||
2838 | if (aStatusCode==508) { | |||
2839 | // remote party needs slow sync | |||
2840 | PDEBUGPRINTFX(DBG_HOT,("engHandleAlertStatus: Remote party needs SlowSync, switching to slowsync (AFTER alert, cancelling possible Resume)")){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("engHandleAlertStatus: Remote party needs SlowSync, switching to slowsync (AFTER alert, cancelling possible Resume)" ); }; | |||
2841 | // Note: in server and client cases, this mode change may happen AFTER alert command exchange | |||
2842 | // - switch to slow sync | |||
2843 | fSlowSync=true; | |||
2844 | // - if we are late-forced to slow sync, this means that this cannot be a resume | |||
2845 | fResuming=false; | |||
2846 | // - update effective alert code that will be saved when this session gets suspended | |||
2847 | fAlertCode=getSyncStateAlertCode(fServerAlerted); | |||
2848 | handled=true; | |||
2849 | } | |||
2850 | else if (aStatusCode==200) { | |||
2851 | handled=true; | |||
2852 | } | |||
2853 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
2854 | // check for resume override by server | |||
2855 | if (!handled && fResuming) { | |||
2856 | // we have requested resume | |||
2857 | if (aStatusCode==509) { | |||
2858 | // resume not accepted by server, but overridden by another sync type | |||
2859 | fResuming=false; | |||
2860 | PDEBUGPRINTFX(DBG_ERROR,("engHandleAlertStatus: Server rejected Resume")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("engHandleAlertStatus: Server rejected Resume" ); }; | |||
2861 | handled=true; | |||
2862 | } | |||
2863 | } | |||
2864 | } | |||
2865 | else { | |||
2866 | // if we have handled it here, sync mode is now stable | |||
2867 | if (handled) { | |||
2868 | // if we get that far, sync mode for server is now stable AND we can receive cached maps | |||
2869 | changeState(dssta_syncmodestable,true); // force it, sync mode is now stable, no further changes are possible | |||
2870 | } | |||
2871 | } | |||
2872 | // no other status codes are supported at the datastore level | |||
2873 | if (!handled && aStatusCode>=400) { | |||
2874 | engAbortDataStoreSync(aStatusCode, false); // remote problem | |||
2875 | handled=true; | |||
2876 | } | |||
2877 | return handled; // status handled | |||
2878 | } // TLocalEngineDS::engHandleAlertStatus | |||
2879 | ||||
2880 | ||||
2881 | // initialize reception of syncop commands for datastore | |||
2882 | // Note: previously, this was implemented as initLocalDatastoreSync in syncsession | |||
2883 | localstatus TLocalEngineDS::engInitForSyncOps( | |||
2884 | const char *aRemoteDatastoreURI // URI of remote datastore | |||
2885 | ) | |||
2886 | { | |||
2887 | localstatus sta = LOCERR_OK; | |||
2888 | ||||
2889 | // no default types | |||
2890 | TSyncItemType *LocalSendToRemoteTypeP=NULL__null; // used by local to send to remote | |||
2891 | TSyncItemType *RemoteReceiveFromLocalTypeP=NULL__null; // used by remote to receive from local | |||
2892 | TSyncItemType *LocalReceiveFromRemoteTypeP=NULL__null; // used by local to receive from remote | |||
2893 | TSyncItemType *RemoteSendToLocalTypeP=NULL__null; // used by remote to send to local | |||
2894 | ||||
2895 | // Now determine remote datastore | |||
2896 | // Note: It might be that this was called already earlier in the session, so | |||
2897 | // the link between local and remote datastore might already exist | |||
2898 | if (fRemoteDatastoreP==NULL__null) { | |||
2899 | // try to locate it by name and set it - in case of superdatastore, it will be set in all subdatastores | |||
2900 | engSetRemoteDatastore(fSessionP->findRemoteDataStore(aRemoteDatastoreURI)); | |||
2901 | } | |||
2902 | else { | |||
2903 | // There is a remote datastore already associated | |||
2904 | #ifdef SYDEBUG2 | |||
2905 | // - make a sanity check to see if sepcified remote URI matches | |||
2906 | if(fRemoteDatastoreP!=fSessionP->findRemoteDataStore(aRemoteDatastoreURI)) { | |||
2907 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS." , aRemoteDatastoreURI, fRemoteDatastoreP->getName() ); } | |||
2908 | "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS.",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS." , aRemoteDatastoreURI, fRemoteDatastoreP->getName() ); } | |||
2909 | aRemoteDatastoreURI,{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS." , aRemoteDatastoreURI, fRemoteDatastoreP->getName() ); } | |||
2910 | fRemoteDatastoreP->getName(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS." , aRemoteDatastoreURI, fRemoteDatastoreP->getName() ); } | |||
2911 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Warning: Received remote DS LocURI '%s' does not match already associated DS '%s'. We use the associated DS." , aRemoteDatastoreURI, fRemoteDatastoreP->getName() ); }; | |||
2912 | } | |||
2913 | #endif | |||
2914 | } | |||
2915 | // Now create a dummy remote data store for a blind sync attempt | |||
2916 | if (!fRemoteDatastoreP) { | |||
2917 | // no such remote datastore for this local datastore known, create one (or fail) | |||
2918 | #ifdef REMOTE_DS_MUST_BE_IN_DEVINF | |||
2919 | if (fSessionP->fRemoteDataStoresKnown) { | |||
2920 | // we have received devinf, but still can't find remote data store: error | |||
2921 | // Note: we had to disable this because of bugs in smartner server | |||
2922 | PDEBUGPRINTFX(DBG_ERROR,("Remote datastore name '%s' not found in received DevInf",aRemoteDatastoreURI)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Remote datastore name '%s' not found in received DevInf" ,aRemoteDatastoreURI); }; | |||
2923 | return 404; | |||
2924 | } | |||
2925 | else | |||
2926 | #else | |||
2927 | if (fSessionP->fRemoteDataStoresKnown) { | |||
2928 | // we have received devinf, but still can't find remote data store: | |||
2929 | // just show in log, but continue as if there was no devInf received at all | |||
2930 | PDEBUGPRINTFX(DBG_ERROR,("Warning: Remote datastore name '%s' not found in received DevInf.",aRemoteDatastoreURI)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: Remote datastore name '%s' not found in received DevInf." ,aRemoteDatastoreURI); }; | |||
2931 | } | |||
2932 | #endif | |||
2933 | { | |||
2934 | // We couldn't retrieve DevInf (or !REMOTE_DS_MUST_BE_IN_DEVINF), so we have to try blind | |||
2935 | // - check remote specifics here if we had no devinf (there might be default remote | |||
2936 | // rules to apply or checking license restrictions | |||
2937 | // - this is executed only once per session, after that, we'll be fRemoteDevInfLock-ed | |||
2938 | if (!fSessionP->fRemoteDevInfKnown && !fSessionP->fRemoteDevInfLock) { | |||
2939 | // detect client specific server behaviour if needed | |||
2940 | sta = fSessionP->checkRemoteSpecifics(NULL__null, NULL__null); | |||
2941 | fSessionP->remoteAnalyzed(); // analyzed now (accepted or not does not matter) | |||
2942 | if (sta!=LOCERR_OK) | |||
2943 | return sta; // not ok, device rejected | |||
2944 | } | |||
2945 | // default data types are those preferred by local datastore (or explicitly marked for blind sync attempts) | |||
2946 | if (getDSConfig()->fTypeSupport.fPreferredLegacy) { | |||
2947 | // we have a preferred type for blind sync attempts | |||
2948 | LocalSendToRemoteTypeP = getSession()->findLocalType(getDSConfig()->fTypeSupport.fPreferredLegacy); | |||
2949 | LocalReceiveFromRemoteTypeP = LocalSendToRemoteTypeP; | |||
2950 | } | |||
2951 | else { | |||
2952 | // no specific "blind" preference, use my own normally preferred types | |||
2953 | LocalSendToRemoteTypeP = getPreferredTxItemType(); // send in preferred tx type of local datastore | |||
2954 | LocalReceiveFromRemoteTypeP = getPreferredRxItemType(); // receive in preferred rx type of local datastore | |||
2955 | } | |||
2956 | // same type on both end (as only local type exists) | |||
2957 | RemoteReceiveFromLocalTypeP = LocalSendToRemoteTypeP; // same on both end | |||
2958 | RemoteSendToLocalTypeP = LocalReceiveFromRemoteTypeP; // same on both end (as only local type exists) | |||
2959 | // create "remote" datastore with matching properties to local one | |||
2960 | PDEBUGPRINTFX(DBG_ERROR,("Warning: No DevInf for remote datastore, running blind sync attempt")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: No DevInf for remote datastore, running blind sync attempt" ); }; | |||
2961 | TRemoteDataStore *remDsP; | |||
2962 | MP_NEW(remDsP,DBG_OBJINST,"TRemoteDataStore",TRemoteDataStore(remDsP = new TRemoteDataStore( fSessionP, aRemoteDatastoreURI , 0 ) | |||
2963 | fSessionP,remDsP = new TRemoteDataStore( fSessionP, aRemoteDatastoreURI , 0 ) | |||
2964 | aRemoteDatastoreURI, // remote name of datastoreremDsP = new TRemoteDataStore( fSessionP, aRemoteDatastoreURI , 0 ) | |||
2965 | 0 // standard Sync capsremDsP = new TRemoteDataStore( fSessionP, aRemoteDatastoreURI , 0 ) | |||
2966 | ))remDsP = new TRemoteDataStore( fSessionP, aRemoteDatastoreURI , 0 ); | |||
2967 | // - set it (in case of superdatastore in all subdatastores as well) | |||
2968 | engSetRemoteDatastore(remDsP); | |||
2969 | // add type support | |||
2970 | fRemoteDatastoreP->setPreferredTypes( | |||
2971 | RemoteReceiveFromLocalTypeP, // remote receives in preferred tx type of local datastore | |||
2972 | RemoteSendToLocalTypeP // remote sends in preferred rx type of local datastore | |||
2973 | ); | |||
2974 | // add it to the remote datastore list | |||
2975 | fSessionP->fRemoteDataStores.push_back(fRemoteDatastoreP); | |||
2976 | // make sure late devInf arriving won't supersede our artificially created remote datastore any more | |||
2977 | fSessionP->fRemoteDevInfLock=true; | |||
2978 | } | |||
2979 | } | |||
2980 | else { | |||
2981 | // found remote DB, determine default data exchange types | |||
2982 | // - common types for sending data to remote | |||
2983 | LocalSendToRemoteTypeP=getTypesForTxTo(fRemoteDatastoreP,&RemoteReceiveFromLocalTypeP); | |||
2984 | // - common types for receiving data from remote | |||
2985 | LocalReceiveFromRemoteTypeP=getTypesForRxFrom(fRemoteDatastoreP,&RemoteSendToLocalTypeP); | |||
2986 | } | |||
2987 | #ifndef NO_REMOTE_RULES | |||
2988 | // check if rule match type will override what we found so far | |||
2989 | if (!fSessionP->fActiveRemoteRules.empty()) { | |||
2990 | // have a look at our rulematch types | |||
2991 | TRuleMatchTypesContainer::iterator pos; | |||
2992 | TSyncItemType *ruleMatchTypeP = NULL__null; | |||
2993 | for (pos=fRuleMatchItemTypes.begin();pos!=fRuleMatchItemTypes.end();++pos) { | |||
2994 | // there is a rule applied | |||
2995 | // - parse match string in format "rule[,rule]..." with * and ? wildcards allowed in "rule" | |||
2996 | cAppCharP p=(*pos).ruleMatchString; | |||
2997 | while (*p!=0) { | |||
2998 | // split at commas | |||
2999 | cAppCharP e=strchr(p,','); | |||
3000 | size_t n; | |||
3001 | if (e) { | |||
3002 | n=e-p; | |||
3003 | e++; | |||
3004 | } | |||
3005 | else { | |||
3006 | n=strlen(p); | |||
3007 | e=p+n; | |||
3008 | } | |||
3009 | // see if that matches with any of the active rules | |||
3010 | TRemoteRulesList::iterator apos; | |||
3011 | for(apos=fSessionP->fActiveRemoteRules.begin();apos!=fSessionP->fActiveRemoteRules.end();apos++) { | |||
3012 | if (strwildcmp((*apos)->getName(), p, 0, n)==0) { | |||
3013 | ruleMatchTypeP=(*pos).itemTypeP; // get the matching type | |||
3014 | break; | |||
3015 | } | |||
3016 | } | |||
3017 | if (ruleMatchTypeP) break; // found a rule match type | |||
3018 | // test next match target | |||
3019 | p=e; | |||
3020 | } | |||
3021 | // apply if found one already | |||
3022 | if (ruleMatchTypeP) { | |||
3023 | // use this instead of normal types | |||
3024 | // - local types | |||
3025 | LocalSendToRemoteTypeP=ruleMatchTypeP; // used by local to send to remote | |||
3026 | LocalReceiveFromRemoteTypeP=ruleMatchTypeP; // used by local to receive from remote | |||
3027 | // Find matching remote types | |||
3028 | // - first look for existing remote type with same config as local one | |||
3029 | TSyncItemType *remCorrTypeP = fSessionP->findRemoteType(ruleMatchTypeP->getTypeConfig(),fRemoteDatastoreP); | |||
3030 | // - if none found, create one and have it inherit the CTCap options of the generic version that is already there | |||
3031 | if (!remCorrTypeP) { | |||
3032 | // none found: need to create one | |||
3033 | remCorrTypeP = ruleMatchTypeP->newCopyForSameType(fSessionP,fRemoteDatastoreP); | |||
3034 | if (remCorrTypeP) { | |||
3035 | // - get generic remote type (the one that might have received CTCap already) | |||
3036 | TSyncItemType *remGenericTypeP = fRemoteDatastoreP->getSendType(ruleMatchTypeP); | |||
3037 | // - copy options | |||
3038 | if (remGenericTypeP) remCorrTypeP->copyCTCapInfoFrom(*remGenericTypeP); | |||
3039 | } | |||
3040 | } | |||
3041 | // now assign | |||
3042 | RemoteReceiveFromLocalTypeP=remCorrTypeP; | |||
3043 | RemoteSendToLocalTypeP=remCorrTypeP; | |||
3044 | // Show that we are using ruleMatch type | |||
3045 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,({ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "An active remote rule overrides default type usage - forcing type '%s' for send and receive" , ruleMatchTypeP->getTypeConfig()->getName() ); } | |||
3046 | "An active remote rule overrides default type usage - forcing type '%s' for send and receive",{ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "An active remote rule overrides default type usage - forcing type '%s' for send and receive" , ruleMatchTypeP->getTypeConfig()->getName() ); } | |||
3047 | ruleMatchTypeP->getTypeConfig()->getName(){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "An active remote rule overrides default type usage - forcing type '%s' for send and receive" , ruleMatchTypeP->getTypeConfig()->getName() ); } | |||
3048 | )){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "An active remote rule overrides default type usage - forcing type '%s' for send and receive" , ruleMatchTypeP->getTypeConfig()->getName() ); }; | |||
3049 | // done | |||
3050 | break; | |||
3051 | } | |||
3052 | } | |||
3053 | } | |||
3054 | #endif | |||
3055 | // check if we are sync compatible (common type for both directions) | |||
3056 | if (LocalSendToRemoteTypeP && LocalReceiveFromRemoteTypeP && RemoteReceiveFromLocalTypeP && RemoteSendToLocalTypeP) { | |||
3057 | // avoid further changes in remote devInf (e.g. by late result of GET, sent *after* first <sync>) | |||
3058 | fSessionP->fRemoteDevInfLock=true; | |||
3059 | // there is a common data type for each of both directions | |||
3060 | // - show local types | |||
3061 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); } | |||
3062 | "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); } | |||
3063 | getName(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); } | |||
3064 | LocalSendToRemoteTypeP->getTypeConfig()->getName(),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP->getTypeVers(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); } | |||
3065 | LocalReceiveFromRemoteTypeP->getTypeConfig()->getName(),LocalReceiveFromRemoteTypeP->getTypeName(), LocalReceiveFromRemoteTypeP->getTypeVers(){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); } | |||
3066 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Local Datastore '%s' - Types: tx to remote: '%s': %s (%s), rx from remote: '%s': %s (%s)" , getName(), LocalSendToRemoteTypeP->getTypeConfig()->getName (),LocalSendToRemoteTypeP->getTypeName(), LocalSendToRemoteTypeP ->getTypeVers(), LocalReceiveFromRemoteTypeP->getTypeConfig ()->getName(),LocalReceiveFromRemoteTypeP->getTypeName( ), LocalReceiveFromRemoteTypeP->getTypeVers() ); }; | |||
3067 | // - show remote types | |||
3068 | PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,({ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); } | |||
3069 | "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)",{ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); } | |||
3070 | fRemoteDatastoreP->getName(),{ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); } | |||
3071 | RemoteSendToLocalTypeP->getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName(), RemoteSendToLocalTypeP->getTypeVers(),{ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); } | |||
3072 | RemoteReceiveFromLocalTypeP->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers(){ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); } | |||
3073 | )){ if (((0x00000080 +0x40000000) & getDbgMask()) == (0x00000080 +0x40000000)) getDbgLogger()->setNextMask(0x00000080 +0x40000000 ).DebugPrintfLastMask ( "Remote Datastore '%s' - Types: tx to local: '%s': %s (%s), rx from local: '%s': %s (%s)" , fRemoteDatastoreP->getName(), RemoteSendToLocalTypeP-> getTypeConfig()->getName(),RemoteSendToLocalTypeP->getTypeName (), RemoteSendToLocalTypeP->getTypeVers(), RemoteReceiveFromLocalTypeP ->getTypeConfig()->getName(),RemoteReceiveFromLocalTypeP ->getTypeName(), RemoteReceiveFromLocalTypeP->getTypeVers () ); }; | |||
3074 | } | |||
3075 | else { | |||
3076 | // datastores are not sync compatible | |||
3077 | sta=415; | |||
3078 | PDEBUGPRINTFX(DBG_ERROR,("No common datastore formats -> cannot sync (415)")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("No common datastore formats -> cannot sync (415)" ); }; | |||
3079 | PDEBUGPRINTFX(DBG_EXOTIC,("- LocalSendToRemoteTypeP = '%s'", LocalSendToRemoteTypeP ? LocalSendToRemoteTypeP->getTypeName() : "<missing>")){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ("- LocalSendToRemoteTypeP = '%s'" , LocalSendToRemoteTypeP ? LocalSendToRemoteTypeP->getTypeName () : "<missing>"); }; | |||
3080 | PDEBUGPRINTFX(DBG_EXOTIC,("- LocalReceiveFromRemoteTypeP = '%s'", LocalReceiveFromRemoteTypeP ? LocalReceiveFromRemoteTypeP->getTypeName() : "<missing>")){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ("- LocalReceiveFromRemoteTypeP = '%s'" , LocalReceiveFromRemoteTypeP ? LocalReceiveFromRemoteTypeP-> getTypeName() : "<missing>"); }; | |||
3081 | PDEBUGPRINTFX(DBG_EXOTIC,("- RemoteSendToLocalTypeP = '%s'", RemoteSendToLocalTypeP ? RemoteSendToLocalTypeP->getTypeName() : "<missing>")){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ("- RemoteSendToLocalTypeP = '%s'" , RemoteSendToLocalTypeP ? RemoteSendToLocalTypeP->getTypeName () : "<missing>"); }; | |||
3082 | PDEBUGPRINTFX(DBG_EXOTIC,("- RemoteReceiveFromLocalTypeP = '%s'", RemoteReceiveFromLocalTypeP ? RemoteReceiveFromLocalTypeP->getTypeName() : "<missing>")){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ("- RemoteReceiveFromLocalTypeP = '%s'" , RemoteReceiveFromLocalTypeP ? RemoteReceiveFromLocalTypeP-> getTypeName() : "<missing>"); }; | |||
3083 | engAbortDataStoreSync(sta,true,false); // do not proceed with sync of this datastore, local problem, not resumable | |||
3084 | return sta; | |||
3085 | } | |||
3086 | // set type info in local datastore | |||
3087 | setSendTypeInfo(LocalSendToRemoteTypeP,RemoteReceiveFromLocalTypeP); | |||
3088 | setReceiveTypeInfo(LocalReceiveFromRemoteTypeP,RemoteSendToLocalTypeP); | |||
3089 | // - initialize usage of types (checks compatibility as well) | |||
3090 | return initDataTypeUse(); | |||
3091 | } // TLocalEngineDS::engInitForSyncOps | |||
3092 | ||||
3093 | ||||
3094 | // called from <sync> command to generate sync sub-commands to be sent to remote | |||
3095 | // Returns true if now finished for this datastore | |||
3096 | // also changes state to dssta_syncgendone when all sync commands have been generated | |||
3097 | bool TLocalEngineDS::engGenerateSyncCommands( | |||
3098 | TSmlCommandPContainer &aNextMessageCommands, | |||
3099 | TSmlCommand * &aInterruptedCommandP, | |||
3100 | const char *aLocalIDPrefix | |||
3101 | ) | |||
3102 | { | |||
3103 | PDEBUGBLOCKFMT(("SyncGen","Now generating sync commands","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockExpanded ("SyncGen","Now generating sync commands" ,"datastore=%s",getName()); | |||
3104 | bool finished=false; | |||
3105 | #ifdef SYSYNC_CLIENT1 | |||
3106 | if (IS_CLIENT(!getSyncAppBase()->isServer())) | |||
3107 | finished = logicGenerateSyncCommandsAsClient(aNextMessageCommands, aInterruptedCommandP, aLocalIDPrefix); | |||
3108 | #endif | |||
3109 | #ifdef SYSYNC_SERVER1 | |||
3110 | if (IS_SERVER(getSyncAppBase()->isServer())) | |||
3111 | finished = logicGenerateSyncCommandsAsServer(aNextMessageCommands, aInterruptedCommandP, aLocalIDPrefix); | |||
3112 | #endif | |||
3113 | // change state when finished | |||
3114 | if (finished) { | |||
3115 | changeState(dssta_syncgendone,true); | |||
3116 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
3117 | // from client only skips to clientmapssent without any server communication | |||
3118 | // (except if we are in old synthesis-compatible mode which runs from-client-only | |||
3119 | // with empty sync-from-server and map phases. | |||
3120 | if (getSyncMode()==smo_fromclient && !fSessionP->fCompleteFromClientOnly) { | |||
3121 | // data access ends with all sync commands generated in from-client-only | |||
3122 | PDEBUGPRINTFX(DBG_PROTO,("From-Client-Only sync: skipping directly to end of map phase now")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("From-Client-Only sync: skipping directly to end of map phase now" ); }; | |||
3123 | changeState(dssta_dataaccessdone,true); | |||
3124 | changeState(dssta_clientmapssent,true); | |||
3125 | } | |||
3126 | } | |||
3127 | } | |||
3128 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "engGenerateSyncCommands ended, state='%s', sync generation %sdone" , getDSStateName(), fLocalDSState>=dssta_syncgendone ? "" : "NOT " ); } | |||
3129 | "engGenerateSyncCommands ended, state='%s', sync generation %sdone",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "engGenerateSyncCommands ended, state='%s', sync generation %sdone" , getDSStateName(), fLocalDSState>=dssta_syncgendone ? "" : "NOT " ); } | |||
3130 | getDSStateName(),{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "engGenerateSyncCommands ended, state='%s', sync generation %sdone" , getDSStateName(), fLocalDSState>=dssta_syncgendone ? "" : "NOT " ); } | |||
3131 | fLocalDSState>=dssta_syncgendone ? "" : "NOT "{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "engGenerateSyncCommands ended, state='%s', sync generation %sdone" , getDSStateName(), fLocalDSState>=dssta_syncgendone ? "" : "NOT " ); } | |||
3132 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "engGenerateSyncCommands ended, state='%s', sync generation %sdone" , getDSStateName(), fLocalDSState>=dssta_syncgendone ? "" : "NOT " ); }; | |||
3133 | PDEBUGENDBLOCK("SyncGen")getDbgLogger()->DebugCloseBlock( "SyncGen"); | |||
3134 | return finished; | |||
3135 | } // TLocalEngineDS::engGenerateSyncCommands | |||
3136 | ||||
3137 | ||||
3138 | // called to confirm a sync operation's completion (status from remote received) | |||
3139 | // @note aSyncOp passed not necessarily reflects what was sent to remote, but what actually happened | |||
3140 | void TLocalEngineDS::dsConfirmItemOp(TSyncOperation aSyncOp, cAppCharP aLocalID, cAppCharP aRemoteID, bool aSuccess, localstatus aErrorStatus) | |||
3141 | { | |||
3142 | // commands failed with "cancelled" should be re-sent for resume | |||
3143 | if (!aSuccess && aErrorStatus==514 && dsResumeSupportedInDB() && fSessionP->isSuspending()) { | |||
3144 | // cancelled syncop as result of explicit suspend: mark for resume as it was never really processed at the other end | |||
3145 | PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,("Cancelled SyncOp during suspend -> mark for resume")){ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ("Cancelled SyncOp during suspend -> mark for resume" ); }; | |||
3146 | engMarkItemForResume(aLocalID,aRemoteID,true); | |||
3147 | } | |||
3148 | PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,({ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3149 | "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd",{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3150 | SyncOpNames[aSyncOp],{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3151 | aLocalID ? aLocalID : "<none>",{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3152 | aRemoteID ? aRemoteID : "<none>",{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3153 | aSuccess ? "SUCCESS" : "FAILURE",{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3154 | aErrorStatus{ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); } | |||
3155 | )){ if (((0x00000080 +0x80000000) & getDbgMask()) == (0x00000080 +0x80000000)) getDbgLogger()->setNextMask(0x00000080 +0x80000000 ).DebugPrintfLastMask ( "dsConfirmItemOp completed, syncop=%s, localID='%s', remoteID='%s', %s, errorstatus=%hd" , SyncOpNames[aSyncOp], aLocalID ? aLocalID : "<none>", aRemoteID ? aRemoteID : "<none>", aSuccess ? "SUCCESS" : "FAILURE", aErrorStatus ); }; | |||
3156 | } // TLocalEngineDS::dsConfirmItemOp | |||
3157 | ||||
3158 | ||||
3159 | ||||
3160 | // handle status of sync operation | |||
3161 | // Note: in case of superdatastore, status is always directed to the originating subdatastore, as | |||
3162 | // the fDataStoreP of the SyncOpCommand is set to subdatastore when generating the SyncOps. | |||
3163 | bool TLocalEngineDS::engHandleSyncOpStatus(TStatusCommand *aStatusCmdP,TSyncOpCommand *aSyncOpCmdP) | |||
3164 | { | |||
3165 | TSyError statuscode = aStatusCmdP->getStatusCode(); | |||
3166 | // we can make it simple here because we KNOW that we do not send multiple items per SyncOp, so we | |||
3167 | // just need to look at the first item's target and source | |||
3168 | const char *localID = aSyncOpCmdP->getSourceLocalID(); | |||
3169 | const char *remoteID = aSyncOpCmdP->getTargetRemoteID(); | |||
3170 | #ifdef SYSYNC_SERVER1 | |||
3171 | string realLocID; | |||
3172 | #endif | |||
3173 | if (localID) { | |||
3174 | #ifdef SUPERDATASTORES1 | |||
3175 | // remove possible prefix if this item was sent in the <sync> command context of a superdatastore | |||
3176 | if (fAsSubDatastoreOf) { | |||
3177 | // let superdatastore remove the prefix for me | |||
3178 | localID = fAsSubDatastoreOf->removeSubDSPrefix(localID,this); | |||
3179 | } | |||
3180 | #endif | |||
3181 | #ifdef SYSYNC_SERVER1 | |||
3182 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
3183 | // for server only: convert to internal representation | |||
3184 | realLocID=localID; | |||
3185 | obtainRealLocalID(realLocID); | |||
3186 | localID=realLocID.c_str(); | |||
3187 | } | |||
3188 | #endif | |||
3189 | } | |||
3190 | // handle special cases for Add/Replace/Delete | |||
3191 | TSyncOperation sop = aSyncOpCmdP->getSyncOp(); | |||
3192 | switch (sop) { | |||
3193 | case sop_wants_add: | |||
3194 | case sop_add: | |||
3195 | if (statuscode<300 || statuscode==419) { | |||
3196 | // All ok status 2xx as well as special "merged" 419 is ok for an add: | |||
3197 | // Whatever remote said, I know this is an add and so I counts this as such | |||
3198 | // (even if the remote somehow merged it with existing data, | |||
3199 | // it is obviously a new item in my sync set with this remote) | |||
3200 | fRemoteItemsAdded++; | |||
3201 | dsConfirmItemOp(sop_add,localID,remoteID,true); // ok added | |||
3202 | } | |||
3203 | else if ( | |||
3204 | statuscode==418 && | |||
3205 | (isResuming() | |||
3206 | || (isSlowSync() && IS_CLIENT(!getSyncAppBase()->isServer())) | |||
3207 | ) | |||
3208 | ) { | |||
3209 | // "Already exists"/418 is acceptable... | |||
3210 | // ... in slow sync as client, as some servers use it instead of 200/419 for slow sync match | |||
3211 | // ... during resumed sync as server with clients like Symbian which | |||
3212 | // can detect duplicate adds themselves. Should not generally | |||
3213 | // occur, as we shouldn't re-send them as long as we haven't seen | |||
3214 | // a map. But symbian cannot send early maps - it instead does | |||
3215 | // it's own duplicate checking. | |||
3216 | // ... during resumed sync as client (as servers might issue 418 for | |||
3217 | // items sent a second time after an implicit suspend) | |||
3218 | PDEBUGPRINTFX(DBG_ERROR,("Warning: received 418 status for add in resumed/slowsync session -> treat it as ok (200)")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: received 418 status for add in resumed/slowsync session -> treat it as ok (200)" ); }; | |||
3219 | dsConfirmItemOp(sop_replace,localID,remoteID,true); // kind of ok | |||
3220 | statuscode=200; // convert to ok (but no count incremented, as nothing changed) | |||
3221 | } | |||
3222 | else { | |||
3223 | dsConfirmItemOp(sop_add,localID,remoteID,false,statuscode); // failed add | |||
3224 | } | |||
3225 | // adding with 420 error: device full | |||
3226 | if (statuscode==420) { | |||
3227 | // special case: device indicates that it is full, so stop adding in this session | |||
3228 | PDEBUGPRINTFX(DBG_ERROR,("Warning: Status %hd: Remote device full -> preventing further adds in this session",statuscode)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: Status %hd: Remote device full -> preventing further adds in this session" ,statuscode); }; | |||
3229 | engStopAddingToRemote(); | |||
3230 | fRemoteItemsError++; // this is considered a remote item error | |||
3231 | } | |||
3232 | break; | |||
3233 | // case sop_copy: break; | |||
3234 | case sop_wants_replace: | |||
3235 | case sop_replace: | |||
3236 | #ifdef SYSYNC_SERVER1 | |||
3237 | if (IS_SERVER(getSyncAppBase()->isServer()) && (statuscode==404 || statuscode==410)) { | |||
3238 | // obviously, remote item that we wanted to change does not exist any more. | |||
3239 | // Instead of aborting the session we'll just remove the map item for that | |||
3240 | // server item, such that it will be re-added in the next sync session | |||
3241 | PDEBUGPRINTFX(DBG_DATA,("Status %hd: Replace target not found on client -> silently ignore but remove map in server (item will be added in next session), ",statuscode)){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Status %hd: Replace target not found on client -> silently ignore but remove map in server (item will be added in next session), " ,statuscode); }; | |||
3242 | // remove map for remote item(s), targetRef contain remoteIDs | |||
3243 | SmlTargetRefListPtr_t targetrefP = aStatusCmdP->getStatusElement()->targetRefList; | |||
3244 | while (targetrefP) { | |||
3245 | // target ref available | |||
3246 | engProcessMap(smlPCDataToCharP(targetrefP->targetRef),NULL__null); | |||
3247 | // next | |||
3248 | targetrefP=targetrefP->next; | |||
3249 | } | |||
3250 | statuscode=410; // always use "gone" status (even if we might have received a 404) | |||
3251 | dsConfirmItemOp(sop_replace,localID,remoteID,false,statuscode); | |||
3252 | break; | |||
3253 | } | |||
3254 | else | |||
3255 | #endif | |||
3256 | if (statuscode==201) { | |||
3257 | fRemoteItemsAdded++; | |||
3258 | dsConfirmItemOp(sop_add,localID,remoteID,true); // ok as add | |||
3259 | } | |||
3260 | else if (statuscode<300 || statuscode==419) { // conflict resolved counts as ok as well | |||
3261 | fRemoteItemsUpdated++; | |||
3262 | dsConfirmItemOp(sop_replace,localID,remoteID,true); // ok as replace | |||
3263 | } | |||
3264 | #ifdef SYSYNC_CLIENT1 | |||
3265 | else if (IS_CLIENT(!getSyncAppBase()->isServer()) && (isSlowSync() && statuscode==418)) { | |||
3266 | // "Already exists"/418 is acceptable as client in slow sync because some | |||
3267 | // servers use it instead of 200/419 for slow sync match | |||
3268 | PDEBUGPRINTFX(DBG_ERROR,("Warning: received 418 for for replace during slow sync - treat it as ok (200), but don't count as update")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: received 418 for for replace during slow sync - treat it as ok (200), but don't count as update" ); }; | |||
3269 | dsConfirmItemOp(sop_replace,localID,remoteID,true); // 418 is acceptable in slow sync (not really conformant, but e.g. happening with Scheduleworld) | |||
3270 | statuscode=200; // always use "gone" status (even if we might have received a 404) | |||
3271 | } | |||
3272 | #endif // SYSYNC_CLIENT | |||
3273 | else { | |||
3274 | dsConfirmItemOp(sop_replace,localID,remoteID,false,statuscode); // failed replace | |||
3275 | } | |||
3276 | break; | |||
3277 | case sop_archive_delete: | |||
3278 | case sop_soft_delete: | |||
3279 | case sop_delete: | |||
3280 | if (statuscode<211) fRemoteItemsDeleted++; | |||
3281 | // allow 211 and 404 for delete - after all, the record is not there | |||
3282 | // any more on the remote | |||
3283 | if (statuscode==404 || statuscode==211) { | |||
3284 | PDEBUGPRINTFX(DBG_DATA,("Status: %hd: To-be-deleted item not found, but accepted this (changed status to 200)",statuscode)){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Status: %hd: To-be-deleted item not found, but accepted this (changed status to 200)" ,statuscode); }; | |||
3285 | statuscode=200; | |||
3286 | } | |||
3287 | // if ok (explicit or implicit), we can confirm the delete | |||
3288 | dsConfirmItemOp(sop_delete,localID,remoteID,statuscode<300,statuscode); // counts as ok delete | |||
3289 | break; | |||
3290 | default: break; | |||
3291 | } // switch | |||
3292 | // check if we want to mark failed items for resend in the next session or abort | |||
3293 | bool resend = fDSConfigP->fResendFailing; // get default from config | |||
3294 | #ifdef SCRIPT_SUPPORT1 | |||
3295 | // let script check status code | |||
3296 | TErrorFuncContext errctx; | |||
3297 | errctx.statuscode = statuscode; | |||
3298 | errctx.resend = resend; | |||
3299 | errctx.newstatuscode = statuscode; | |||
3300 | errctx.syncop = sop; | |||
3301 | errctx.datastoreP = this; | |||
3302 | // - first check datastore level | |||
3303 | if ( | |||
3304 | TScriptContext::executeTest( | |||
3305 | false, // assume script does NOT handle status entirely | |||
3306 | fDataStoreScriptContextP, | |||
3307 | fDSConfigP->fSentItemStatusScript, | |||
3308 | &ErrorFuncTable, | |||
3309 | &errctx // caller context | |||
3310 | ) | |||
3311 | ) { | |||
3312 | // completely handled | |||
3313 | PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: Handled by datastore script (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: %hd: Handled by datastore script (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3314 | return true; | |||
3315 | } | |||
3316 | errctx.statuscode = errctx.newstatuscode; | |||
3317 | // - then check session level | |||
3318 | if ( | |||
3319 | TScriptContext::executeTest( | |||
3320 | false, // assume script does NOT handle status entirely | |||
3321 | fSessionP->fSessionScriptContextP, | |||
3322 | fSessionP->getSessionConfig()->fSentItemStatusScript, | |||
3323 | &ErrorFuncTable, | |||
3324 | &errctx // caller context | |||
3325 | ) | |||
3326 | ) { | |||
3327 | // completely handled | |||
3328 | PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: Handled by session script (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: %hd: Handled by session script (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3329 | return true; | |||
3330 | } | |||
3331 | // not completely handled, use possibly modified status code | |||
3332 | #ifdef SYDEBUG2 | |||
3333 | if (statuscode != errctx.newstatuscode) { | |||
3334 | PDEBUGPRINTFX(DBG_ERROR,("Status: Script changed original status=%hd to %hd (original op was %s)",statuscode,errctx.newstatuscode,SyncOpNames[errctx.syncop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: Script changed original status=%hd to %hd (original op was %s)" ,statuscode,errctx.newstatuscode,SyncOpNames[errctx.syncop]); }; | |||
3335 | } | |||
3336 | #endif | |||
3337 | statuscode = errctx.newstatuscode; | |||
3338 | resend = errctx.resend; | |||
3339 | #endif | |||
3340 | // now perform default action according to status code | |||
3341 | switch (statuscode) { | |||
3342 | case 200: | |||
3343 | break; | |||
3344 | case 201: | |||
3345 | PDEBUGPRINTFX(DBG_PROTO,("Status: %hd: Item added (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Status: %hd: Item added (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3346 | break; | |||
3347 | case 204: | |||
3348 | PDEBUGPRINTFX(DBG_PROTO,("Status: %hd: No content (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Status: %hd: No content (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3349 | break; | |||
3350 | case 207: | |||
3351 | case 208: | |||
3352 | case 209: | |||
3353 | case 419: | |||
3354 | PDEBUGPRINTFX(DBG_HOT,("Status: %hd: Conflict resolved (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Status: %hd: Conflict resolved (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3355 | break; | |||
3356 | case 210: | |||
3357 | PDEBUGPRINTFX(DBG_HOT,("Status: %hd: Delete without archive (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Status: %hd: Delete without archive (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3358 | break; | |||
3359 | case 211: | |||
3360 | PDEBUGPRINTFX(DBG_ERROR,("Status: %hd: nothing deleted, item not found (original op was %s)",statuscode,SyncOpNames[sop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: %hd: nothing deleted, item not found (original op was %s)" ,statuscode,SyncOpNames[sop]); }; | |||
3361 | break; | |||
3362 | case 410: // gone | |||
3363 | case 420: // device full | |||
3364 | // these have been handled above and are considered ok now | |||
3365 | break; | |||
3366 | case 514: // cancelled | |||
3367 | // ignore cancelling while suspending, as these are CAUSED by the suspend | |||
3368 | if (fSessionP->isSuspending() && dsResumeSupportedInDB()) { | |||
3369 | // don't do anything here - we'll be suspended later (but process commands until then) | |||
3370 | // dsConfirmItemOp() has already caused the item to be marked for resume | |||
3371 | break; | |||
3372 | } | |||
3373 | // for non-DS-1.2 sessions, we treat 514 like the other errors below (that is - retry might help) | |||
3374 | case 424: // size mismatch (e.g. due to faild partial item resume attempt -> retry will help) | |||
3375 | case 417: // retry later (remote says that retry will probably work) | |||
3376 | case 506: // processing error, retry later (remote says that retry will probably work) | |||
3377 | case 404: // not found (retry is not likely to help, but does not harm too much, either) | |||
3378 | case 408: // timeout (if that happens on a single item, retry probably helps) | |||
3379 | case 415: // bad type (retry is not likely to help, but does not harm too much, either) | |||
3380 | case 510: // datastore failure (too unspecific to know if retry might help, but why not?) | |||
3381 | case 500: // general failure (too unspecific to know if retry might help, but why not?) | |||
3382 | // these errors cause either a resend in a later session | |||
3383 | // or only abort the datastore, but not the session | |||
3384 | if (resend && dsResumeSupportedInDB()) { | |||
3385 | PDEBUGPRINTFX(DBG_ERROR,("Status: General error %hd (original op was %s) -> marking item for resend in next session",statuscode,SyncOpNames[sop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: General error %hd (original op was %s) -> marking item for resend in next session" ,statuscode,SyncOpNames[sop]); }; | |||
3386 | engMarkItemForResend(localID,remoteID); // Note: includes incrementing fRemoteItemsError | |||
3387 | } | |||
3388 | else { | |||
3389 | PDEBUGPRINTFX(DBG_ERROR,("Status: General error %hd (original op was %s) -> aborting sync with this datastore",statuscode,SyncOpNames[sop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: General error %hd (original op was %s) -> aborting sync with this datastore" ,statuscode,SyncOpNames[sop]); }; | |||
3390 | engAbortDataStoreSync(statuscode,false); // remote problem | |||
3391 | } | |||
3392 | break; | |||
3393 | default: | |||
3394 | // let command handle it | |||
3395 | return false; | |||
3396 | //break; | |||
3397 | } | |||
3398 | // status handled | |||
3399 | return true; // handled status | |||
3400 | } // TLocalEngineDS::engHandleSyncOpStatus | |||
3401 | ||||
3402 | ||||
3403 | /// Internal events during sync for derived classes | |||
3404 | /// @Note local DB authorisation must be established already before calling these | |||
3405 | /// - cause loading of all session anchoring info and other admin data (logicMakeAdminReady()) | |||
3406 | /// fLastRemoteAnchor,fLastLocalAnchor,fNextLocalAnchor; isFirstTimeSync() will be valid after the call | |||
3407 | /// - in case of superdatastore, consolidates the anchor information from the subdatastores | |||
3408 | localstatus TLocalEngineDS::engInitSyncAnchors( | |||
3409 | cAppCharP aDatastoreURI, ///< local datastore URI | |||
3410 | cAppCharP aRemoteDBID ///< ID of remote datastore (to find session information in local DB) | |||
3411 | ) | |||
3412 | { | |||
3413 | // nothing more to do than making admin data ready | |||
3414 | // - this will fill all dsSavedAdminData members here and in all derived classes | |||
3415 | localstatus sta=logicMakeAdminReady(aDatastoreURI, aRemoteDBID); | |||
3416 | if (sta==LOCERR_OK) { | |||
3417 | changeState(dssta_adminready); // admin data is now ready | |||
3418 | } | |||
3419 | // return on error | |||
3420 | return sta; | |||
3421 | } // TLocalEngineDS::engInitSyncAnchors | |||
3422 | ||||
3423 | ||||
3424 | #ifdef SYSYNC_CLIENT1 | |||
3425 | ||||
3426 | // initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams() | |||
3427 | localstatus TLocalEngineDS::engPrepareClientSyncAlert(void) | |||
3428 | { | |||
3429 | #ifdef SUPERDATASTORES1 | |||
3430 | // no operation here if running under control of a superdatastore. | |||
3431 | // superdatastore's engPrepareClientSyncAlert() will call engPrepareClientRealDSSyncAlert of all subdatastores at the right time | |||
3432 | if (fAsSubDatastoreOf) | |||
3433 | return LOCERR_OK; | |||
3434 | #endif | |||
3435 | // this is a real datastore | |||
3436 | return engPrepareClientDSForAlert(); | |||
3437 | } // TLocalEngineDS::engPrepareClientSyncAlert | |||
3438 | ||||
3439 | ||||
3440 | ||||
3441 | // initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams() | |||
3442 | localstatus TLocalEngineDS::engPrepareClientDSForAlert(void) | |||
3443 | { | |||
3444 | localstatus sta; | |||
3445 | ||||
3446 | // reset the filters that might be added to in alertprepscript | |||
3447 | // (as they might have been half set-up in a previous failed alert, they must be cleared and re-constructed here) | |||
3448 | resetFiltering(); | |||
3449 | ||||
3450 | #ifdef SCRIPT_SUPPORT1 | |||
3451 | // AlertPrepareScript to add filters and CGI | |||
3452 | // - rebuild early (before all of the other DS scripts in makeAdminReady caused by engInitSyncAnchors below!) | |||
3453 | TScriptContext::rebuildContext(fSessionP->getSyncAppBase(),fDSConfigP->fAlertPrepScript,fDataStoreScriptContextP,fSessionP); | |||
3454 | // - add custom DS 1.2 filters and/or custom CGI to fRemoteDBPath | |||
3455 | TScriptContext::execute( | |||
3456 | fDataStoreScriptContextP, | |||
3457 | fDSConfigP->fAlertPrepScript, | |||
3458 | fDSConfigP->getClientDBFuncTable(), // function table with extra | |||
3459 | this // datastore pointer needed for context | |||
3460 | ); | |||
3461 | #endif | |||
3462 | // - save the identifying name of the DB | |||
3463 | fIdentifyingDBName = fLocalDBPath; | |||
3464 | // - get information about last session out of database | |||
3465 | sta = engInitSyncAnchors( | |||
3466 | relativeURI(fLocalDBPath.c_str()), | |||
3467 | fRemoteDBPath.c_str() | |||
3468 | ); | |||
3469 | if (sta!=LOCERR_OK) { | |||
3470 | // error getting anchors | |||
3471 | PDEBUGPRINTFX(DBG_ERROR,("Could not get Sync Anchor info")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Could not get Sync Anchor info" ); }; | |||
3472 | return localError(sta); | |||
3473 | } | |||
3474 | // check if we are forced to slowsync (otherwise, fSlowSync is pre-set from dsSetClientSyncParams() | |||
3475 | fSlowSync = fSlowSync || fLastLocalAnchor.empty() || fFirstTimeSync; | |||
3476 | // check for resume | |||
3477 | if (fResumeAlertCode!=0 && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
3478 | // we have a suspended session, try to resume | |||
3479 | PDEBUGPRINTFX(DBG_PROTO,("Found suspended session with Alert Code = %hd",fResumeAlertCode)){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Found suspended session with Alert Code = %hd" ,fResumeAlertCode); }; | |||
3480 | fResuming = true; | |||
3481 | } | |||
3482 | return LOCERR_OK; // ok | |||
3483 | } // TLocalEngineDS::engPrepareClientDSForAlert | |||
3484 | ||||
3485 | ||||
3486 | // generate Sync alert for datastore after initialisation with engPrepareClientSyncAlert() | |||
3487 | // Note: this could be repeatedly called due to auth failures at beginning of session | |||
3488 | // Note: this is a NOP for subDatastores (should not be called in this case, anyway) | |||
3489 | localstatus TLocalEngineDS::engGenerateClientSyncAlert( | |||
3490 | TAlertCommand *&aAlertCommandP | |||
3491 | ) | |||
3492 | { | |||
3493 | aAlertCommandP=NULL__null; | |||
3494 | #ifdef SUPERDATASTORES1 | |||
3495 | if (fAsSubDatastoreOf) return LOCERR_OK; // NOP, ok, only superdatastore creates an alert! | |||
3496 | #endif | |||
3497 | ||||
3498 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
3499 | "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
3500 | fLastLocalAnchor.c_str(),{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
3501 | fNextLocalAnchor.c_str(){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); } | |||
3502 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "(Saved) Last Local Client Anchor='%s', (generated) Next Local Client Anchor='%s' (sent to server as <last>/<next> in <alert>)" , fLastLocalAnchor.c_str(), fNextLocalAnchor.c_str() ); }; | |||
3503 | // create appropriate initial alert command | |||
3504 | TAgentConfig *configP = static_cast<TAgentConfig *>(static_cast<TSyncAgent *>(getSession())->getRootConfig()->fAgentConfigP); | |||
3505 | uInt16 alertCode = getSyncStateAlertCode(fServerAlerted, configP->fPreferSlowSync); | |||
3506 | // check for resume | |||
3507 | if (fResuming) { | |||
3508 | // check if what we resume is same as what we wanted to do | |||
3509 | if (alertCode != fResumeAlertCode) { | |||
3510 | // this is ok for client, just show in log | |||
3511 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)" , alertCode, fResumeAlertCode ); } | |||
3512 | "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)" , alertCode, fResumeAlertCode ); } | |||
3513 | alertCode,{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)" , alertCode, fResumeAlertCode ); } | |||
3514 | fResumeAlertCode{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)" , alertCode, fResumeAlertCode ); } | |||
3515 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Sync mode seems to have changed (alert code = %hd) since last Suspend (alert code = %hd)" , alertCode, fResumeAlertCode ); }; | |||
3516 | } | |||
3517 | // resume | |||
3518 | alertCode=225; // resume | |||
3519 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Alerting resume of last sync session (original alert code = %hd)" , fResumeAlertCode ); } | |||
3520 | "Alerting resume of last sync session (original alert code = %hd)",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Alerting resume of last sync session (original alert code = %hd)" , fResumeAlertCode ); } | |||
3521 | fResumeAlertCode{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Alerting resume of last sync session (original alert code = %hd)" , fResumeAlertCode ); } | |||
3522 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Alerting resume of last sync session (original alert code = %hd)" , fResumeAlertCode ); }; | |||
3523 | } | |||
3524 | aAlertCommandP = new TAlertCommand(fSessionP,this,alertCode); | |||
3525 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3526 | "%s: ALERTING server for %s%s%s Sync",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3527 | getName(),{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3528 | fResuming ? "RESUMED " : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3529 | fSlowSync ? "slow" : "normal",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3530 | fFirstTimeSync ? " first time" : ""{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); } | |||
3531 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "%s: ALERTING server for %s%s%s Sync" , getName(), fResuming ? "RESUMED " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "" ); }; | |||
3532 | // add Item with Anchors and URIs | |||
3533 | SmlItemPtr_t itemP = newItem(); | |||
3534 | // - anchors | |||
3535 | itemP->meta=newMetaAnchor(fNextLocalAnchor.c_str(),fLastLocalAnchor.c_str()); | |||
3536 | // - MaxObjSize here again to make SCTS happy | |||
3537 | if ( | |||
3538 | (fSessionP->getSyncMLVersion()>=syncml_vers_1_1) && | |||
3539 | (fSessionP->getRootConfig()->fLocalMaxObjSize>0) | |||
3540 | ) { | |||
3541 | // SyncML 1.1 has object size and we need to put it here for SCTS | |||
3542 | smlPCDataToMetInfP(itemP->meta)->maxobjsize=newPCDataLong( | |||
3543 | fSessionP->getRootConfig()->fLocalMaxObjSize | |||
3544 | ); | |||
3545 | } | |||
3546 | // - URIs | |||
3547 | itemP->source=newLocation(fLocalDBPath.c_str()); // local DB ID | |||
3548 | itemP->target=newLocation(fRemoteDBPath.c_str()); // use remote path as configured in client settings | |||
3549 | // - add DS 1.2 filters | |||
3550 | if (!fRemoteRecordFilterQuery.empty() || false /* %%% field level filter */) { | |||
3551 | if (fSessionP->getSyncMLVersion()<syncml_vers_1_2) { | |||
3552 | PDEBUGPRINTFX(DBG_ERROR,("Filter specified, but SyncML version is < 1.2")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Filter specified, but SyncML version is < 1.2" ); }; | |||
3553 | engAbortDataStoreSync(406, true, false); // can't continue sync | |||
3554 | return 406; // feature not supported | |||
3555 | } | |||
3556 | SmlFilterPtr_t filterP = SML_NEW(SmlFilter_t)((SmlFilter_t*) _smlMalloc(sizeof(SmlFilter_t))); | |||
3557 | memset(filterP,0,sizeof(SmlFilter_t)); | |||
3558 | if (filterP) { | |||
3559 | // Must have a meta with the content type | |||
3560 | // - get the preferred receive type | |||
3561 | TSyncItemType *itemTypeP = fSessionP->findLocalType(fDSConfigP->fTypeSupport.fPreferredRx); | |||
3562 | if (itemTypeP) { | |||
3563 | // add meta type | |||
3564 | filterP->meta = newMetaType(itemTypeP->getTypeName()); | |||
3565 | } | |||
3566 | // add filtertype if needed (=not EXCLUSIVE) | |||
3567 | if (fRemoteFilterInclusive) { | |||
3568 | filterP->filtertype=newPCDataString(SYNCML_FILTERTYPE_INCLUSIVE"INCLUSIVE"); | |||
3569 | } | |||
3570 | // record level | |||
3571 | if (!fRemoteRecordFilterQuery.empty()) { | |||
3572 | // add <record> | |||
3573 | filterP->record = SML_NEW(SmlRecordOrFieldFilter_t)((SmlRecordOrFieldFilter_t*) _smlMalloc(sizeof(SmlRecordOrFieldFilter_t ))); | |||
3574 | // - add item with data=filterquery | |||
3575 | filterP->record->item = newStringDataItem(fRemoteRecordFilterQuery.c_str()); | |||
3576 | // - add meta type with grammar | |||
3577 | filterP->record->item->meta = newMetaType(SYNCML_FILTERTYPE_CGI"syncml:filtertype-cgi"); | |||
3578 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerting with %sCLUSIVE Record Level Filter Query = '%s'" , fRemoteFilterInclusive ? "IN" : "EX", fRemoteRecordFilterQuery .c_str() ); } | |||
3579 | "Alerting with %sCLUSIVE Record Level Filter Query = '%s'",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerting with %sCLUSIVE Record Level Filter Query = '%s'" , fRemoteFilterInclusive ? "IN" : "EX", fRemoteRecordFilterQuery .c_str() ); } | |||
3580 | fRemoteFilterInclusive ? "IN" : "EX",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerting with %sCLUSIVE Record Level Filter Query = '%s'" , fRemoteFilterInclusive ? "IN" : "EX", fRemoteRecordFilterQuery .c_str() ); } | |||
3581 | fRemoteRecordFilterQuery.c_str(){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerting with %sCLUSIVE Record Level Filter Query = '%s'" , fRemoteFilterInclusive ? "IN" : "EX", fRemoteRecordFilterQuery .c_str() ); } | |||
3582 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Alerting with %sCLUSIVE Record Level Filter Query = '%s'" , fRemoteFilterInclusive ? "IN" : "EX", fRemoteRecordFilterQuery .c_str() ); }; | |||
3583 | } | |||
3584 | // field level | |||
3585 | /// @todo %%% to be implemented | |||
3586 | if (false) { | |||
3587 | // !!! remember to add real check (now: false) in outer if-statement as well!!!!! | |||
3588 | // %%% tbd | |||
3589 | } | |||
3590 | } | |||
3591 | // add it to item | |||
3592 | itemP->target->filter = filterP; | |||
3593 | } | |||
3594 | // - add to alert command | |||
3595 | aAlertCommandP->addItem(itemP); | |||
3596 | // we have now produced the client alert command, change state | |||
3597 | return changeState(dssta_clientsentalert); | |||
3598 | } // TLocalEngineDS::engGenerateClientSyncAlert | |||
3599 | ||||
3600 | ||||
3601 | // Init engine for client sync | |||
3602 | // - determine types to exchange | |||
3603 | // - make sync set ready | |||
3604 | localstatus TLocalEngineDS::engInitForClientSync(void) | |||
3605 | { | |||
3606 | #ifdef SUPERDATASTORES1 | |||
3607 | // no init in case we are under control of a superdatastore -> the superdatastore will do that | |||
3608 | if (fAsSubDatastoreOf) | |||
3609 | return LOCERR_OK; | |||
3610 | #endif | |||
3611 | return engInitDSForClientSync(); | |||
3612 | } // TLocalEngineDS::engInitForClientSync | |||
3613 | ||||
3614 | ||||
3615 | ||||
3616 | // Init engine for client sync | |||
3617 | // - determine types to exchange | |||
3618 | // - make sync set ready | |||
3619 | localstatus TLocalEngineDS::engInitDSForClientSync(void) | |||
3620 | { | |||
3621 | // make ready for syncops | |||
3622 | localstatus sta = engInitForSyncOps(getRemoteDBPath()); | |||
3623 | if (sta==LOCERR_OK) { | |||
3624 | // - let local datastore (derived DB-specific class) prepare for sync | |||
3625 | sta = changeState(dssta_dataaccessstarted); | |||
3626 | if (sta==LOCERR_OK && isStarted(false)) { | |||
3627 | // already started now, change state | |||
3628 | sta = changeState(dssta_syncsetready); | |||
3629 | } | |||
3630 | } | |||
3631 | return sta; | |||
3632 | } // TLocalEngineDS::engInitDSForClientSync | |||
3633 | ||||
3634 | ||||
3635 | #endif // Client | |||
3636 | ||||
3637 | ||||
3638 | // get Alert code for current Sync State | |||
3639 | uInt16 TLocalEngineDS::getSyncStateAlertCode(bool aServerAlerted, bool aClientMinimal) | |||
3640 | { | |||
3641 | uInt16 alertcode=0; | |||
3642 | ||||
3643 | switch (fSyncMode) { | |||
3644 | case smo_twoway : | |||
3645 | alertcode = aServerAlerted ? 206 : 200; | |||
3646 | break; | |||
3647 | case smo_fromclient : | |||
3648 | alertcode = aServerAlerted ? 207 : 202; // fully specified | |||
3649 | break; | |||
3650 | case smo_fromserver : | |||
3651 | if (aClientMinimal) { | |||
3652 | // refresh from server is just client not sending any data, so we can alert like two-way | |||
3653 | alertcode = aServerAlerted ? 206 : 200; | |||
3654 | } | |||
3655 | else { | |||
3656 | // correctly alert it | |||
3657 | alertcode = aServerAlerted ? 209 : 204; | |||
3658 | } | |||
3659 | break; | |||
3660 | case numSyncModes: | |||
3661 | // invalid | |||
3662 | break; | |||
3663 | } | |||
3664 | // slowsync/refresh variants are always plus one, except 206 --> 201 (same as client initiated slow sync) | |||
3665 | if (fSlowSync) alertcode = (alertcode!=206 ? alertcode+1 : 201); | |||
3666 | return alertcode; | |||
3667 | } // TLocalEngineDS::getSyncStateAlertCode | |||
3668 | ||||
3669 | ||||
3670 | /// initializes Sync state variables and returns false if alert is not supported | |||
3671 | localstatus TLocalEngineDS::setSyncModeFromAlertCode(uInt16 aAlertCode, bool aAsClient) | |||
3672 | { | |||
3673 | localstatus sta; | |||
3674 | TSyncModes syncMode; | |||
3675 | bool slowSync, serverAlerted; | |||
3676 | // - translate into mode and flags | |||
3677 | sta=getSyncModeFromAlertCode(aAlertCode,syncMode,slowSync,serverAlerted); | |||
3678 | if (sta==LOCERR_OK) { | |||
3679 | // - set them | |||
3680 | sta=setSyncMode(aAsClient,syncMode,slowSync,serverAlerted); | |||
3681 | } | |||
3682 | return sta; | |||
3683 | } // TLocalEngineDS::setSyncModeFromAlertCode | |||
3684 | ||||
3685 | ||||
3686 | /// initializes Sync mode variables | |||
3687 | localstatus TLocalEngineDS::setSyncMode(bool aAsClient, TSyncModes aSyncMode, bool aIsSlowSync, bool aIsServerAlerted) | |||
3688 | { | |||
3689 | // get sync caps of this datastore | |||
3690 | uInt32 synccapmask=getSyncCapMask(); | |||
3691 | // check if mode supported | |||
3692 | if (aIsServerAlerted) { | |||
3693 | // check if we support server alerted modes, SyncCap/Bit=7 | |||
3694 | if (~synccapmask & (1<<7)) return 406; // not supported | |||
3695 | } | |||
3696 | switch(aSyncMode) { | |||
3697 | case smo_twoway: | |||
3698 | // Two-way Sync, SyncCap/Bit=1 | |||
3699 | // or Two-way slow Sync, SyncCap/Bit=2 | |||
3700 | if (~synccapmask & (1<< (aIsSlowSync ? 2 : 1))) return 406; // not supported | |||
3701 | if (fSyncMode==smo_fromserver && aAsClient) | |||
3702 | aSyncMode=smo_fromserver; // for client, if already fromserver mode set, keep it | |||
3703 | break; | |||
3704 | case smo_fromclient: | |||
3705 | // One-way from client, SyncCap/Bit=3 | |||
3706 | // or Refresh (=slow one-way) from client, SyncCap/Bit=4 | |||
3707 | if (~synccapmask & (1<< (aIsSlowSync ? 4 : 3))) return 406; // not supported | |||
3708 | if (!aAsClient) fRefreshOnly=true; // as server, we are in refresh-only-mode if we get one-way from client | |||
3709 | break; | |||
3710 | case smo_fromserver: | |||
3711 | // One-way from server, SyncCap/Bit=5 | |||
3712 | // or Refresh (=slow one-way) from server, SyncCap/Bit=6 | |||
3713 | if (~synccapmask & (1<< (aIsSlowSync ? 6 : 5))) return 406; // not supported | |||
3714 | if (aAsClient) fRefreshOnly=true; // as client, we are in refresh-only-mode if we get one-way fromm server | |||
3715 | break; | |||
3716 | default: | |||
3717 | return 400; // bad request | |||
3718 | } | |||
3719 | // now set mode and flags (possibly adjusted above) | |||
3720 | fSyncMode=aSyncMode; | |||
3721 | fSlowSync=aIsSlowSync; | |||
3722 | fServerAlerted=aIsServerAlerted; | |||
3723 | // ok | |||
3724 | return LOCERR_OK; | |||
3725 | } // TLocalEngineDS::setSyncMode | |||
3726 | ||||
3727 | ||||
3728 | /// get Sync mode variables from given alert code | |||
3729 | localstatus TLocalEngineDS::getSyncModeFromAlertCode(uInt16 aAlertCode, TSyncModes &aSyncMode, bool &aIsSlowSync, bool &aIsServerAlerted) | |||
3730 | { | |||
3731 | // these might be pre-defined) | |||
3732 | /// @deprecated state change does not belong here | |||
3733 | aIsSlowSync=false; | |||
3734 | aIsServerAlerted=false; | |||
3735 | aSyncMode=smo_twoway; // to make sure it is valid | |||
3736 | // first test if server-alerted | |||
3737 | if (aAlertCode>=206 && aAlertCode<210) { | |||
3738 | // Server alerted modes | |||
3739 | aIsServerAlerted=true; | |||
3740 | } | |||
3741 | // test for compatibility with alert code | |||
3742 | switch(aAlertCode) { | |||
3743 | case 200: | |||
3744 | case 206: | |||
3745 | // Two-way Sync | |||
3746 | aSyncMode=smo_twoway; | |||
3747 | aIsSlowSync=false; | |||
3748 | break; | |||
3749 | case 201: | |||
3750 | // Two-way slow Sync | |||
3751 | aSyncMode=smo_twoway; | |||
3752 | aIsSlowSync=true; | |||
3753 | break; | |||
3754 | case 202: | |||
3755 | case 207: | |||
3756 | // One-way from client | |||
3757 | aSyncMode=smo_fromclient; | |||
3758 | aIsSlowSync=false; | |||
3759 | break; | |||
3760 | case 203: | |||
3761 | case 208: | |||
3762 | // refresh (=slow one-way) from client | |||
3763 | aSyncMode=smo_fromclient; | |||
3764 | aIsSlowSync=true; | |||
3765 | break; | |||
3766 | case 204: | |||
3767 | case 209: | |||
3768 | // One-way from server | |||
3769 | aSyncMode=smo_fromserver; | |||
3770 | aIsSlowSync=false; | |||
3771 | break; | |||
3772 | case 205: | |||
3773 | case 210: | |||
3774 | // refresh (=slow one-way) from server | |||
3775 | aSyncMode=smo_fromserver; | |||
3776 | aIsSlowSync=true; | |||
3777 | break; | |||
3778 | default: | |||
3779 | // bad alert | |||
3780 | return 400; | |||
3781 | } | |||
3782 | return LOCERR_OK; | |||
3783 | } // TLocalEngineDS::getSyncModeFromAlertCode | |||
3784 | ||||
3785 | ||||
3786 | // create new Sync capabilities info from capabilities mask | |||
3787 | // Bit0=reserved, Bit1..Bitn = SyncType 1..n available | |||
3788 | // Note: derived classes might add special sync codes and/or mask standard ones | |||
3789 | SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) | |||
3790 | { | |||
3791 | SmlDevInfSyncCapPtr_t synccapP; | |||
3792 | SmlPcdataPtr_t synctypeP; | |||
3793 | ||||
3794 | // new synccap structure | |||
3795 | synccapP = SML_NEW(SmlDevInfSyncCap_t)((SmlDevInfSyncCap_t*) _smlMalloc(sizeof(SmlDevInfSyncCap_t)) ); | |||
3796 | // synccap list is empty | |||
3797 | synccapP->synctype=NULL__null; | |||
3798 | // now add standard synccaps | |||
3799 | for (sInt16 k=0; k<32; k++) { | |||
3800 | if (aSyncCapMask & (1<<k)) { | |||
3801 | // capability available | |||
3802 | synctypeP=newPCDataLong(k); | |||
3803 | addPCDataToList(synctypeP,&(synccapP->synctype)); | |||
3804 | } | |||
3805 | } | |||
3806 | // Now add non-standard synccaps. | |||
3807 | // From the spec: "Other values can also be specified." | |||
3808 | // Values are PCDATA, so we can use plain strings. | |||
3809 | // | |||
3810 | // But the Funambol server expects integer numbers and | |||
3811 | // throws a parser error when sent a string. So better | |||
3812 | // stick to a semi-random number (hopefully no-one else | |||
3813 | // is using it). | |||
3814 | // | |||
3815 | // Worse, Nokia phones cancel direct sync sessions with an | |||
3816 | // OBEX error ("Forbidden") when non-standard sync modes | |||
3817 | // are included in the SyncCap. | |||
3818 | // Event worse, some servers refuse to sync at | |||
3819 | // all with strange/misleading error codes when extensions are found. | |||
3820 | // Therefore, this is nothing to be enabled in general, | |||
3821 | // so it needs to be explicitly enabled in config from | |||
3822 | // 3.4.0.45 onwards (using <syncmodeextensions>yes</syncmodeextensions> | |||
3823 | // If it is enabled in config, the following logic is used: | |||
3824 | // - libsynthesis in a SyncML client will always send | |||
3825 | // all the extended sync modes; with the Funambol | |||
3826 | // workaround in place that works | |||
3827 | // - libsynthesis in a SyncML server will only send the | |||
3828 | // extended sync modes if the client has sent any | |||
3829 | // extended sync modes itself; the 390002 mode is | |||
3830 | // sent unconditionally for that purpose | |||
3831 | // | |||
3832 | // Corresponding code in TRemoteDataStore::setDatastoreDevInf(). | |||
3833 | // | |||
3834 | if ( | |||
3835 | fSessionP->getSessionConfig()->fSyncModeExtensions && // must be enabled in config | |||
3836 | (!IS_SERVER(getSyncAppBase()->isServer()) || fSessionP->receivedSyncModeExtensions()) // and if, server only uses it with clients which have it in their devInf as well | |||
3837 | ) { | |||
3838 | bool extended=false; | |||
3839 | if (canRestart()) { | |||
3840 | synctypeP=newPCDataString("390001"); | |||
3841 | addPCDataToList(synctypeP,&(synccapP->synctype)); | |||
3842 | extended=true; | |||
3843 | } | |||
3844 | // Finally add non-standard synccaps that are outside of the | |||
3845 | // engine's control. | |||
3846 | set<string> modes; | |||
3847 | getSyncModes(modes); | |||
3848 | for (set<string>::const_iterator it = modes.begin(); | |||
3849 | it != modes.end(); | |||
3850 | ++it) { | |||
3851 | synctypeP=newPCDataString(*it); | |||
3852 | addPCDataToList(synctypeP,&(synccapP->synctype)); | |||
3853 | extended=true; | |||
3854 | } | |||
3855 | ||||
3856 | // Add fake mode to signal peer that we support extensions. | |||
3857 | // Otherwise a server won't send them, to avoid breaking | |||
3858 | // client's (like Nokia phones) which don't. | |||
3859 | // | |||
3860 | // Don't send the fake mode unnecessarily (= when some other | |||
3861 | // non-standard modes where already added), because Funambol seems | |||
3862 | // to have a hard-coded limit of 9 entries in the <SyncCap> and | |||
3863 | // complains with a 513 internal server error (when using WBXML) | |||
3864 | // or a 'Expected "CTCap" end tag, found "CTType" end tag' (when | |||
3865 | // using XML). | |||
3866 | if (!extended) { | |||
3867 | synctypeP=newPCDataString("390002"); | |||
3868 | addPCDataToList(synctypeP,&(synccapP->synctype)); | |||
3869 | } | |||
3870 | } | |||
3871 | ||||
3872 | // return it | |||
3873 | return synccapP; | |||
3874 | } // TLocalEngineDS::newDevInfSyncCap | |||
3875 | ||||
3876 | ||||
3877 | // obtain new datastore info, returns NULL if none available | |||
3878 | SmlDevInfDatastorePtr_t TLocalEngineDS::newDevInfDatastore(bool aAsServer, bool aWithoutCTCapProps) | |||
3879 | { | |||
3880 | SmlDevInfDatastorePtr_t datastoreP; | |||
3881 | ||||
3882 | // set only basic info, details must be added in derived class | |||
3883 | // - sourceref is the name of the datastore, | |||
3884 | // or for server, if already alerted, the name used in the alert | |||
3885 | // (this is to allow /dsname/foldername with clients that expect the | |||
3886 | // devInf to contain exactly the same full path as name, like newer Nokias) | |||
3887 | string dotname; | |||
3888 | #ifdef SYSYNC_SERVER1 | |||
3889 | if (IS_SERVER(getSyncAppBase()->isServer()) && testState(dssta_serveralerted,false) && fSessionP->fDSPathInDevInf) { | |||
3890 | // server and already alerted | |||
3891 | // - don't include sub-datastores | |||
3892 | if (fAsSubDatastoreOf) { | |||
3893 | return NULL__null; | |||
3894 | } | |||
3895 | ||||
3896 | // - use datastore spec as sent from remote, minus CGI, as relative spec | |||
3897 | dotname = URI_RELPREFIX"./"; | |||
3898 | dotname += fSessionP->SessionRelativeURI(fRemoteViewOfLocalURI.c_str()); | |||
3899 | if (!fSessionP->fDSCgiInDevInf) { | |||
3900 | // remove CGI | |||
3901 | string::size_type n=dotname.find("?"); | |||
3902 | if (n!=string::npos) | |||
3903 | dotname.resize(n); // remove CGI | |||
3904 | } | |||
3905 | } | |||
3906 | else | |||
3907 | #endif | |||
3908 | { | |||
3909 | // client or not yet alerted - just use datastore base name | |||
3910 | StringObjPrintf(dotname,URI_RELPREFIX"./" "%s",fName.c_str()); | |||
3911 | } | |||
3912 | ||||
3913 | // create new | |||
3914 | datastoreP=SML_NEW(SmlDevInfDatastore_t)((SmlDevInfDatastore_t*) _smlMalloc(sizeof(SmlDevInfDatastore_t ))); | |||
3915 | datastoreP->sourceref=newPCDataString(dotname); | |||
3916 | #ifndef MINIMAL_CODE | |||
3917 | // - Optional display name | |||
3918 | datastoreP->displayname=newPCDataOptString(getDisplayName()); | |||
3919 | #else | |||
3920 | datastoreP->displayname=NULL__null; | |||
3921 | #endif | |||
3922 | // - MaxGUIDsize (for client only) | |||
3923 | if (aAsServer) | |||
3924 | datastoreP->maxguidsize=NULL__null; | |||
3925 | else | |||
3926 | datastoreP->maxguidsize=newPCDataLong(fMaxGUIDSize); | |||
3927 | // - check for legacy mode type (that is to be used as "preferred" instead of normal preferred) | |||
3928 | TSyncItemType *legacyTypeP = NULL__null; | |||
3929 | if (getSession()->fLegacyMode && getDSConfig()->fTypeSupport.fPreferredLegacy) { | |||
3930 | // get the type marked as blind | |||
3931 | legacyTypeP = getSession()->findLocalType(getDSConfig()->fTypeSupport.fPreferredLegacy); | |||
3932 | } | |||
3933 | // - RxPref | |||
3934 | if (!fRxPrefItemTypeP) SYSYNC_THROW(TStructException("Datastore has no RxPref ItemType"))throw TStructException("Datastore has no RxPref ItemType"); | |||
3935 | datastoreP->rxpref = (legacyTypeP ? legacyTypeP : fRxPrefItemTypeP)->newXMitDevInf(); | |||
3936 | // - Rx (excluding the type we report as preferred) | |||
3937 | datastoreP->rx=TSyncItemType::newXMitListDevInf(fRxItemTypes,legacyTypeP ? legacyTypeP : fRxPrefItemTypeP); | |||
3938 | // - TxPref | |||
3939 | if (!fTxPrefItemTypeP) SYSYNC_THROW(TStructException("Datastore has no TxPref ItemType"))throw TStructException("Datastore has no TxPref ItemType"); | |||
3940 | datastoreP->txpref = (legacyTypeP ? legacyTypeP : fTxPrefItemTypeP)->newXMitDevInf(); | |||
3941 | // - Tx (excluding the type we report as preferred) | |||
3942 | datastoreP->tx=TSyncItemType::newXMitListDevInf(fTxItemTypes,legacyTypeP ? legacyTypeP : fTxPrefItemTypeP); | |||
3943 | // - DSMem | |||
3944 | /// @todo %%% tbd: add dsmem | |||
3945 | datastoreP->dsmem=NULL__null; | |||
3946 | // - SyncML DS 1.2 datastore-local CTCap | |||
3947 | if (fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
3948 | // CTCap is local to datastore, get only CTCaps relevant for this datastore, but independently from alerted state | |||
3949 | datastoreP->ctcap = fSessionP->newLocalCTCapList(false, this, aWithoutCTCapProps); | |||
3950 | } | |||
3951 | else | |||
3952 | datastoreP->ctcap=NULL__null; // before SyncML 1.2, there was no datastore-local CTCap | |||
3953 | // - SyncML DS 1.2 flags (SmlDevInfHierarchical_f) | |||
3954 | /// @todo %%% tbd: add SmlDevInfHierarchical_f | |||
3955 | datastoreP->flags=0; | |||
3956 | // - SyncML DS 1.2 filters | |||
3957 | datastoreP->filterrx=NULL__null; | |||
3958 | datastoreP->filtercap=NULL__null; | |||
3959 | #ifdef OBJECT_FILTERING1 | |||
3960 | if (IS_SERVER(getSyncAppBase()->isServer()) && fDSConfigP->fDS12FilterSupport && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
3961 | // Show Filter info in 1.2 devInf if this is not a client | |||
3962 | // - FilterRx constant | |||
3963 | datastoreP->filterrx = SML_NEW(SmlDevInfXmitList_t)((SmlDevInfXmitList_t*) _smlMalloc(sizeof(SmlDevInfXmitList_t ))); | |||
3964 | datastoreP->filterrx->next = NULL__null; | |||
3965 | datastoreP->filterrx->data = SML_NEW(SmlDevInfXmit_t)((SmlDevInfXmit_t*) _smlMalloc(sizeof(SmlDevInfXmit_t))); | |||
3966 | datastoreP->filterrx->data->cttype=newPCDataString(SYNCML_FILTERTYPE_CGI"syncml:filtertype-cgi"); | |||
3967 | datastoreP->filterrx->data->verct=newPCDataString(SYNCML_FILTERTYPE_CGI_VERS"1.0"); | |||
3968 | // build filtercap | |||
3969 | SmlPcdataListPtr_t filterkeywords = NULL__null; | |||
3970 | SmlPcdataListPtr_t filterprops = NULL__null; | |||
3971 | // - fill the lists from types | |||
3972 | addFilterCapPropsAndKeywords(filterkeywords,filterprops); | |||
3973 | // - if we have something, actually build a filtercap | |||
3974 | if (filterkeywords || filterprops) { | |||
3975 | // we have filtercaps, add them | |||
3976 | // - FilterCap list | |||
3977 | datastoreP->filtercap = SML_NEW(SmlDevInfFilterCapList_t)((SmlDevInfFilterCapList_t*) _smlMalloc(sizeof(SmlDevInfFilterCapList_t ))); | |||
3978 | datastoreP->filtercap->next = NULL__null; | |||
3979 | datastoreP->filtercap->data = SML_NEW(SmlDevInfFilterCap_t)((SmlDevInfFilterCap_t*) _smlMalloc(sizeof(SmlDevInfFilterCap_t ))); | |||
3980 | datastoreP->filtercap->data->cttype=newPCDataString(SYNCML_FILTERTYPE_CGI"syncml:filtertype-cgi"); | |||
3981 | datastoreP->filtercap->data->verct=newPCDataString(SYNCML_FILTERTYPE_CGI_VERS"1.0"); | |||
3982 | // - add list we've got | |||
3983 | datastoreP->filtercap->data->filterkeyword=filterkeywords; | |||
3984 | datastoreP->filtercap->data->propname=filterprops; | |||
3985 | } | |||
3986 | } | |||
3987 | #endif // OBJECT_FILTERING | |||
3988 | // - Sync capabilities of this datastore | |||
3989 | datastoreP->synccap=newDevInfSyncCap(getSyncCapMask()); | |||
3990 | // return it | |||
3991 | return datastoreP; | |||
3992 | } // TLocalEngineDS::newDevInfDatastore | |||
3993 | ||||
3994 | ||||
3995 | // Set remote datastore for local | |||
3996 | void TLocalEngineDS::engSetRemoteDatastore( | |||
3997 | TRemoteDataStore *aRemoteDatastoreP // the remote datastore involved | |||
3998 | ) | |||
3999 | { | |||
4000 | // save link to remote datastore | |||
4001 | if (fRemoteDatastoreP) { | |||
4002 | if (fRemoteDatastoreP!=aRemoteDatastoreP) | |||
4003 | SYSYNC_THROW(TSyncException("Sync continues with different datastore"))throw TSyncException("Sync continues with different datastore" ); | |||
4004 | } | |||
4005 | fRemoteDatastoreP=aRemoteDatastoreP; | |||
4006 | } // TLocalEngineDS::engSetRemoteDatastore | |||
4007 | ||||
4008 | ||||
4009 | // SYNC command bracket start (check credentials if needed) | |||
4010 | bool TLocalEngineDS::engProcessSyncCmd( | |||
4011 | SmlSyncPtr_t aSyncP, // the Sync element | |||
4012 | TStatusCommand &aStatusCommand, // status that might be modified | |||
4013 | bool &aQueueForLater // will be set if command must be queued for later (re-)execution | |||
4014 | ) | |||
4015 | { | |||
4016 | // get number of changes info if available | |||
4017 | if (aSyncP->noc) { | |||
4018 | StrToLong(smlPCDataToCharP(aSyncP->noc),fRemoteNumberOfChanges); | |||
4019 | } | |||
4020 | // check if datastore is aborted | |||
4021 | if (CheckAborted(aStatusCommand)) return false; | |||
4022 | // check | |||
4023 | if (!fRemoteDatastoreP) | |||
4024 | SYSYNC_THROW(TSyncException("No remote datastore linked"))throw TSyncException("No remote datastore linked"); | |||
4025 | // let remote datastore process it first | |||
4026 | if (!fRemoteDatastoreP->remoteProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater)) { | |||
4027 | PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: remote datastore failed processing <sync>")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("TLocalEngineDS::engProcessSyncCmd: remote datastore failed processing <sync>" ); }; | |||
4028 | changeState(dssta_idle,true); // force it | |||
4029 | return false; | |||
4030 | } | |||
4031 | // check for combined init&sync | |||
4032 | if (!testState(dssta_syncmodestable,false)) { | |||
4033 | // <sync> encountered before sync mode stable: could be combined init&sync session | |||
4034 | if (fLocalDSState>=dssta_serveransweredalert) { | |||
4035 | // ok for switching to combined init&sync | |||
4036 | PDEBUGPRINTFX(DBG_HOT,("TLocalEngineDS::engProcessSyncCmd: detected combined init&sync, freeze sync mode already now")){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("TLocalEngineDS::engProcessSyncCmd: detected combined init&sync, freeze sync mode already now" ); }; | |||
4037 | // - freeze sync mode as it is now | |||
4038 | changeState(dssta_syncmodestable,true); // force it, sync mode is now stable, no further changes are possible | |||
4039 | } | |||
4040 | } | |||
4041 | // now init if this is the first <sync> command | |||
4042 | bool startingNow = false; // assume start already initiated | |||
4043 | if (testState(dssta_syncmodestable,true)) { | |||
4044 | // all alert and alert status must be done by now, sync mode must be stable | |||
4045 | CONSOLEPRINTF(("- Starting Sync with Datastore '%s', %s sync",fRemoteViewOfLocalURI.c_str(),fSlowSync ? "slow" : "normal"))SySync_ConsolePrintf(stderr, "SYSYNC " "- Starting Sync with Datastore '%s', %s sync" "\n",fRemoteViewOfLocalURI.c_str(),fSlowSync ? "slow" : "normal" ); | |||
4046 | startingNow = true; // initiating start now | |||
4047 | #ifdef SYSYNC_SERVER1 | |||
4048 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4049 | // at this point, all temporary GUIDs become invalid (no "early map" possible any more which might refer to last session's tempGUIDs) | |||
4050 | fTempGUIDMap.clear(); // forget all previous session's temp GUID mappings | |||
4051 | // let local datastore (derived DB-specific class) prepare for sync | |||
4052 | localstatus sta = changeState(dssta_dataaccessstarted); | |||
4053 | if (sta!=LOCERR_OK) { | |||
4054 | // abort session (old comment: %%% aborting datastore only does not work, will loop, why? %%%) | |||
4055 | aStatusCommand.setStatusCode(syncmlError(sta)); | |||
4056 | PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: could not change state to dsssta_dataaccessstarted -> abort")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("TLocalEngineDS::engProcessSyncCmd: could not change state to dsssta_dataaccessstarted -> abort" ); }; | |||
4057 | engAbortDataStoreSync(sta,true); // local problem | |||
4058 | return false; | |||
4059 | } | |||
4060 | } | |||
4061 | #endif | |||
4062 | } | |||
4063 | // if data access started (finished or not), check start status | |||
4064 | // for every execution and re-execution of the sync command | |||
4065 | if (testState(dssta_dataaccessstarted)) { | |||
4066 | // queue <sync> command if datastore is not yet started already | |||
4067 | if (engIsStarted(!startingNow)) { // wait only if start was already initiated | |||
4068 | // - is now initialized | |||
4069 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4070 | // - for server, make the sync set ready now (as engine is now started) | |||
4071 | changeState(dssta_syncsetready,true); // force it | |||
4072 | } | |||
4073 | else { | |||
4074 | // - for client, we need at least dssta_syncgendone (sync set has been ready long ago, we've already sent changes to server!) | |||
4075 | if (!testState(dssta_syncgendone)) { | |||
4076 | // bad sequence of commands (<sync> from server too early!) | |||
4077 | aStatusCommand.setStatusCode(400); | |||
4078 | PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: client received SYNC before sending SYNC complete")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("TLocalEngineDS::engProcessSyncCmd: client received SYNC before sending SYNC complete" ); }; | |||
4079 | engAbortDataStoreSync(400,false,false); // remote problem, not resumable | |||
4080 | return false; | |||
4081 | } | |||
4082 | } | |||
4083 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Started %s Sync (first <sync> command)" , fSlowSync ? "slow" : "normal" ); } | |||
4084 | "- Started %s Sync (first <sync> command)",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Started %s Sync (first <sync> command)" , fSlowSync ? "slow" : "normal" ); } | |||
4085 | fSlowSync ? "slow" : "normal"{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Started %s Sync (first <sync> command)" , fSlowSync ? "slow" : "normal" ); } | |||
4086 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Started %s Sync (first <sync> command)" , fSlowSync ? "slow" : "normal" ); }; | |||
4087 | if (fRemoteNumberOfChanges>=0) PDEBUGPRINTFX(DBG_HOT,("- NumberOfChanges announced by remote = %ld",(long)fRemoteNumberOfChanges)){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("- NumberOfChanges announced by remote = %ld" ,(long)fRemoteNumberOfChanges); }; | |||
4088 | DB_PROGRESS_EVENT(this,pev_syncstart,0,0,0)this->getSession()->NotifySessionProgressEvent(pev_syncstart ,this->getDSConfig(),0,0,0); | |||
4089 | } | |||
4090 | else { | |||
4091 | // - not yet started | |||
4092 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Starting sync not yet complete - re-execute <sync> command again in next message" ); } | |||
4093 | "- Starting sync not yet complete - re-execute <sync> command again in next message"{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Starting sync not yet complete - re-execute <sync> command again in next message" ); } | |||
4094 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "- Starting sync not yet complete - re-execute <sync> command again in next message" ); }; | |||
4095 | aQueueForLater=true; | |||
4096 | return true; // ok so far | |||
4097 | } | |||
4098 | } | |||
4099 | // must be syncready here (otherwise we return before we reach this) | |||
4100 | if (!testState(dssta_syncsetready)) { | |||
4101 | aStatusCommand.setStatusCode(403); // forbidden | |||
4102 | ADDDEBUGITEM(aStatusCommand,"SYNC received too early"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("SYNC received too early"); }; | |||
4103 | PDEBUGPRINTFX(DBG_ERROR,("TLocalEngineDS::engProcessSyncCmd: SYNC received too early")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("TLocalEngineDS::engProcessSyncCmd: SYNC received too early" ); }; | |||
4104 | engAbortDataStoreSync(403,false,false); // remote problem, not resumable | |||
4105 | return false; | |||
4106 | } | |||
4107 | // state is now syncing | |||
4108 | /// @deprecated - dssta_syncsetready is enough | |||
4109 | //fState=dss_syncing; | |||
4110 | return true; | |||
4111 | } // TLocalEngineDS::engProcessSyncCmd | |||
4112 | ||||
4113 | ||||
4114 | // SYNC command bracket end (but another might follow in next message) | |||
4115 | bool TLocalEngineDS::engProcessSyncCmdEnd(bool &aQueueForLater) | |||
4116 | { | |||
4117 | bool ok=true; | |||
4118 | // queue it for later as long as datastore is not ready now | |||
4119 | if (!engIsStarted(false)) { // not waiting if not started | |||
4120 | // no state change, just postpone execution | |||
4121 | aQueueForLater=true; | |||
4122 | } | |||
4123 | // also inform remote | |||
4124 | if (fRemoteDatastoreP) ok=fRemoteDatastoreP->remoteProcessSyncCmdEnd(); | |||
4125 | return ok; | |||
4126 | } // TLocalEngineDS::engProcessSyncCmdEnd | |||
4127 | ||||
4128 | ||||
4129 | #ifdef SYSYNC_SERVER1 | |||
4130 | ||||
4131 | // server case: called whenever outgoing Message of Sync Package starts | |||
4132 | void TLocalEngineDS::engServerStartOfSyncMessage(void) | |||
4133 | { | |||
4134 | // this is where we might start our own <Sync> command (all | |||
4135 | // received commands are now processed) | |||
4136 | // - Note that this might be a subdatastore -> if so, do NOT | |||
4137 | // start a sync (superdatastore will handle this) | |||
4138 | // - Note that this will be called even if current message is | |||
4139 | // already full, so it could well be that this sync command | |||
4140 | // is queued. | |||
4141 | if (!fSessionP->fCompleteFromClientOnly && testState(dssta_serverseenclientmods) && getSyncMode()==smo_fromclient) { | |||
4142 | // from-client only does not send back a <sync> command, simply end data access here | |||
4143 | PDEBUGPRINTFX(DBG_PROTO,("from-client-only:do not send <sync> command back to client, data access ends here")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("from-client-only:do not send <sync> command back to client, data access ends here" ); }; | |||
4144 | changeState(dssta_syncgendone,true); | |||
4145 | changeState(dssta_dataaccessdone,true); | |||
4146 | } | |||
4147 | // ### SyncFest #5, found with Tactel Jazz Client: | |||
4148 | // - do not send anything when remote datastore is not known | |||
4149 | else if (fRemoteDatastoreP) { | |||
4150 | if (!testState(dssta_serversyncgenstarted) && testState(dssta_serverseenclientmods)) { | |||
4151 | changeState(dssta_serversyncgenstarted,true); | |||
4152 | if (!isSubDatastore()) { | |||
4153 | // - Note: if sync command was already started, the | |||
4154 | // finished(), continueIssue() mechanism will make sure that | |||
4155 | // more commands are generated | |||
4156 | // - Note2: if all sync commands can be sent at once, | |||
4157 | // fState will be modified by issuing <sync>, so | |||
4158 | // it must be ok for issuing syncops here already! | |||
4159 | TSyncCommand *synccmdP = | |||
4160 | new TSyncCommand( | |||
4161 | fSessionP, | |||
4162 | this, // local datastore | |||
4163 | fRemoteDatastoreP // remote datastore | |||
4164 | ); | |||
4165 | // issue | |||
4166 | ISSUE_COMMAND_ROOT(fSessionP,synccmdP){ TSmlCommand* p=synccmdP; synccmdP=__null; fSessionP->issueRootPtr (p); }; | |||
4167 | } | |||
4168 | } | |||
4169 | } | |||
4170 | else { | |||
4171 | changeState(dssta_idle,true); // force it | |||
4172 | } | |||
4173 | } // TLocalEngineDS::engServerStartOfSyncMessage | |||
4174 | ||||
4175 | ||||
4176 | #endif // server only | |||
4177 | ||||
4178 | ||||
4179 | // called whenever Message of Sync Package ends or after last queued Sync command is executed | |||
4180 | // - aEndOfAllSyncCommands is true when at end of Sync-data-from-remote packet | |||
4181 | // AND all possibly queued sync/syncop commands have been processed. | |||
4182 | void TLocalEngineDS::engEndOfSyncFromRemote(bool aEndOfAllSyncCommands) | |||
4183 | { | |||
4184 | // is called for all local datastores, including superdatastore, even inactive ones, so check state first | |||
4185 | if (testState(dssta_syncsetready)) { | |||
4186 | if (aEndOfAllSyncCommands) { | |||
4187 | // we are at end of sync-data-from-remote for THIS datastore | |||
4188 | if (IS_CLIENT(!getSyncAppBase()->isServer())) { | |||
4189 | // - we are done with <Sync> from server, that is, data access is done now | |||
4190 | changeState(dssta_dataaccessdone,true); // force it | |||
4191 | } | |||
4192 | else { | |||
4193 | // - server has seen all client modifications now | |||
4194 | // Note: in case of the simulated-empty-sync-hack in action, we | |||
4195 | // allow that we are already in server_sync_gen_started and | |||
4196 | // won't try to force down to dssta_serverseenclientmods | |||
4197 | if (!fSessionP->fFakeFinalFlag || getDSState()<dssta_serverseenclientmods) { | |||
4198 | // under normal circumstances, wee need dssta_serverseenclientmods here | |||
4199 | changeState(dssta_serverseenclientmods,true); // force it | |||
4200 | } | |||
4201 | else { | |||
4202 | // hack exception | |||
4203 | PDEBUGPRINTFX(DBG_ERROR,("Warning: simulated </final> active - allowing state>server_seen_client_mods")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: simulated </final> active - allowing state>server_seen_client_mods" ); }; | |||
4204 | } | |||
4205 | } | |||
4206 | } | |||
4207 | #ifdef SYSYNC_SERVER1 | |||
4208 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4209 | engServerStartOfSyncMessage(); | |||
4210 | } | |||
4211 | #endif | |||
4212 | // now do final things | |||
4213 | if (testState(dssta_dataaccessdone,true)) { | |||
4214 | // @todo: I think, as long as we need to send maps, we're not done yet!!! | |||
4215 | // sync packets in both directions done, forget remote datastore | |||
4216 | fRemoteDatastoreP=NULL__null; | |||
4217 | } | |||
4218 | } // dssta_syncsetready | |||
4219 | } // TLocalEngineDS::engEndOfSyncFromRemote | |||
4220 | ||||
4221 | ||||
4222 | // - must return true if this datastore is finished with <sync> | |||
4223 | // (if all datastores return true, | |||
4224 | // session is allowed to finish sync packet with outgoing message | |||
4225 | bool TLocalEngineDS::isSyncDone(void) | |||
4226 | { | |||
4227 | // is called for all local datastores, even inactive ones, which must signal sync done, too | |||
4228 | // - only datastores currently receiving or sending <sync> commands are not done with sync | |||
4229 | // - aborted datastores are also done with sync, no matter what status they have | |||
4230 | return ( | |||
4231 | fAbortStatusCode!=0 || | |||
4232 | //(fState!=dss_syncsend && fState!=dss_syncing && fState!=dss_syncready && fState!=dss_syncfinish) | |||
4233 | !testState(dssta_clientsentalert) || // nothing really happened yet... | |||
4234 | testState(dssta_syncgendone) // ...or already completed generating <sync> | |||
4235 | ); | |||
4236 | } // TLocalEngineDS::isSyncDone | |||
4237 | ||||
4238 | ||||
4239 | // test datastore state for minimal or exact state | |||
4240 | bool TLocalEngineDS::testState(TLocalEngineDSState aMinState, bool aNeedExactMatch) | |||
4241 | { | |||
4242 | bool result = | |||
4243 | (!aNeedExactMatch || (fLocalDSState==aMinState)) && | |||
4244 | (fLocalDSState>=aMinState); | |||
4245 | DEBUGPRINTFX(DBG_EXOTIC,({ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4246 | "%s: testState=%s - expected state%c='%s', found state=='%s'",{ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4247 | getName(),{ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4248 | result ? "TRUE" : "FALSE",{ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4249 | aNeedExactMatch ? '=' : '>',{ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4250 | getDSStateName(aMinState),{ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4251 | getDSStateName(){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); } | |||
4252 | )){ if (((0x80000000) & getDbgMask()) == (0x80000000)) getDbgLogger ()->setNextMask(0x80000000).DebugPrintfLastMask ( "%s: testState=%s - expected state%c='%s', found state=='%s'" , getName(), result ? "TRUE" : "FALSE", aNeedExactMatch ? '=' : '>', getDSStateName(aMinState), getDSStateName() ); }; | |||
4253 | return result; | |||
4254 | } // TLocalEngineDS::testState | |||
4255 | ||||
4256 | ||||
4257 | // change datastore state, calls logic layer before and after change | |||
4258 | localstatus TLocalEngineDS::changeState(TLocalEngineDSState aNewState, bool aForceOnError) | |||
4259 | { | |||
4260 | localstatus err1,err2; | |||
4261 | TLocalEngineDSState oldState = fLocalDSState; | |||
4262 | ||||
4263 | // nop if no change in state | |||
4264 | if (aNewState==oldState) return LOCERR_OK; | |||
4265 | // state cannot be decremented except down to adminready and below | |||
4266 | if ((aNewState<oldState) && (aNewState>dssta_adminready)) { | |||
4267 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); } | |||
4268 | "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); } | |||
4269 | getName(),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); } | |||
4270 | getDSStateName(oldState),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); } | |||
4271 | getDSStateName(aNewState){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); } | |||
4272 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "%s: Internal error: attempt to reduce state from '%s' to '%s' - aborting" , getName(), getDSStateName(oldState), getDSStateName(aNewState ) ); }; | |||
4273 | err1 = 500; | |||
4274 | dsAbortDatastoreSync(err1,true); | |||
4275 | return err1; | |||
4276 | } | |||
4277 | // give logic opportunity to react before state changes | |||
4278 | PDEBUGBLOCKFMT((getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4279 | "DSStateChange",getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4280 | "Datastore changes state",getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4281 | "datastore=%s|oldstate=%s|newstate=%s",getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4282 | getName(),getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4283 | getDSStateName(oldState),getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4284 | getDSStateName(aNewState)getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ) | |||
4285 | ))getDbgLogger()->DebugOpenBlockExpanded ( "DSStateChange", "Datastore changes state" , "datastore=%s|oldstate=%s|newstate=%s", getName(), getDSStateName (oldState), getDSStateName(aNewState) ); | |||
4286 | err2 = LOCERR_OK; | |||
4287 | err1 = dsBeforeStateChange(oldState,aNewState); | |||
4288 | if (!aForceOnError && err1) goto endchange; | |||
4289 | // switch state | |||
4290 | fLocalDSState = aNewState; | |||
4291 | ||||
4292 | if (aNewState == dssta_syncmodestable) { | |||
4293 | // There are multiple places where the sync mode is frozen. Ensure | |||
4294 | // that this change is reported in all of them by putting the code | |||
4295 | // here. | |||
4296 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4297 | "executing %s%s%s Sync%s",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4298 | fResuming ? "resumed " : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4299 | fSlowSync ? "slow" : "normal",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4300 | fFirstTimeSync ? " first time" : "",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4301 | fSyncMode == smo_twoway ? ", two-way" :{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4302 | fSyncMode == smo_fromclient ? " from client" :{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4303 | fSyncMode == smo_fromserver ? " from server" :{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4304 | " in unknown direction?!"{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); } | |||
4305 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "executing %s%s%s Sync%s" , fResuming ? "resumed " : "", fSlowSync ? "slow" : "normal", fFirstTimeSync ? " first time" : "", fSyncMode == smo_twoway ? ", two-way" : fSyncMode == smo_fromclient ? " from client" : fSyncMode == smo_fromserver ? " from server" : " in unknown direction?!" ); }; | |||
4306 | #ifdef PROGRESS_EVENTS1 | |||
4307 | // progress event | |||
4308 | DB_PROGRESS_EVENT(this,this->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode) | |||
4309 | pev_alerted,this->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode) | |||
4310 | fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,this->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode) | |||
4311 | fResuming ? 1 : 0,this->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode) | |||
4312 | fSyncModethis->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode) | |||
4313 | )this->getSession()->NotifySessionProgressEvent(pev_alerted ,this->getDSConfig(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0,fSyncMode); | |||
4314 | #endif // PROGRESS_EVENTS | |||
4315 | } | |||
4316 | ||||
4317 | // now give logic opportunity to react again | |||
4318 | err2 = dsAfterStateChange(oldState,aNewState); | |||
4319 | endchange: | |||
4320 | PDEBUGENDBLOCK("DSStateChange")getDbgLogger()->DebugCloseBlock( "DSStateChange"); | |||
4321 | // return most recent error | |||
4322 | return err2 ? err2 : err1; | |||
4323 | } // TLocalEngineDS::changeState | |||
4324 | ||||
4325 | ||||
4326 | ||||
4327 | // test datastore abort status | |||
4328 | // datastore is aborted when | |||
4329 | // - it was explicitly aborted (engAbortDataStoreSync() called, fAbortStatusCode set) | |||
4330 | // - session is suspending and the datastore has not yet completed sync up to sending | |||
4331 | // maps (client) or admin already saved (server+client). | |||
4332 | // If client has sent maps, all that MIGHT be missing would be map status, and | |||
4333 | // if that hasn't arrived, the pendingMaps mechanism will make sure these get | |||
4334 | // sent in the next session. | |||
4335 | bool TLocalEngineDS::isAborted(void) | |||
4336 | { | |||
4337 | return fAbortStatusCode!=0 || (fSessionP->isSuspending() && !testState(dssta_clientmapssent)); | |||
4338 | } // TLocalEngineDS::isAborted | |||
4339 | ||||
4340 | ||||
4341 | // abort sync with this datastore | |||
4342 | void TLocalEngineDS::engAbortDataStoreSync(TSyError aStatusCode, bool aLocalProblem, bool aResumable) | |||
4343 | { | |||
4344 | if (fLocalDSState!=dssta_idle && !fAbortStatusCode) { | |||
4345 | // prepare status | |||
4346 | fAbortStatusCode = aStatusCode ? aStatusCode : 514; // make sure we have a non-zero fAbortStatusCode | |||
4347 | fLocalAbortCause = aLocalProblem; | |||
4348 | if (!aResumable) preventResuming(); // prevent resuming | |||
4349 | PDEBUGBLOCKFMT((getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ) | |||
4350 | "DSAbort","Aborting datastore sync","abortStatusCode=%hd|localProblem=%s|resumable=%s",getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ) | |||
4351 | aStatusCode,getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ) | |||
4352 | aLocalProblem ? "yes" : "no",getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ) | |||
4353 | aResumable ? "yes" : "no"getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ) | |||
4354 | ))getDbgLogger()->DebugOpenBlockExpanded ( "DSAbort","Aborting datastore sync" ,"abortStatusCode=%hd|localProblem=%s|resumable=%s", aStatusCode , aLocalProblem ? "yes" : "no", aResumable ? "yes" : "no" ); | |||
4355 | // tell that to the session | |||
4356 | fSessionP->DatastoreFailed(aStatusCode,aLocalProblem); | |||
4357 | // as soon as sync set is ready, we have potentially started the sync and resume makes sense | |||
4358 | // NOTE: before we have made the sync set ready, WE MUST NOT resume, because making the sync | |||
4359 | // set ready includes zapping it on slow refreshes, and this is only done when not resuming | |||
4360 | // (so saving a suspend state before dssta_syncsetready would cause that the zapping is | |||
4361 | // possibly skipped) | |||
4362 | if (!testState(dssta_syncsetready)) preventResuming(); // prevent resuming before sync set is ready | |||
4363 | // save resume (or non-resumable!) status only if this is NOT A TIMEOUT, because if it is a | |||
4364 | // (server) timeout, suspend state was saved at end of last request, and writing again here would destroy | |||
4365 | // the state. | |||
4366 | if (aStatusCode!=408) { | |||
4367 | engSaveSuspendState(true); // save even if already aborted | |||
4368 | } | |||
4369 | // let derivates know | |||
4370 | dsAbortDatastoreSync(aStatusCode, aLocalProblem); | |||
4371 | // show abort | |||
4372 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4373 | "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4374 | (long)((getSession()->getSystemNowAs(TCTX_UTC)-fSessionP->getLastRequestStarted()) / secondToLinearTimeFactor),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4375 | (long)((getSession()->getSystemNowAs(TCTX_UTC)-fSessionP->getSessionStarted()) / secondToLinearTimeFactor),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4376 | aLocalProblem ? "LOCAL" : "REMOTE",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4377 | aStatusCode{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); } | |||
4378 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "*************** Warning: Datastore flagged aborted (after %ld sec. request processing, %ld sec. total) with %s Status %hd" , (long)((getSession()->getSystemNowAs(((timecontext_t) (( tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP->getLastRequestStarted ()) / secondToLinearTimeFactor), (long)((getSession()->getSystemNowAs (((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)))-fSessionP ->getSessionStarted()) / secondToLinearTimeFactor), aLocalProblem ? "LOCAL" : "REMOTE", aStatusCode ); }; | |||
4379 | DB_PROGRESS_EVENT(this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4380 | this,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4381 | pev_syncend,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4382 | getAbortStatusCode(),this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4383 | fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4384 | fResuming ? 1 : 0this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4385 | )this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),getAbortStatusCode(),fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0); | |||
4386 | PDEBUGENDBLOCK("DSAbort")getDbgLogger()->DebugCloseBlock( "DSAbort"); | |||
4387 | } | |||
4388 | } // TLocalEngineDS::engAbortDataStoreSync | |||
4389 | ||||
4390 | ||||
4391 | // check if aborted, set status to abort reason code if yes | |||
4392 | bool TLocalEngineDS::CheckAborted(TStatusCommand &aStatusCommand) | |||
4393 | { | |||
4394 | if (fAbortStatusCode!=0) { | |||
4395 | aStatusCommand.setStatusCode( | |||
4396 | fSessionP->getSyncMLVersion()>=syncml_vers_1_1 ? 514 : // cancelled | |||
4397 | (fAbortStatusCode<LOCAL_STATUS_CODE ? fAbortStatusCode : 512) // sync failed | |||
4398 | ); | |||
4399 | PDEBUGPRINTFX(DBG_DATA,("This datastore is in aborted state, rejects all commands with %hd",aStatusCommand.getStatusCode())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("This datastore is in aborted state, rejects all commands with %hd" ,aStatusCommand.getStatusCode()); }; | |||
4400 | return true; // aborted, status set | |||
4401 | } | |||
4402 | return false; // not aborted | |||
4403 | } // TLocalEngineDS::CheckAborted | |||
4404 | ||||
4405 | ||||
4406 | ||||
4407 | // Do common logfile substitutions | |||
4408 | void TLocalEngineDS::DoLogSubstitutions(string &aLog,bool aPlaintext) | |||
4409 | { | |||
4410 | #ifndef MINIMAL_CODE | |||
4411 | string s; | |||
4412 | ||||
4413 | if (aPlaintext) { | |||
4414 | StringObjTimestamp(s,fEndOfSyncTime); | |||
4415 | // %T Time of sync (in derived datastores, this is the point of reference for newer/older comparisons) as plain text | |||
4416 | StringSubst(aLog,"%T",s,2); | |||
4417 | // %seT Time of session end (with this datastore) as plain text | |||
4418 | StringSubst(aLog,"%seT",s,4); | |||
4419 | // %ssT Time of session start as plain text | |||
4420 | StringObjTimestamp(s,fSessionP->getSessionStarted()); | |||
4421 | StringSubst(aLog,"%ssT",s,4); | |||
4422 | } | |||
4423 | // %sdT sync duration (in seconds) for this datastore (start of session until datastore finished) | |||
4424 | StringSubst(aLog,"%sdT",((sInt32)(fEndOfSyncTime-fSessionP->getSessionStarted())/secondToLinearTimeFactor),4); | |||
4425 | // %nD Datastore name | |||
4426 | StringSubst(aLog,"%nD",getName(),3); | |||
4427 | // %rD Datastore remote path | |||
4428 | StringSubst(aLog,"%rD",fRemoteDBPath,3); | |||
4429 | // %lD Datastore local path (complete with all CGI) | |||
4430 | StringSubst(aLog,"%lD",fRemoteViewOfLocalURI,3); | |||
4431 | // %iR Remote Device ID (URI) | |||
4432 | StringSubst(aLog,"%iR",fSessionP->getRemoteURI(),3); | |||
4433 | // %nR Remote name: [Manufacturer ]Model") | |||
4434 | StringSubst(aLog,"%nR",fSessionP->getRemoteDescName(),3); | |||
4435 | // %vR Remote Device Version Info ("Type (HWV, FWV, SWV) Oem") | |||
4436 | StringSubst(aLog,"%vR",fSessionP->getRemoteInfoString(),3); | |||
4437 | // %U User Name | |||
4438 | StringSubst(aLog,"%U",fSessionP->getSyncUserName(),2); | |||
4439 | // %iS local Session ID | |||
4440 | StringSubst(aLog,"%iS",fSessionP->getLocalSessionID(),3); | |||
4441 | // %sS Status code (0 if successful) | |||
4442 | StringSubst(aLog,"%sS",fAbortStatusCode,3); | |||
4443 | // %ssS Session Status code (0 if successful) | |||
4444 | StringSubst(aLog,"%ssS",fSessionP->getAbortReasonStatus(),4); | |||
4445 | // %syV SyncML version (as text) of session | |||
4446 | StringSubst(aLog,"%syV",SyncMLVerDTDNames[fSessionP->getSyncMLVersion()],4); | |||
4447 | // %syV SyncML version numeric (0=unknown, 1=1.0, 2=1.1, 3=1.2) of session | |||
4448 | StringSubst(aLog,"%syVn",(long)fSessionP->getSyncMLVersion(),5); | |||
4449 | // %mS Syncmode (0=twoway, 1=fromclient 2=fromserver) | |||
4450 | StringSubst(aLog,"%mS",(sInt32)fSyncMode,3); | |||
4451 | // %tS Synctype (0=normal,1=slow,2=firsttime slow, +10 if resumed session) | |||
4452 | StringSubst(aLog,"%tS",(fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0) + (isResuming() ? 10 : 0),3); | |||
4453 | // %laI locally added Items | |||
4454 | StringSubst(aLog,"%laI",fLocalItemsAdded,4); | |||
4455 | // %raI remotely added Items | |||
4456 | StringSubst(aLog,"%raI",fRemoteItemsAdded,4); | |||
4457 | // %ldI locally deleted Items | |||
4458 | StringSubst(aLog,"%ldI",fLocalItemsDeleted,4); | |||
4459 | // %rdI remotely deleted Items | |||
4460 | StringSubst(aLog,"%rdI",fRemoteItemsDeleted,4); | |||
4461 | // %luI locally updated Items | |||
4462 | StringSubst(aLog,"%luI",fLocalItemsUpdated,4); | |||
4463 | // %ruI remotely updated Items | |||
4464 | StringSubst(aLog,"%ruI",fRemoteItemsUpdated,4); | |||
4465 | // %reI locally not accepted Items (sent error to remote, remote MAY resend them or abort the session) | |||
4466 | StringSubst(aLog,"%leI",fLocalItemsError,4); | |||
4467 | // %leI remotely not accepted Items (got error from remote, local will resend them later) | |||
4468 | StringSubst(aLog,"%reI",fRemoteItemsError,4); | |||
4469 | #ifdef SYSYNC_SERVER1 | |||
4470 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4471 | // %smI Slowsync matched Items | |||
4472 | StringSubst(aLog,"%smI",fSlowSyncMatches,4); | |||
4473 | // %scI Server won Conflicts | |||
4474 | StringSubst(aLog,"%scI",fConflictsServerWins,4); | |||
4475 | // %ccI Client won Conflicts | |||
4476 | StringSubst(aLog,"%ccI",fConflictsClientWins,4); | |||
4477 | // %dcI Conflicts with duplications | |||
4478 | StringSubst(aLog,"%dcI",fConflictsDuplicated,4); | |||
4479 | // %tiB total incoming bytes | |||
4480 | StringSubst(aLog,"%tiB",fSessionP->getIncomingBytes(),4); | |||
4481 | // %toB total outgoing bytes | |||
4482 | StringSubst(aLog,"%toB",fSessionP->getOutgoingBytes(),4); | |||
4483 | } | |||
4484 | #endif | |||
4485 | // %niB net incoming data bytes for this datastore | |||
4486 | StringSubst(aLog,"%diB",fIncomingDataBytes,4); | |||
4487 | // %noB net incoming data bytes for this datastore | |||
4488 | StringSubst(aLog,"%doB",fOutgoingDataBytes,4); | |||
4489 | #endif | |||
4490 | } // TLocalEngineDS::DoLogSubstitutions | |||
4491 | ||||
4492 | ||||
4493 | // log datastore sync result | |||
4494 | // - Called at end of sync with this datastore | |||
4495 | void TLocalEngineDS::dsLogSyncResult(void) | |||
4496 | { | |||
4497 | #ifndef MINIMAL_CODE | |||
4498 | if (fSessionP->logEnabled()) { | |||
4499 | string logtext; | |||
4500 | logtext=fSessionP->getSessionConfig()->fLogFileFormat; | |||
4501 | if (!logtext.empty()) { | |||
4502 | // substitute | |||
4503 | DoLogSubstitutions(logtext,true); // plaintext | |||
4504 | // show | |||
4505 | fSessionP->WriteLogLine(logtext.c_str()); | |||
4506 | } | |||
4507 | } | |||
4508 | #endif | |||
4509 | } // TLocalEngineDS::dsLogSyncResult | |||
4510 | ||||
4511 | ||||
4512 | ||||
4513 | // Terminate all activity with this datastore | |||
4514 | // Note: may be called repeatedly, must only execute relevant shutdown code once | |||
4515 | void TLocalEngineDS::engTerminateDatastore(localstatus aAbortStatusCode) | |||
4516 | { | |||
4517 | // now abort (if not already aborted), then finish activities | |||
4518 | engFinishDataStoreSync(aAbortStatusCode); | |||
4519 | // and finally reset completely | |||
4520 | engResetDataStore(); | |||
4521 | } // TLocalEngineDS::TerminateDatastore | |||
4522 | ||||
4523 | ||||
4524 | // called at very end of sync session, when everything is done | |||
4525 | // Note: is also called before deleting a datastore (so aborted sessions | |||
4526 | // can do cleanup and/or statistics display as well) | |||
4527 | void TLocalEngineDS::engFinishDataStoreSync(localstatus aErrorStatus) | |||
4528 | { | |||
4529 | // set end of sync time | |||
4530 | fEndOfSyncTime = getSession()->getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))); | |||
4531 | // check if we have something to do at all | |||
4532 | if (fLocalDSState!=dssta_idle && fLocalDSState!=dssta_completed) { | |||
4533 | if (aErrorStatus==LOCERR_OK) { | |||
4534 | // Check if we need to abort now due to failed items only | |||
4535 | if (fRemoteItemsError>0) { | |||
4536 | // remote reported errors | |||
4537 | if (fSlowSync && fRemoteItemsAdded==0 && fRemoteItemsDeleted==0 && fRemoteItemsUpdated==0 && fSessionP->getSessionConfig()->fAbortOnAllItemsFailed) { | |||
4538 | PDEBUGPRINTFX(DBG_ERROR+DBG_DETAILS,("All remote item operations failed -> abort sync")){ if (((0x00000002 +0x40000000) & getDbgMask()) == (0x00000002 +0x40000000)) getDbgLogger()->setNextMask(0x00000002 +0x40000000 ).DebugPrintfLastMask ("All remote item operations failed -> abort sync" ); }; | |||
4539 | engAbortDataStoreSync(512,false,false); // remote problems (only failed items in a slow sync) caused sync to fail, not resumable | |||
4540 | } | |||
4541 | else | |||
4542 | fSessionP->DatastoreHadErrors(); // at least SOME items were successful, so it's not a completely unsuccessful sync | |||
4543 | } | |||
4544 | } | |||
4545 | // abort, if requested from caller or only-failed-items | |||
4546 | if (aErrorStatus!=LOCERR_OK) | |||
4547 | engAbortDataStoreSync(aErrorStatus,true); // if we have an error here, this is considered a local problem | |||
4548 | else { | |||
4549 | DB_PROGRESS_EVENT(this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4550 | this,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4551 | pev_syncend,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4552 | fAbortStatusCode,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4553 | fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4554 | fResuming ? 1 : 0this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0) | |||
4555 | )this->getSession()->NotifySessionProgressEvent(pev_syncend ,this->getDSConfig(),fAbortStatusCode,fSlowSync ? (fFirstTimeSync ? 2 : 1) : 0,fResuming ? 1 : 0); | |||
4556 | } | |||
4557 | #ifdef SUPERDATASTORES1 | |||
4558 | // if this is part of a superdatastore, include its statistics into mine, as | |||
4559 | // superdatastore can not save any statistics. | |||
4560 | // This ensures that the result sum over all subdatastores is correct, | |||
4561 | // however the assignment of error and byte counts is not (all non-related | |||
4562 | // counts go to first subdatastores with the following code) | |||
4563 | if (fAsSubDatastoreOf) { | |||
4564 | fOutgoingDataBytes += fAsSubDatastoreOf->fOutgoingDataBytes; | |||
4565 | fIncomingDataBytes += fAsSubDatastoreOf->fIncomingDataBytes; | |||
4566 | fRemoteItemsError += fAsSubDatastoreOf->fRemoteItemsError; | |||
4567 | fLocalItemsError += fAsSubDatastoreOf->fLocalItemsError; | |||
4568 | // consumed now, clear in superdatastore | |||
4569 | fAsSubDatastoreOf->fOutgoingDataBytes=0; | |||
4570 | fAsSubDatastoreOf->fIncomingDataBytes=0; | |||
4571 | fAsSubDatastoreOf->fRemoteItemsError=0; | |||
4572 | fAsSubDatastoreOf->fLocalItemsError=0; | |||
4573 | } | |||
4574 | #endif | |||
4575 | // make log entry | |||
4576 | dsLogSyncResult(); | |||
4577 | // update my session state vars for successful sessions | |||
4578 | if (aErrorStatus==LOCERR_OK) { | |||
4579 | // update anchor | |||
4580 | fLastRemoteAnchor=fNextRemoteAnchor; | |||
4581 | fLastLocalAnchor=fNextLocalAnchor; // note: when using TStdLogicDS, this is not saved, but re-generated at next sync from timestamp | |||
4582 | // no resume | |||
4583 | fResumeAlertCode=0; | |||
4584 | // no resume item (just to make sure we don't get strange effects later) | |||
4585 | fLastItemStatus = 0; | |||
4586 | fLastSourceURI.erase(); | |||
4587 | fLastTargetURI.erase(); | |||
4588 | fPartialItemState = pi_state_none; | |||
4589 | fPIStoredSize = 0; | |||
4590 | } | |||
4591 | // now shift state to complete, let logic and implementation save the state | |||
4592 | changeState(dssta_completed,true); | |||
4593 | #ifdef SCRIPT_SUPPORT1 | |||
4594 | // - call DB finish script | |||
4595 | TScriptContext::execute( | |||
4596 | fDataStoreScriptContextP, | |||
4597 | fDSConfigP->fDBFinishScript, | |||
4598 | &DBFuncTable, // context's function table | |||
4599 | this // datastore pointer needed for context | |||
4600 | ); | |||
4601 | #endif | |||
4602 | } | |||
4603 | // in any case: idle now again (note: could be shift from dssta_completed to dssta_idle) | |||
4604 | changeState(dssta_idle,true); | |||
4605 | } // TLocalEngineDS::engFinishDataStoreSync | |||
4606 | ||||
4607 | ||||
4608 | /// inform everyone of coming state change | |||
4609 | localstatus TLocalEngineDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState) | |||
4610 | { | |||
4611 | localstatus sta = LOCERR_OK; | |||
4612 | return sta; | |||
4613 | } // TLocalEngineDS::dsBeforeStateChange | |||
4614 | ||||
4615 | ||||
4616 | /// inform everyone of happened state change | |||
4617 | localstatus TLocalEngineDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState) | |||
4618 | { | |||
4619 | localstatus sta = LOCERR_OK; | |||
4620 | if (aOldState>dssta_idle && aNewState==dssta_completed) { | |||
4621 | // we are going from a non-idle state to completed | |||
4622 | // - show statistics | |||
4623 | showStatistics(); | |||
4624 | } | |||
4625 | return sta; | |||
4626 | } // TLocalEngineDS::dsAfterStateChange | |||
4627 | ||||
4628 | ||||
4629 | // show statistics or error of current sync | |||
4630 | void TLocalEngineDS::showStatistics(void) | |||
4631 | { | |||
4632 | // Console | |||
4633 | CONSOLEPRINTF((""))SySync_ConsolePrintf(stderr, "SYSYNC " "" "\n"); | |||
4634 | CONSOLEPRINTF(("- Sync Statistics for '%s' (%s), %s sync",SySync_ConsolePrintf(stderr, "SYSYNC " "- Sync Statistics for '%s' (%s), %s sync" "\n", getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal") | |||
4635 | getName(),SySync_ConsolePrintf(stderr, "SYSYNC " "- Sync Statistics for '%s' (%s), %s sync" "\n", getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal") | |||
4636 | fRemoteViewOfLocalURI.c_str(),SySync_ConsolePrintf(stderr, "SYSYNC " "- Sync Statistics for '%s' (%s), %s sync" "\n", getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal") | |||
4637 | fSlowSync ? "slow" : "normal"SySync_ConsolePrintf(stderr, "SYSYNC " "- Sync Statistics for '%s' (%s), %s sync" "\n", getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal") | |||
4638 | ))SySync_ConsolePrintf(stderr, "SYSYNC " "- Sync Statistics for '%s' (%s), %s sync" "\n", getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal"); | |||
4639 | // now show results | |||
4640 | if (isAborted()) { | |||
4641 | // failed | |||
4642 | CONSOLEPRINTF((" ************ Failed with status code=%hd",fAbortStatusCode))SySync_ConsolePrintf(stderr, "SYSYNC " " ************ Failed with status code=%hd" "\n",fAbortStatusCode); | |||
4643 | } | |||
4644 | else { | |||
4645 | // successful: show statistics on console | |||
4646 | CONSOLEPRINTF((" =================================================="))SySync_ConsolePrintf(stderr, "SYSYNC " " ==================================================" "\n"); | |||
4647 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4648 | CONSOLEPRINTF((" on Server on Client"))SySync_ConsolePrintf(stderr, "SYSYNC " " on Server on Client" "\n"); | |||
4649 | } | |||
4650 | else { | |||
4651 | CONSOLEPRINTF((" on Client on Server"))SySync_ConsolePrintf(stderr, "SYSYNC " " on Client on Server" "\n"); | |||
4652 | } | |||
4653 | CONSOLEPRINTF((" Added: %9ld %9ld",(long)fLocalItemsAdded,(long)fRemoteItemsAdded))SySync_ConsolePrintf(stderr, "SYSYNC " " Added: %9ld %9ld" "\n",(long)fLocalItemsAdded,(long)fRemoteItemsAdded); | |||
4654 | CONSOLEPRINTF((" Deleted: %9ld %9ld",(long)fLocalItemsDeleted,(long)fRemoteItemsDeleted))SySync_ConsolePrintf(stderr, "SYSYNC " " Deleted: %9ld %9ld" "\n",(long)fLocalItemsDeleted,(long)fRemoteItemsDeleted); | |||
4655 | CONSOLEPRINTF((" Updated: %9ld %9ld",(long)fLocalItemsUpdated,(long)fRemoteItemsUpdated))SySync_ConsolePrintf(stderr, "SYSYNC " " Updated: %9ld %9ld" "\n",(long)fLocalItemsUpdated,(long)fRemoteItemsUpdated); | |||
4656 | CONSOLEPRINTF((" Rejected with error: %9ld %9ld",(long)fLocalItemsError,(long)fRemoteItemsError))SySync_ConsolePrintf(stderr, "SYSYNC " " Rejected with error: %9ld %9ld" "\n",(long)fLocalItemsError,(long)fRemoteItemsError); | |||
4657 | #ifdef SYSYNC_SERVER1 | |||
4658 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4659 | CONSOLEPRINTF((" SlowSync Matches: %9ld",(long)fSlowSyncMatches))SySync_ConsolePrintf(stderr, "SYSYNC " " SlowSync Matches: %9ld" "\n",(long)fSlowSyncMatches); | |||
4660 | CONSOLEPRINTF((" Server won Conflicts: %9ld",(long)fConflictsServerWins))SySync_ConsolePrintf(stderr, "SYSYNC " " Server won Conflicts: %9ld" "\n",(long)fConflictsServerWins); | |||
4661 | CONSOLEPRINTF((" Client won Conflicts: %9ld",(long)fConflictsClientWins))SySync_ConsolePrintf(stderr, "SYSYNC " " Client won Conflicts: %9ld" "\n",(long)fConflictsClientWins); | |||
4662 | CONSOLEPRINTF((" Conflicts with Duplication: %9ld",(long)fConflictsDuplicated))SySync_ConsolePrintf(stderr, "SYSYNC " " Conflicts with Duplication: %9ld" "\n",(long)fConflictsDuplicated); | |||
4663 | } | |||
4664 | #endif | |||
4665 | } | |||
4666 | CONSOLEPRINTF((""))SySync_ConsolePrintf(stderr, "SYSYNC " "" "\n"); | |||
4667 | // Always provide statistics as events | |||
4668 | DB_PROGRESS_EVENT(this,pev_dsstats_l,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted)this->getSession()->NotifySessionProgressEvent(pev_dsstats_l ,this->getDSConfig(),fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted ); | |||
4669 | DB_PROGRESS_EVENT(this,pev_dsstats_r,fRemoteItemsAdded,fRemoteItemsUpdated,fRemoteItemsDeleted)this->getSession()->NotifySessionProgressEvent(pev_dsstats_r ,this->getDSConfig(),fRemoteItemsAdded,fRemoteItemsUpdated ,fRemoteItemsDeleted); | |||
4670 | DB_PROGRESS_EVENT(this,pev_dsstats_e,fLocalItemsError,fRemoteItemsError,0)this->getSession()->NotifySessionProgressEvent(pev_dsstats_e ,this->getDSConfig(),fLocalItemsError,fRemoteItemsError,0); | |||
4671 | #ifdef SYSYNC_SERVER1 | |||
4672 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4673 | DB_PROGRESS_EVENT(this,pev_dsstats_s,fSlowSyncMatches,0,0)this->getSession()->NotifySessionProgressEvent(pev_dsstats_s ,this->getDSConfig(),fSlowSyncMatches,0,0); | |||
4674 | DB_PROGRESS_EVENT(this,pev_dsstats_c,fConflictsServerWins,fConflictsClientWins,fConflictsDuplicated)this->getSession()->NotifySessionProgressEvent(pev_dsstats_c ,this->getDSConfig(),fConflictsServerWins,fConflictsClientWins ,fConflictsDuplicated); | |||
4675 | } | |||
4676 | #endif | |||
4677 | // NOTE: pev_dsstats_d should remain the last log data event sent (as it terminates collecting data in some GUIs) | |||
4678 | DB_PROGRESS_EVENT(this,pev_dsstats_d,fOutgoingDataBytes,fIncomingDataBytes,fRemoteItemsError)this->getSession()->NotifySessionProgressEvent(pev_dsstats_d ,this->getDSConfig(),fOutgoingDataBytes,fIncomingDataBytes ,fRemoteItemsError); | |||
4679 | // Always show statistics in debug log | |||
4680 | #ifdef SYDEBUG2 | |||
4681 | PDEBUGPRINTFX(DBG_HOT,("Sync Statistics for '%s' (%s), %s sync",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Sync Statistics for '%s' (%s), %s sync" , getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal" ); } | |||
4682 | getName(),{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Sync Statistics for '%s' (%s), %s sync" , getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal" ); } | |||
4683 | fRemoteViewOfLocalURI.c_str(),{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Sync Statistics for '%s' (%s), %s sync" , getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal" ); } | |||
4684 | fSlowSync ? "slow" : "normal"{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Sync Statistics for '%s' (%s), %s sync" , getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal" ); } | |||
4685 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Sync Statistics for '%s' (%s), %s sync" , getName(), fRemoteViewOfLocalURI.c_str(), fSlowSync ? "slow" : "normal" ); }; | |||
4686 | if (PDEBUGTEST(DBG_HOT)(((0x00000001) & getDbgMask()) == (0x00000001))) { | |||
4687 | string stats = | |||
4688 | "==================================================\n"; | |||
4689 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4690 | stats += " on Server on Client\n"; | |||
4691 | } | |||
4692 | else { | |||
4693 | stats += " on Client on Server\n"; | |||
4694 | } | |||
4695 | StringObjAppendPrintf(stats,"Added: %9ld %9ld\n",(long)fLocalItemsAdded,(long)fRemoteItemsAdded); | |||
4696 | StringObjAppendPrintf(stats,"Deleted: %9ld %9ld\n",(long)fLocalItemsDeleted,(long)fRemoteItemsDeleted); | |||
4697 | StringObjAppendPrintf(stats,"Updated: %9ld %9ld\n",(long)fLocalItemsUpdated,(long)fRemoteItemsUpdated); | |||
4698 | StringObjAppendPrintf(stats,"Rejected with error: %9ld %9ld\n\n",(long)fLocalItemsError,(long)fRemoteItemsError); | |||
4699 | #ifdef SYSYNC_SERVER1 | |||
4700 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4701 | StringObjAppendPrintf(stats,"SlowSync Matches: %9ld\n",(long)fSlowSyncMatches); | |||
4702 | StringObjAppendPrintf(stats,"Server won Conflicts: %9ld\n",(long)fConflictsServerWins); | |||
4703 | StringObjAppendPrintf(stats,"Client won Conflicts: %9ld\n",(long)fConflictsClientWins); | |||
4704 | StringObjAppendPrintf(stats,"Conflicts with Duplication: %9ld\n\n",(long)fConflictsDuplicated); | |||
4705 | } | |||
4706 | #endif | |||
4707 | StringObjAppendPrintf(stats,"Content Data Bytes sent: %9ld\n",(long)fOutgoingDataBytes); | |||
4708 | StringObjAppendPrintf(stats,"Content Data Bytes received: %9ld\n\n",(long)fIncomingDataBytes); | |||
4709 | StringObjAppendPrintf(stats,"Duration of sync [seconds]: %9ld\n",(long)((fEndOfSyncTime-fSessionP->getSessionStarted())/secondToLinearTimeFactor)); | |||
4710 | PDEBUGPUTSXX(DBG_HOT,stats.c_str(),0,true){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->DebugPuts( 0x00000001,stats.c_str(),0,true); }; | |||
4711 | } | |||
4712 | if (isAborted()) { | |||
4713 | // failed | |||
4714 | PDEBUGPRINTFX(DBG_ERROR,("Warning: Failed with status code=%hd, statistics are incomplete!!",fAbortStatusCode)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: Failed with status code=%hd, statistics are incomplete!!" ,fAbortStatusCode); }; | |||
4715 | } | |||
4716 | #endif | |||
4717 | } // TLocalEngineDS::showStatistics | |||
4718 | ||||
4719 | ||||
4720 | ||||
4721 | // create a new syncop command for sending to remote | |||
4722 | TSyncOpCommand *TLocalEngineDS::newSyncOpCommand( | |||
4723 | TSyncItem *aSyncItemP, // the sync item | |||
4724 | TSyncItemType *aSyncItemTypeP, // the sync item type | |||
4725 | cAppCharP aLocalIDPrefix | |||
4726 | ) | |||
4727 | { | |||
4728 | // get operation | |||
4729 | TSyncOperation syncop=aSyncItemP->getSyncOp(); | |||
4730 | // obtain meta | |||
4731 | SmlPcdataPtr_t metaP = newMetaType(aSyncItemTypeP->getTypeName()); | |||
4732 | // create command | |||
4733 | TSyncOpCommand *syncopcmdP = new TSyncOpCommand(fSessionP,this,syncop,metaP); | |||
4734 | // make sure item does not have stuff it is not allowed to have | |||
4735 | // %%% SCTS does not like SourceURI in Replace and Delete commands sent to Client | |||
4736 | // there are the only ones allowed to carry a GUID | |||
4737 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4738 | #ifdef SYSYNC_SERVER1 | |||
4739 | // Server: commands only have remote IDs, except add which only has target ID | |||
4740 | if (syncop==sop_add || syncop==sop_wants_add) | |||
4741 | aSyncItemP->clearRemoteID(); // no remote ID | |||
4742 | else { | |||
4743 | if (!fDSConfigP->fAlwaysSendLocalID && | |||
4744 | aSyncItemP->hasRemoteID()) { | |||
4745 | // only if localID may not be included in all syncops, | |||
4746 | // and not if the item has no remote ID yet | |||
4747 | // | |||
4748 | // The second case had to be added to solve an issue | |||
4749 | // during suspended syncs: | |||
4750 | // - server tries to add a new item and uses the Replace op for it | |||
4751 | // - pending Replace is added to map | |||
4752 | // - next sync resends the Replace, but with empty IDs and thus | |||
4753 | // cannot be processed by client | |||
4754 | // | |||
4755 | // Log from such a failed sync: | |||
4756 | // Item localID='328' already has map entry: remoteid='', mapflags=0x1, changed=0, deleted=0, added=0, markforresume=0, savedmark=1 | |||
4757 | // Resuming and found marked-for-resume -> send replace | |||
4758 | // ... | |||
4759 | // Command 'Replace': is 1-th counted cmd, cmdsize(+tags needed to end msg)=371, available=130664 (maxfree=299132, freeaftersend=298761, notUsableBufferBytes()=168468) | |||
4760 | // Item remoteID='', localID='', datasize=334 | |||
4761 | // Replace: issued as (outgoing MsgID=2, CmdID=4), now queueing for status | |||
4762 | // ... | |||
4763 | // Status 404: Replace target not found on client -> silently ignore but remove map in server (item will be added in next session), | |||
4764 | aSyncItemP->clearLocalID(); // no local ID | |||
4765 | } | |||
4766 | } | |||
4767 | #endif | |||
4768 | } | |||
4769 | else { | |||
4770 | // Client: all commands only have local IDs | |||
4771 | aSyncItemP->clearRemoteID(); // no remote ID | |||
4772 | } | |||
4773 | // add the localID prefix if we do have a localID to send | |||
4774 | if (aSyncItemP->hasLocalID()) { | |||
4775 | if (IS_SERVER(getSyncAppBase()->isServer())) { | |||
4776 | #ifdef SYSYNC_SERVER1 | |||
4777 | // make sure GUID (plus prefixes) is not exceeding allowed size | |||
4778 | adjustLocalIDforSize(aSyncItemP->fLocalID,getRemoteDatastore()->getMaxGUIDSize(),aLocalIDPrefix ? strlen(aLocalIDPrefix) : 0); | |||
4779 | #endif | |||
4780 | } | |||
4781 | // add local ID prefix, if any | |||
4782 | if (aLocalIDPrefix && *aLocalIDPrefix) | |||
4783 | aSyncItemP->fLocalID.insert(0,aLocalIDPrefix); | |||
4784 | } | |||
4785 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
4786 | // init item generation variables | |||
4787 | fItemSizeLimit=fSizeLimit; | |||
4788 | #else | |||
4789 | fItemSizeLimit=-1; // no limit | |||
4790 | #endif | |||
4791 | // now add item | |||
4792 | SmlItemPtr_t itemP = aSyncItemTypeP->newSmlItem(aSyncItemP,this); | |||
4793 | // check if data size is ok | |||
4794 | if (itemP && fSessionP->fMaxOutgoingObjSize) { | |||
4795 | if (itemP->data && itemP->data->content && itemP->data->length) { | |||
4796 | // there is data, check if size is ok | |||
4797 | if (itemP->data->length > (MemSize_t)fSessionP->fMaxOutgoingObjSize) { | |||
4798 | // too large, suppress it | |||
4799 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend" , (long)itemP->data->length, (long)fSessionP->fMaxOutgoingObjSize ); } | |||
4800 | "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend" , (long)itemP->data->length, (long)fSessionP->fMaxOutgoingObjSize ); } | |||
4801 | (long)itemP->data->length,{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend" , (long)itemP->data->length, (long)fSessionP->fMaxOutgoingObjSize ); } | |||
4802 | (long)fSessionP->fMaxOutgoingObjSize{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend" , (long)itemP->data->length, (long)fSessionP->fMaxOutgoingObjSize ); } | |||
4803 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "WARNING: outgoing item is larger (%ld) than MaxObjSize (%ld) of remote -> suppress now/mark for resend" , (long)itemP->data->length, (long)fSessionP->fMaxOutgoingObjSize ); }; | |||
4804 | smlFreeItemPtr(itemP); | |||
4805 | itemP=NULL__null; | |||
4806 | // mark item for resend | |||
4807 | // For datastores without resume support, this will just have no effect at all | |||
4808 | engMarkItemForResend(aSyncItemP->getLocalID(),aSyncItemP->getRemoteID()); | |||
4809 | } | |||
4810 | } | |||
4811 | } | |||
4812 | if (itemP) { | |||
4813 | // add it to the command | |||
4814 | syncopcmdP->addItem(itemP); | |||
4815 | } | |||
4816 | else { | |||
4817 | // no item - command is invalid, delete it | |||
4818 | delete syncopcmdP; | |||
4819 | syncopcmdP=NULL__null; | |||
4820 | } | |||
4821 | // return command | |||
4822 | return syncopcmdP; | |||
4823 | } // TLocalEngineDS::newSyncOpCommand | |||
4824 | ||||
4825 | ||||
4826 | // create SyncItem suitable for being sent from local to remote | |||
4827 | TSyncItem *TLocalEngineDS::newItemForRemote( | |||
4828 | uInt16 aExpectedTypeID // typeid of expected type | |||
4829 | ) | |||
4830 | { | |||
4831 | // safety | |||
4832 | if (!canCreateItemForRemote()) | |||
4833 | SYSYNC_THROW(TSyncException("newItemForRemote called without sufficient type information ready"))throw TSyncException("newItemForRemote called without sufficient type information ready" ); | |||
4834 | // create | |||
4835 | TSyncItem *itemP = fLocalSendToRemoteTypeP->newSyncItem(fRemoteReceiveFromLocalTypeP,this); | |||
4836 | if (!itemP) | |||
4837 | SYSYNC_THROW(TSyncException("newItemForRemote could not create item"))throw TSyncException("newItemForRemote could not create item" ); | |||
4838 | // check type | |||
4839 | if (!itemP->isBasedOn(aExpectedTypeID)) { | |||
4840 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "newItemForRemote created item of typeID %hd, caller expects %hd" , itemP->getTypeID(), aExpectedTypeID ); } | |||
4841 | "newItemForRemote created item of typeID %hd, caller expects %hd",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "newItemForRemote created item of typeID %hd, caller expects %hd" , itemP->getTypeID(), aExpectedTypeID ); } | |||
4842 | itemP->getTypeID(),{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "newItemForRemote created item of typeID %hd, caller expects %hd" , itemP->getTypeID(), aExpectedTypeID ); } | |||
4843 | aExpectedTypeID{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "newItemForRemote created item of typeID %hd, caller expects %hd" , itemP->getTypeID(), aExpectedTypeID ); } | |||
4844 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "newItemForRemote created item of typeID %hd, caller expects %hd" , itemP->getTypeID(), aExpectedTypeID ); }; | |||
4845 | SYSYNC_THROW(TSyncException("newItemForRemote created wrong item type"))throw TSyncException("newItemForRemote created wrong item type" ); | |||
4846 | } | |||
4847 | return itemP; | |||
4848 | } // TLocalEngineDS::newItemForRemote | |||
4849 | ||||
4850 | ||||
4851 | // return pure relative (item) URI (removes absolute part or ./ prefix) | |||
4852 | const char *TLocalEngineDS::DatastoreRelativeURI(const char *aURI) | |||
4853 | { | |||
4854 | return relativeURI(relativeURI(aURI,fSessionP->getLocalURI()),getName()); | |||
4855 | } // TLocalEngineDS::DatastoreRelativeURI | |||
4856 | ||||
4857 | ||||
4858 | ||||
4859 | // - init filtering and check if needed (sets fTypeFilteringNeeded, fFilteringNeeded and fFilteringNeededForAll) | |||
4860 | void TLocalEngineDS::initPostFetchFiltering(void) | |||
4861 | { | |||
4862 | #ifdef OBJECT_FILTERING1 | |||
4863 | if (!fLocalSendToRemoteTypeP) { | |||
4864 | fTypeFilteringNeeded=false; | |||
4865 | fFilteringNeeded=false; | |||
4866 | fFilteringNeededForAll=false; | |||
4867 | } | |||
4868 | else { | |||
4869 | // get basic settings from type | |||
4870 | fLocalSendToRemoteTypeP->initPostFetchFiltering(fTypeFilteringNeeded,fFilteringNeededForAll,this); | |||
4871 | fFilteringNeeded=fTypeFilteringNeeded; | |||
4872 | // NOTE: if type filtering is needed, it's the responsibility of initPostFetchFiltering() of | |||
4873 | // the type to check (using the DBHANDLESOPTS() script func) if DB does already handle | |||
4874 | // the range filters and such and possibly avoid type filtering then. | |||
4875 | // then check for standard filter requirements | |||
4876 | #ifdef SYDEBUG2 | |||
4877 | #ifdef SYNCML_TAF_SUPPORT | |||
4878 | if (!fTargetAddressFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic, temporary) TAF expression from CGI : %s",fTargetAddressFilter.c_str())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("using (dynamic, temporary) TAF expression from CGI : %s" ,fTargetAddressFilter.c_str()); }; | |||
4879 | if (!fIntTargetAddressFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic, temporary) internally set TAF expression : %s",fIntTargetAddressFilter.c_str())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("using (dynamic, temporary) internally set TAF expression : %s" ,fIntTargetAddressFilter.c_str()); }; | |||
4880 | #endif // SYNCML_TAF_SUPPORT | |||
4881 | if (!fSyncSetFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (dynamic) sync set filter expression : %s",fSyncSetFilter.c_str())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("using (dynamic) sync set filter expression : %s" ,fSyncSetFilter.c_str()); }; | |||
4882 | if (!fLocalDBFilter.empty()) PDEBUGPRINTFX(DBG_DATA,("using (static) local db filter expression : %s",fLocalDBFilter.c_str())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("using (static) local db filter expression : %s" ,fLocalDBFilter.c_str()); }; | |||
4883 | #endif // SYDEBUG | |||
4884 | // - if DB does the standard filters, we don't need to check them here again | |||
4885 | if (!engFilteredFetchesFromDB(true)) { | |||
4886 | // If DB does NOT do the standard filters, we have to do them here | |||
4887 | // - this is the case if we have an (old-style) sync set filter, but not filtered by DB | |||
4888 | // we need to filter all because sync set filter can be dynamic | |||
4889 | if (!fSyncSetFilter.empty()) | |||
4890 | fFilteringNeededForAll=true; | |||
4891 | // always return true if there is something to filter at all | |||
4892 | if ( | |||
4893 | !fLocalDBFilter.empty() || | |||
4894 | !fDSConfigP->fInvisibleFilter.empty() || | |||
4895 | !fSyncSetFilter.empty() || | |||
4896 | !fDSConfigP->fRemoteAcceptFilter.empty() | |||
4897 | ) | |||
4898 | fFilteringNeeded=true; | |||
4899 | } | |||
4900 | } | |||
4901 | PDEBUGPRINTFX(DBG_FILTER+DBG_HOT,({ if (((0x08000000 +0x00000001) & getDbgMask()) == (0x08000000 +0x00000001)) getDbgLogger()->setNextMask(0x08000000 +0x00000001 ).DebugPrintfLastMask ( "Datastore-level postfetch filtering %sneeded%s" , fFilteringNeeded ? "" : "NOT ", fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records" ) : "" ); } | |||
4902 | "Datastore-level postfetch filtering %sneeded%s",{ if (((0x08000000 +0x00000001) & getDbgMask()) == (0x08000000 +0x00000001)) getDbgLogger()->setNextMask(0x08000000 +0x00000001 ).DebugPrintfLastMask ( "Datastore-level postfetch filtering %sneeded%s" , fFilteringNeeded ? "" : "NOT ", fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records" ) : "" ); } | |||
4903 | fFilteringNeeded ? "" : "NOT ",{ if (((0x08000000 +0x00000001) & getDbgMask()) == (0x08000000 +0x00000001)) getDbgLogger()->setNextMask(0x08000000 +0x00000001 ).DebugPrintfLastMask ( "Datastore-level postfetch filtering %sneeded%s" , fFilteringNeeded ? "" : "NOT ", fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records" ) : "" ); } | |||
4904 | fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records") : ""{ if (((0x08000000 +0x00000001) & getDbgMask()) == (0x08000000 +0x00000001)) getDbgLogger()->setNextMask(0x08000000 +0x00000001 ).DebugPrintfLastMask ( "Datastore-level postfetch filtering %sneeded%s" , fFilteringNeeded ? "" : "NOT ", fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records" ) : "" ); } | |||
4905 | )){ if (((0x08000000 +0x00000001) & getDbgMask()) == (0x08000000 +0x00000001)) getDbgLogger()->setNextMask(0x08000000 +0x00000001 ).DebugPrintfLastMask ( "Datastore-level postfetch filtering %sneeded%s" , fFilteringNeeded ? "" : "NOT ", fFilteringNeeded ? (fFilteringNeededForAll ? " and to be applied to all records" : " only for changed records" ) : "" ); }; | |||
4906 | #endif | |||
4907 | } // TLocalEngineDS::initPostFetchFiltering | |||
4908 | ||||
4909 | ||||
4910 | // filter fetched record | |||
4911 | bool TLocalEngineDS::postFetchFiltering(TSyncItem *aSyncItemP) | |||
4912 | { | |||
4913 | #ifndef OBJECT_FILTERING1 | |||
4914 | return true; // no filters, always pass | |||
4915 | #else | |||
4916 | if (!aSyncItemP) return false; // null item does not pass | |||
4917 | // first do standard filters | |||
4918 | // - if DB has filtered the | |||
4919 | bool passes=true; | |||
4920 | if (fFilteringNeeded) { | |||
4921 | // - first make sure outgoing object has all properties set | |||
4922 | // such that it would pass the acceptance filter (for example KIND for calendar...) | |||
4923 | if (!aSyncItemP->makePassFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) { | |||
4924 | // we could not make item pass acceptance filters | |||
4925 | PDEBUGPRINTFX(DBG_ERROR,("- item localid='%s' cannot be made passing <acceptfilter> -> ignored",aSyncItemP->getLocalID())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("- item localid='%s' cannot be made passing <acceptfilter> -> ignored" ,aSyncItemP->getLocalID()); }; | |||
4926 | passes=false; | |||
4927 | } | |||
4928 | // now check for field-level filters | |||
4929 | if (passes && !engFilteredFetchesFromDB()) { | |||
4930 | // DB has not already filtered these, so we need to do it here | |||
4931 | // - "moving target" first | |||
4932 | passes=fSyncSetFilter.empty() || aSyncItemP->testFilter(fSyncSetFilter.c_str()); | |||
4933 | if (passes) { | |||
4934 | // - static filters | |||
4935 | passes = | |||
4936 | aSyncItemP->testFilter(fLocalDBFilter.c_str()) && // local filter | |||
4937 | ( | |||
4938 | fDSConfigP->fInvisibleFilter.empty() || // and either no invisibility defined... | |||
4939 | !aSyncItemP->testFilter(fDSConfigP->fInvisibleFilter.c_str()) // ...or NOT passed | |||
4940 | ); | |||
4941 | } | |||
4942 | } | |||
4943 | if (passes && fTypeFilteringNeeded) { | |||
4944 | // finally, apply type's filter | |||
4945 | passes=aSyncItemP->postFetchFiltering(this); | |||
4946 | } | |||
4947 | } | |||
4948 | else { | |||
4949 | // no filtering needed, DB has already filtered out those that would not pass | |||
4950 | // BUT: make sure outgoing items WILL pass the acceptance filter. If this | |||
4951 | // cannot be done, item will be filtered out. | |||
4952 | if (!aSyncItemP->makePassFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) { | |||
4953 | // we could not make item pass acceptance filters | |||
4954 | PDEBUGPRINTFX(DBG_ERROR,("- item localid='%s' cannot be made passing <acceptfilter> -> ignored",aSyncItemP->getLocalID())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("- item localid='%s' cannot be made passing <acceptfilter> -> ignored" ,aSyncItemP->getLocalID()); }; | |||
4955 | passes=false; | |||
4956 | } | |||
4957 | } | |||
4958 | #ifdef SYDEBUG2 | |||
4959 | if (!passes) { | |||
4960 | PDEBUGPRINTFX(DBG_DATA,("- item localid='%s' does not pass filters -> ignored",aSyncItemP->getLocalID())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("- item localid='%s' does not pass filters -> ignored" ,aSyncItemP->getLocalID()); }; | |||
4961 | } | |||
4962 | #endif | |||
4963 | // return result | |||
4964 | return passes; | |||
4965 | #endif | |||
4966 | } // TLocalEngineDS::postFetchFiltering | |||
4967 | ||||
4968 | ||||
4969 | #ifdef OBJECT_FILTERING1 | |||
4970 | ||||
4971 | // - called to check if incoming item passes acception filters | |||
4972 | bool TLocalEngineDS::isAcceptable(TSyncItem *aSyncItemP, TStatusCommand &aStatusCommand) | |||
4973 | { | |||
4974 | // test acceptance | |||
4975 | if (aSyncItemP->testFilter(fDSConfigP->fRemoteAcceptFilter.c_str())) return true; // ok | |||
4976 | // not accepted, set 415 error | |||
4977 | if (!fDSConfigP->fSilentlyDiscardUnaccepted) | |||
4978 | aStatusCommand.setStatusCode(415); | |||
4979 | ADDDEBUGITEM(aStatusCommand,"Received item does not pass acceptance filter"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Received item does not pass acceptance filter" ); }; | |||
4980 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Received item does not pass acceptance filter: %s" , fDSConfigP->fRemoteAcceptFilter.c_str() ); } | |||
4981 | "Received item does not pass acceptance filter: %s",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Received item does not pass acceptance filter: %s" , fDSConfigP->fRemoteAcceptFilter.c_str() ); } | |||
4982 | fDSConfigP->fRemoteAcceptFilter.c_str(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Received item does not pass acceptance filter: %s" , fDSConfigP->fRemoteAcceptFilter.c_str() ); } | |||
4983 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Received item does not pass acceptance filter: %s" , fDSConfigP->fRemoteAcceptFilter.c_str() ); }; | |||
4984 | return false; | |||
4985 | } // TLocalEngineDS::isAcceptable | |||
4986 | ||||
4987 | ||||
4988 | /// @brief called to make incoming item visible | |||
4989 | /// @return true if now visible | |||
4990 | bool TLocalEngineDS::makeVisible(TSyncItem *aSyncItemP) | |||
4991 | { | |||
4992 | bool invisible=false; | |||
4993 | if (!fDSConfigP->fInvisibleFilter.empty()) { | |||
4994 | invisible=aSyncItemP->testFilter(fDSConfigP->fInvisibleFilter.c_str()); | |||
4995 | } | |||
4996 | if (invisible) { | |||
4997 | return aSyncItemP->makePassFilter(fDSConfigP->fMakeVisibleFilter.c_str()); | |||
4998 | } | |||
4999 | return true; // is already visible | |||
5000 | } // TLocalEngineDS::makeVisible | |||
5001 | ||||
5002 | ||||
5003 | /// @brief called to make incoming item INvisible | |||
5004 | /// @return true if now INvisible | |||
5005 | bool TLocalEngineDS::makeInvisible(TSyncItem *aSyncItemP) | |||
5006 | { | |||
5007 | // return true if could make invisible or already was invisible | |||
5008 | if (fDSConfigP->fInvisibleFilter.empty()) | |||
5009 | return false; // no invisible filter, cannot make invisible | |||
5010 | // make pass invisible filter - if successful, we're now invisible | |||
5011 | return aSyncItemP->makePassFilter(fDSConfigP->fInvisibleFilter.c_str()); // try to make invisible (and return result) | |||
5012 | } // TLocalEngineDS::makeInvisible | |||
5013 | ||||
5014 | ||||
5015 | ||||
5016 | // - called to make incoming item pass sync set filtering | |||
5017 | bool TLocalEngineDS::makePassSyncSetFilter(TSyncItem *aSyncItemP) | |||
5018 | { | |||
5019 | bool pass=true; | |||
5020 | ||||
5021 | // make sure we pass sync set filtering and stay visible | |||
5022 | if (!fSyncSetFilter.empty()) { | |||
5023 | // try to make pass sync set filter (modifies item only if it would not pass otherwise) | |||
5024 | pass=aSyncItemP->makePassFilter(fSyncSetFilter.c_str()); | |||
5025 | } | |||
5026 | if (!pass || fSyncSetFilter.empty()) { | |||
5027 | // specified sync set filter cannot make item pass, or no sync set filter at all: | |||
5028 | // - apply makePassFilter default expression | |||
5029 | if (!fDSConfigP->fMakePassFilter.empty()) { | |||
5030 | pass=aSyncItemP->makePassFilter(fDSConfigP->fMakePassFilter.c_str()); | |||
5031 | if (pass) { | |||
5032 | // check again to check if item would pass the syncset filter now | |||
5033 | pass=aSyncItemP->testFilter(fSyncSetFilter.c_str()); | |||
5034 | } | |||
5035 | } | |||
5036 | } | |||
5037 | return pass; | |||
5038 | } // TLocalEngineDS::makePassSyncSetFilter | |||
5039 | ||||
5040 | #endif | |||
5041 | ||||
5042 | ||||
5043 | // process remote item | |||
5044 | bool TLocalEngineDS::engProcessRemoteItem( | |||
5045 | TSyncItem *syncitemP, | |||
5046 | TStatusCommand &aStatusCommand | |||
5047 | ) | |||
5048 | { | |||
5049 | #ifdef SYSYNC_CLIENT1 | |||
5050 | if (IS_CLIENT(!getSyncAppBase()->isServer())) | |||
5051 | return engProcessRemoteItemAsClient(syncitemP,aStatusCommand); // status, must be set to correct status code (ok / error) | |||
5052 | #endif | |||
5053 | #ifdef SYSYNC_SERVER1 | |||
5054 | if (IS_SERVER(getSyncAppBase()->isServer())) | |||
5055 | return engProcessRemoteItemAsServer(syncitemP,aStatusCommand); // status, must be set to correct status code (ok / error) | |||
5056 | #endif | |||
5057 | // neither | |||
5058 | return false; | |||
5059 | } // TLocalEngineDS::engProcessRemoteItem | |||
5060 | ||||
5061 | class SyncOpItemAux : public SmlItemAux_t { | |||
5062 | static void freeAuxImpl(SmlItemAuxPtr_t ptr); | |||
5063 | public: | |||
5064 | SyncOpItemAux(); | |||
5065 | ||||
5066 | TSyncItemType *remoteTypeP; | |||
5067 | TSyncItemType *localTypeP; | |||
5068 | TFmtTypes fmt; | |||
5069 | TSyncItem *syncitemP; | |||
5070 | }; | |||
5071 | ||||
5072 | SyncOpItemAux::SyncOpItemAux() | |||
5073 | { | |||
5074 | freeAux = freeAuxImpl; | |||
5075 | } | |||
5076 | ||||
5077 | void SyncOpItemAux::freeAuxImpl(SmlItemAuxPtr_t ptr) | |||
5078 | { | |||
5079 | delete static_cast<SyncOpItemAux *>(ptr); | |||
5080 | } | |||
5081 | ||||
5082 | // process SyncML SyncOp command for this datastore | |||
5083 | bool TLocalEngineDS::engProcessSyncOpItem( | |||
5084 | TSyncOperation aSyncOp, // the operation | |||
5085 | SmlItemPtr_t aItemP, // the item to be processed | |||
5086 | SmlMetInfMetInfPtr_t aMetaP, // command-wide meta, if any | |||
5087 | TStatusCommand &aStatusCommand // pre-set 200 status, can be modified in case of errors | |||
5088 | ) | |||
5089 | { | |||
5090 | bool regular = false; | |||
5091 | // determine SyncItemType that can handle this item data | |||
5092 | if (fRemoteDatastoreP==NULL__null) { | |||
5093 | PDEBUGPRINTFX(DBG_ERROR,("engProcessSyncOpItem: Remote Datastore not known")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("engProcessSyncOpItem: Remote Datastore not known" ); }; | |||
5094 | aStatusCommand.setStatusCode(500); | |||
5095 | } | |||
5096 | ||||
5097 | // We need the local and remote type plus format to | |||
5098 | // process the item. | |||
5099 | TSyncItemType *remoteTypeP; | |||
5100 | TSyncItemType *localTypeP; | |||
5101 | TFmtTypes fmt; | |||
5102 | TSyncItem *syncitemP=NULL__null; | |||
5103 | ||||
5104 | string versstr; | |||
5105 | ||||
5106 | SyncOpItemAux *aux = aItemP->aux ? static_cast<SyncOpItemAux *>(aItemP->aux) : NULL__null; | |||
5107 | if (aux) { | |||
5108 | // Reuse the previously calculated | |||
5109 | // values when being called again. | |||
5110 | remoteTypeP = aux->remoteTypeP; | |||
5111 | localTypeP = aux->remoteTypeP; | |||
5112 | fmt = aux->fmt; | |||
5113 | syncitemP = aux->syncitemP; | |||
5114 | ||||
5115 | goto process; | |||
5116 | } | |||
5117 | ||||
5118 | if (false) { | |||
5119 | again: | |||
5120 | if (!aux) { | |||
5121 | aux = new SyncOpItemAux; | |||
5122 | } | |||
5123 | aItemP->aux = aux; | |||
5124 | // cppcheck-suppress uninitvar | |||
5125 | aux->remoteTypeP = remoteTypeP; | |||
5126 | // cppcheck-suppress uninitvar | |||
5127 | aux->localTypeP = localTypeP; | |||
5128 | aux->fmt = fmt; | |||
5129 | aux->syncitemP = syncitemP; | |||
5130 | return false; | |||
5131 | } | |||
5132 | ||||
5133 | ||||
5134 | // - start with default | |||
5135 | remoteTypeP=getRemoteSendType(); | |||
5136 | localTypeP=getLocalReceiveType(); | |||
5137 | // - see if command-wide meta plus item contents specify another type | |||
5138 | // (item meta, if present, overrides command wide meta) | |||
5139 | // see if item itself or command meta specify a type name or format | |||
5140 | SmlMetInfMetInfPtr_t itemmetaP; | |||
5141 | itemmetaP = smlPCDataToMetInfP(aItemP->meta); | |||
5142 | // - format | |||
5143 | fmt=fmt_chr; | |||
5144 | if (itemmetaP && itemmetaP->format) | |||
5145 | smlPCDataToFormat(itemmetaP->format,fmt); // use type name from item's meta | |||
5146 | else if (aMetaP && aMetaP->format) | |||
5147 | smlPCDataToFormat(aMetaP->format,fmt); // use type name from command-wide meta | |||
5148 | // - type | |||
5149 | const char *typestr; | |||
5150 | typestr = NULL__null; | |||
5151 | if (itemmetaP && itemmetaP->type) | |||
5152 | typestr = smlPCDataToCharP(itemmetaP->type); // use type name from item's meta | |||
5153 | else if (aMetaP && aMetaP->type) | |||
5154 | typestr = smlPCDataToCharP(aMetaP->type); // use type name from command-wide meta | |||
5155 | // check if there is a type specified | |||
5156 | if (typestr) { | |||
5157 | PDEBUGPRINTFX(DBG_DATA,("Explicit type '%s' specified in command or item meta",typestr)){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Explicit type '%s' specified in command or item meta" ,typestr); }; | |||
5158 | if (strcmp(remoteTypeP->getTypeName(),typestr)!=0) { | |||
5159 | // specified type is NOT default type: search appropriate remote type | |||
5160 | remoteTypeP=fRemoteDatastoreP->getSendType(typestr,NULL__null); // no version known so far | |||
5161 | if (!remoteTypeP) { | |||
5162 | // specified type is not a remote type listed in remote's devInf. | |||
5163 | // But as remote is actually using it, we can assume it does support it, so use local type of same name instead | |||
5164 | PDEBUGPRINTFX(DBG_ERROR,("According to remote devInf, '%s' is not supported, but obviously it is used here so we try to handle it",typestr)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("According to remote devInf, '%s' is not supported, but obviously it is used here so we try to handle it" ,typestr); }; | |||
5165 | // look it up in local datastore's list | |||
5166 | remoteTypeP=getReceiveType(typestr,NULL__null); | |||
5167 | } | |||
5168 | } | |||
5169 | if (remoteTypeP) { | |||
5170 | #ifdef APP_CAN_EXPIRE | |||
5171 | // get modified date of item | |||
5172 | lineardate_t moddat=0; // IMPORTANT, must be initialized in case expiryFromData returns nothing! | |||
5173 | bool ok = remoteTypeP->expiryFromData(aItemP,moddat)<=MAX_EXPIRY_DIFF15+5; | |||
5174 | // ok==true: we are within hard expiry | |||
5175 | // ok==false: we are out of hard expiry | |||
5176 | #ifdef SYSER_REGISTRATION | |||
5177 | if (getSession()->getSyncAppBase()->fRegOK) { | |||
5178 | // we have a license (permanent or timed) --> hard expiry is irrelevant | |||
5179 | // (so override ok according to validity of current license) | |||
5180 | ok=true; // assume ok | |||
5181 | // check if license is timed, and if so, check if mod date is within timed range | |||
5182 | // (if not, set ok to false) | |||
5183 | uInt8 rd = getSession()->getSyncAppBase()->fRegDuration; | |||
5184 | if (rd) { | |||
5185 | lineardate_t ending = date2lineardate(rd/12+2000,rd%12+1,1); | |||
5186 | ok = ending>=moddat; // ok if not modified after end of license period | |||
5187 | } | |||
5188 | } | |||
5189 | #endif | |||
5190 | // when we have no license (neither permanent nor timed), hard expiry decides as is | |||
5191 | // (so just use ok as is) | |||
5192 | if (!ok) { | |||
5193 | aStatusCommand.setStatusCode(403); // forbidden to hack this expiry stuff! | |||
5194 | fSessionP->AbortSession(403,true); // local problem | |||
5195 | return false; | |||
5196 | } | |||
5197 | #endif // APP_CAN_EXPIRE | |||
5198 | // we have a type, which should be able to determine version from data | |||
5199 | if (remoteTypeP->versionFromData(aItemP,versstr)) { | |||
5200 | // version found, Make sure version matches as well | |||
5201 | PDEBUGPRINTFX(DBG_DATA,("Version '%s' obtained from item data",versstr.c_str())){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Version '%s' obtained from item data" ,versstr.c_str()); }; | |||
5202 | // check if current remotetype already has correct version (and type, but we know this already) | |||
5203 | if (!remoteTypeP->supportsType(remoteTypeP->getTypeName(),versstr.c_str(),true)) { | |||
5204 | // no, type/vers do not match, search again | |||
5205 | remoteTypeP=fRemoteDatastoreP->getSendType(typestr,versstr.c_str()); | |||
5206 | if (!remoteTypeP) { | |||
5207 | // specified type is not a remote type listed in remote's devInf. | |||
5208 | // But as remote is actually using it, we can assume it does support it, so use local type of same name instead | |||
5209 | PDEBUGPRINTFX(DBG_ERROR,("According to remote devInf, '%s' version '%s' is not supported, but obviously it is used here so we try to handle it",typestr,versstr.c_str())){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("According to remote devInf, '%s' version '%s' is not supported, but obviously it is used here so we try to handle it" ,typestr,versstr.c_str()); }; | |||
5210 | // look it up in local datastore's list | |||
5211 | remoteTypeP=getReceiveType(typestr,versstr.c_str()); | |||
5212 | } | |||
5213 | } | |||
5214 | } | |||
5215 | else { | |||
5216 | PDEBUGPRINTFX(DBG_HOT,("Version could not be obtained from item data")){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("Version could not be obtained from item data" ); }; | |||
5217 | } | |||
5218 | } | |||
5219 | if (!remoteTypeP) { | |||
5220 | // no matching remote type: fail | |||
5221 | aStatusCommand.setStatusCode(415); | |||
5222 | ADDDEBUGITEM(aStatusCommand,"Incompatible content type specified in command or item meta"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Incompatible content type specified in command or item meta" ); }; | |||
5223 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Incompatible content type '%s' version '%s' specified in command or item meta" , typestr, versstr.empty() ? "[none]" : versstr.c_str() ); } | |||
5224 | "Incompatible content type '%s' version '%s' specified in command or item meta",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Incompatible content type '%s' version '%s' specified in command or item meta" , typestr, versstr.empty() ? "[none]" : versstr.c_str() ); } | |||
5225 | typestr,{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Incompatible content type '%s' version '%s' specified in command or item meta" , typestr, versstr.empty() ? "[none]" : versstr.c_str() ); } | |||
5226 | versstr.empty() ? "[none]" : versstr.c_str(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Incompatible content type '%s' version '%s' specified in command or item meta" , typestr, versstr.empty() ? "[none]" : versstr.c_str() ); } | |||
5227 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Incompatible content type '%s' version '%s' specified in command or item meta" , typestr, versstr.empty() ? "[none]" : versstr.c_str() ); }; | |||
5228 | return false; // irregular | |||
5229 | } | |||
5230 | else { | |||
5231 | // we have the remote type, now determine matching local type | |||
5232 | // - first check if this is compatible with the existing localTypeP (which | |||
5233 | // was possibly selected by remote rule match | |||
5234 | if (!localTypeP->supportsType(remoteTypeP->getTypeName(),remoteTypeP->getTypeVers(),false)) { | |||
5235 | // current default local type does not support specified remote type | |||
5236 | // - find a matching local type | |||
5237 | localTypeP=getReceiveType(remoteTypeP); | |||
5238 | #ifdef SYDEBUG2 | |||
5239 | if (localTypeP) { | |||
5240 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,({ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item" , typestr, localTypeP->getTypeConfig()->getName() ); } | |||
5241 | "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item",{ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item" , typestr, localTypeP->getTypeConfig()->getName() ); } | |||
5242 | typestr,{ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item" , typestr, localTypeP->getTypeConfig()->getName() ); } | |||
5243 | localTypeP->getTypeConfig()->getName(){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item" , typestr, localTypeP->getTypeConfig()->getName() ); } | |||
5244 | )){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Explicit type '%s' does not match default type -> switching to local type '%s' for processing item" , typestr, localTypeP->getTypeConfig()->getName() ); }; | |||
5245 | } | |||
5246 | #endif | |||
5247 | } | |||
5248 | ||||
5249 | } | |||
5250 | } | |||
5251 | // Now process or resume. We cannot jump right into | |||
5252 | // the block because of the try/catch, so some checks | |||
5253 | // for resuming are necessary. | |||
5254 | process: | |||
5255 | // cppcheck-suppress uninitvar | |||
5256 | if (localTypeP && remoteTypeP) { | |||
5257 | // create the item (might have empty data in case of delete) | |||
5258 | if (!syncitemP) | |||
5259 | syncitemP=remoteTypeP->newSyncItem(aItemP,aSyncOp,fmt,localTypeP,this,aStatusCommand); | |||
5260 | if (!syncitemP) { | |||
5261 | // failed to create item | |||
5262 | return false; // irregular | |||
5263 | } | |||
5264 | // Now start the real processing | |||
5265 | PDEBUGBLOCKFMT(("Process_Item","processing remote item",getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ) | |||
5266 | "SyncOp=%s|LocalID=%s|RemoteID=%s",getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ) | |||
5267 | SyncOpNames[syncitemP->getSyncOp()],getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ) | |||
5268 | syncitemP->getLocalID(),getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ) | |||
5269 | syncitemP->getRemoteID()getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ) | |||
5270 | ))getDbgLogger()->DebugOpenBlockExpanded ("Process_Item","processing remote item" , "SyncOp=%s|LocalID=%s|RemoteID=%s", SyncOpNames[syncitemP-> getSyncOp()], syncitemP->getLocalID(), syncitemP->getRemoteID () ); | |||
5271 | #ifdef SCRIPT_SUPPORT1 | |||
5272 | TErrorFuncContext errctx; | |||
5273 | errctx.syncop = syncitemP->getSyncOp(); | |||
5274 | #endif | |||
5275 | SYSYNC_TRYtry { | |||
5276 | // this call frees the item, unless it wants to be called again | |||
5277 | regular = | |||
5278 | engProcessRemoteItem(syncitemP,aStatusCommand); | |||
5279 | if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { | |||
5280 | goto again; | |||
5281 | } | |||
5282 | syncitemP = NULL__null; | |||
5283 | PDEBUGENDBLOCK("Process_Item")getDbgLogger()->DebugCloseBlock( "Process_Item"); | |||
5284 | } | |||
5285 | SYSYNC_CATCH (...)catch(...) { | |||
5286 | // Hmm, was the item freed? Not sure, so assume that it was freed. | |||
5287 | PDEBUGENDBLOCK("Process_Item")getDbgLogger()->DebugCloseBlock( "Process_Item"); | |||
5288 | SYSYNC_RETHROWthrow; | |||
5289 | SYSYNC_ENDCATCH} | |||
5290 | // Check for datastore level scripts that might change the status code and/or regular status | |||
5291 | #ifdef SCRIPT_SUPPORT1 | |||
5292 | errctx.statuscode = aStatusCommand.getStatusCode(); | |||
5293 | errctx.newstatuscode = errctx.statuscode; | |||
5294 | errctx.datastoreP = this; | |||
5295 | // call script | |||
5296 | regular = | |||
5297 | TScriptContext::executeTest( | |||
5298 | regular, // pass through regular status | |||
5299 | fDataStoreScriptContextP, | |||
5300 | fDSConfigP->fReceivedItemStatusScript, | |||
5301 | &ErrorFuncTable, | |||
5302 | &errctx // caller context | |||
5303 | ); | |||
5304 | // use possibly modified status code | |||
5305 | #ifdef SYDEBUG2 | |||
5306 | if (aStatusCommand.getStatusCode() != errctx.newstatuscode) { | |||
5307 | PDEBUGPRINTFX(DBG_ERROR,("Status: Datastore script changed original status=%hd to %hd (original op was %s)",aStatusCommand.getStatusCode(),errctx.newstatuscode,SyncOpNames[errctx.syncop])){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Status: Datastore script changed original status=%hd to %hd (original op was %s)" ,aStatusCommand.getStatusCode(),errctx.newstatuscode,SyncOpNames [errctx.syncop]); }; | |||
5308 | } | |||
5309 | #endif | |||
5310 | aStatusCommand.setStatusCode(errctx.newstatuscode); | |||
5311 | #endif | |||
5312 | if (regular) { | |||
5313 | // item 100% successfully processed | |||
5314 | // - set new defaults to same type as current item | |||
5315 | setReceiveTypeInfo(localTypeP,remoteTypeP); | |||
5316 | } | |||
5317 | } | |||
5318 | else { | |||
5319 | // missing remote or local type: fail | |||
5320 | aStatusCommand.setStatusCode(415); | |||
5321 | ADDDEBUGITEM(aStatusCommand,"Unknown content type"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Unknown content type"); }; | |||
5322 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Missing remote or local SyncItemType" ); } | |||
5323 | "Missing remote or local SyncItemType"{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Missing remote or local SyncItemType" ); } | |||
5324 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Missing remote or local SyncItemType" ); }; | |||
5325 | regular=false; // irregular | |||
5326 | } | |||
5327 | return regular; | |||
5328 | } // TLocalEngineDS::engProcessSyncOpItem | |||
5329 | ||||
5330 | ||||
5331 | #ifdef SYSYNC_SERVER1 | |||
5332 | ||||
5333 | ||||
5334 | // Server Case | |||
5335 | // =========== | |||
5336 | ||||
5337 | // helper to cause database version of an item (as identified by aSyncItemP's ID) to be sent to client | |||
5338 | // (aka "force a conflict") | |||
5339 | TSyncItem *TLocalEngineDS::SendDBVersionOfItemAsServer(TSyncItem *aSyncItemP) | |||
5340 | { | |||
5341 | TStatusCommand dummy(fSessionP); | |||
5342 | // - create new item | |||
5343 | TSyncItem *conflictingItemP = | |||
5344 | newItemForRemote(aSyncItemP->getTypeID()); | |||
5345 | if (!conflictingItemP) return NULL__null; | |||
5346 | // - set IDs | |||
5347 | conflictingItemP->setLocalID(aSyncItemP->getLocalID()); | |||
5348 | conflictingItemP->setRemoteID(aSyncItemP->getRemoteID()); | |||
5349 | // - this is always a replace conflict (item exists in DB) | |||
5350 | conflictingItemP->setSyncOp(sop_wants_replace); | |||
5351 | // - try to get from DB | |||
5352 | bool ok=logicRetrieveItemByID(*conflictingItemP,dummy); | |||
5353 | if (ok && dummy.getStatusCode()!=404) { | |||
5354 | // item found in DB, add it to the sync set so it can be sent to remote | |||
5355 | // if not cancelled by dontSendItemAsServer() | |||
5356 | SendItemAsServer(conflictingItemP); | |||
5357 | PDEBUGPRINTFX(DBG_DATA,("Forced conflict with corresponding item from server DB")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Forced conflict with corresponding item from server DB" ); }; | |||
5358 | } | |||
5359 | else { | |||
5360 | // no item found, we cannot force a conflict | |||
5361 | delete conflictingItemP; | |||
5362 | conflictingItemP=NULL__null; | |||
5363 | } | |||
5364 | return conflictingItemP; | |||
5365 | } // TLocalEngineDS::SendDBVersionOfItemAsServer | |||
5366 | ||||
5367 | ||||
5368 | ||||
5369 | // process map | |||
5370 | localstatus TLocalEngineDS::engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID) | |||
5371 | { | |||
5372 | if (!testState(dssta_syncmodestable)) { | |||
5373 | // Map received when not appropriate | |||
5374 | PDEBUGPRINTFX(DBG_ERROR,("Map not allowed in this stage of sync")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Map not allowed in this stage of sync" ); }; | |||
5375 | return 403; | |||
5376 | } | |||
5377 | // pre-process localID | |||
5378 | string realLocalID; | |||
5379 | if (aLocalID && *aLocalID) { | |||
5380 | // Note: Map must be ready to have either empty local or remote ID to delete an entry | |||
5381 | // perform reverse lookup of received GUID to real GUID | |||
5382 | realLocalID = aLocalID; | |||
5383 | obtainRealLocalID(realLocalID); | |||
5384 | aLocalID=realLocalID.c_str(); | |||
5385 | } | |||
5386 | else { | |||
5387 | aLocalID=NULL__null; | |||
5388 | } | |||
5389 | // pre-process remoteID | |||
5390 | if (!aRemoteID || *aRemoteID==0) | |||
5391 | aRemoteID=NULL__null; | |||
5392 | // let implementation process the map command | |||
5393 | return logicProcessMap(aRemoteID, aLocalID); | |||
5394 | } // TLocalEngineDS::engProcessMap | |||
5395 | ||||
5396 | enum LocalItemOp | |||
5397 | { | |||
5398 | LOCAL_ITEM_DELETE, | |||
5399 | LOCAL_ITEM_ADD_NORMAL, | |||
5400 | LOCAL_ITEM_ADD_DELETED, | |||
5401 | LOCAL_ITEM_ADD_DUPLICATE, | |||
5402 | LOCAL_ITEM_REPLACE_MERGED, | |||
5403 | LOCAL_ITEM_REPLACE_FROM_CLIENT, | |||
5404 | LOCAL_ITEM_REPLACE, | |||
5405 | LOCAL_ITEM_ADD_MERGED, | |||
5406 | LOCAL_ITEM_REPLACE_MERGED2, | |||
5407 | LOCAL_ITEM_ADD_SLOW | |||
5408 | }; | |||
5409 | ||||
5410 | struct TLocalSyncItemAux : public TSyncItemAux | |||
5411 | { | |||
5412 | TSyncItem *fConflictingItemP; | |||
5413 | TSyncItem *fEchoItemP; | |||
5414 | TSyncItem *fDelItemP; | |||
5415 | TSyncItem *fMatchingItemP; | |||
5416 | bool fChangedIncoming; | |||
5417 | bool fChangedExisting; | |||
5418 | bool fRemainsVisible; | |||
5419 | TSyncOperation fSyncOp; | |||
5420 | uInt16 fItemTypeID; | |||
5421 | string fRemoteID; | |||
5422 | LocalItemOp fOp; | |||
5423 | ||||
5424 | TSyncOperation fCurrentSyncOp; | |||
5425 | TSyncOperation fEchoItemOp; | |||
5426 | TConflictResolution fItemConflictStrategy; | |||
5427 | bool fForceConflict; | |||
5428 | bool fDeleteWins; | |||
5429 | bool fPreventAdd; | |||
5430 | bool fIgnoreUpdate; | |||
5431 | sInt16 fRejectStatus; | |||
5432 | }; | |||
5433 | ||||
5434 | // process sync operation from client with specified sync item | |||
5435 | // (according to current sync mode of local datastore) | |||
5436 | // - returns true (and unmodified or non-200-successful status) if | |||
5437 | // operation could be processed regularily | |||
5438 | // - returns false (but probably still successful status) if | |||
5439 | // operation was processed with internal irregularities, such as | |||
5440 | // trying to delete non-existant item in datastore with | |||
5441 | // incomplete Rollbacks (which returns status 200 in this case!). | |||
5442 | bool TLocalEngineDS::engProcessRemoteItemAsServer( | |||
5443 | TSyncItem *aSyncItemP, | |||
5444 | TStatusCommand &aStatusCommand // status, must be set to correct status code (ok / error) | |||
5445 | ) | |||
5446 | { | |||
5447 | // The logic of this function is pretty complex. Instead of trying | |||
5448 | // to retrace our steps when called again after a LOCERR_AGAIN, let's | |||
5449 | // jump to labels directly. For that to work, all local variables | |||
5450 | // must be defined before the goto. | |||
5451 | TSyncItem *conflictingItemP; | |||
5452 | TSyncItem *echoItemP; | |||
5453 | TSyncItem *delitemP; | |||
5454 | TSyncItem *matchingItemP = NULL__null; | |||
5455 | bool changedincoming; | |||
5456 | bool changedexisting; | |||
5457 | bool remainsvisible; | |||
5458 | TSyncOperation syncop; | |||
5459 | uInt16 itemtypeid; | |||
5460 | string remoteid; | |||
5461 | LocalItemOp op; | |||
5462 | bool ok=false; | |||
5463 | ||||
5464 | TLocalSyncItemAux *aux = static_cast<TLocalSyncItemAux *>(aSyncItemP->getAux(TSyncItem::LOCAL_ENGINE)); | |||
5465 | if (aux) { | |||
5466 | // Resuming the function call: restore variables, jump to store | |||
5467 | // method call. | |||
5468 | // | |||
5469 | // The compiler will tell us if we jump across a variable | |||
5470 | // instantiation which initializes the variable ("jump bypasses | |||
5471 | // variable initialization"). Variables which are not needed when | |||
5472 | // resuming must be initialized with normal assignments to avoid | |||
5473 | // this error. Variables which are needed, need to be moved to the | |||
5474 | // section above and added to the store/restore. | |||
5475 | conflictingItemP = aux->fConflictingItemP; | |||
5476 | echoItemP = aux->fEchoItemP; | |||
5477 | delitemP = aux->fDelItemP; | |||
5478 | matchingItemP = aux->fMatchingItemP; | |||
5479 | changedincoming = aux->fChangedIncoming; | |||
5480 | changedexisting = aux->fChangedExisting; | |||
5481 | remainsvisible = aux->fRemainsVisible; | |||
5482 | syncop = aux->fSyncOp; | |||
5483 | itemtypeid = aux->fItemTypeID; | |||
5484 | remoteid = aux->fRemoteID; | |||
5485 | op = aux->fOp; | |||
5486 | ||||
5487 | // Besides the local variables, we also need to set members | |||
5488 | // to the value they had before leaving. See checkItem() below. | |||
5489 | // Some of these will never be different from the default because | |||
5490 | // if they got set in the first call, we don't queue and thus | |||
5491 | // don't get here. But restore them anyway, just to be sure. | |||
5492 | fCurrentSyncOp = aux->fCurrentSyncOp; | |||
5493 | fEchoItemOp = aux->fEchoItemOp; | |||
5494 | fItemConflictStrategy = aux->fItemConflictStrategy; | |||
5495 | fForceConflict = aux->fForceConflict; | |||
5496 | fDeleteWins = aux->fDeleteWins; | |||
5497 | fPreventAdd = aux->fPreventAdd; | |||
5498 | fIgnoreUpdate = aux->fIgnoreUpdate; | |||
5499 | fRejectStatus = aux->fRejectStatus; | |||
5500 | ||||
5501 | PDEBUGPRINTFX(DBG_DATA,("%s item operation resumed",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation resumed" ,SyncOpNames[syncop]); }; | |||
5502 | switch (op) { | |||
5503 | case LOCAL_ITEM_DELETE: goto do_delete; | |||
5504 | case LOCAL_ITEM_ADD_NORMAL: goto do_add_normal; | |||
5505 | case LOCAL_ITEM_ADD_DELETED: goto do_add_deleted; | |||
5506 | case LOCAL_ITEM_ADD_DUPLICATE: goto do_add_duplicate; | |||
5507 | case LOCAL_ITEM_REPLACE_MERGED: goto do_replace_merged; | |||
5508 | case LOCAL_ITEM_REPLACE_FROM_CLIENT: goto do_replace_from_client; | |||
5509 | case LOCAL_ITEM_REPLACE: goto do_replace; | |||
5510 | case LOCAL_ITEM_ADD_MERGED: goto do_add_merged; | |||
5511 | case LOCAL_ITEM_REPLACE_MERGED2: goto do_replace_merged2; | |||
5512 | case LOCAL_ITEM_ADD_SLOW: goto do_add_slow; | |||
5513 | }; | |||
5514 | } | |||
5515 | if (false) { | |||
5516 | // Prepare for resuming the function call. Will only be reached | |||
5517 | // via goto with "op" set to something identifying the source of | |||
5518 | // the jump. | |||
5519 | again: | |||
5520 | #define CHECK_FOR_AGAIN(_op)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = _op ; goto again; } \ | |||
5521 | if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { \ | |||
5522 | op = _op; \ | |||
5523 | goto again; \ | |||
5524 | } | |||
5525 | ||||
5526 | if (!aux) { | |||
5527 | aux = new TLocalSyncItemAux; | |||
5528 | aSyncItemP->setAux(TSyncItem::LOCAL_ENGINE, aux); | |||
5529 | } | |||
5530 | ||||
5531 | // cppcheck-suppress uninitvar | |||
5532 | aux->fConflictingItemP = conflictingItemP; | |||
5533 | // cppcheck-suppress uninitvar | |||
5534 | aux->fEchoItemP = echoItemP; | |||
5535 | // cppcheck-suppress uninitvar | |||
5536 | aux->fDelItemP = delitemP; | |||
5537 | aux->fMatchingItemP = matchingItemP; | |||
5538 | // cppcheck-suppress uninitvar | |||
5539 | aux->fChangedIncoming = changedincoming; | |||
5540 | // cppcheck-suppress uninitvar | |||
5541 | aux->fChangedExisting = changedexisting; | |||
5542 | // cppcheck-suppress uninitvar | |||
5543 | aux->fRemainsVisible = remainsvisible; | |||
5544 | // cppcheck-suppress uninitvar | |||
5545 | aux->fSyncOp = syncop; | |||
5546 | aux->fItemTypeID = itemtypeid; | |||
5547 | aux->fRemoteID = remoteid; | |||
5548 | // cppcheck-suppress uninitvar | |||
5549 | aux->fOp = op; | |||
5550 | ||||
5551 | aux->fCurrentSyncOp = fCurrentSyncOp; | |||
5552 | aux->fEchoItemOp = fEchoItemOp; | |||
5553 | aux->fItemConflictStrategy = fItemConflictStrategy; | |||
5554 | aux->fForceConflict = fForceConflict; | |||
5555 | aux->fDeleteWins = fDeleteWins; | |||
5556 | aux->fPreventAdd = fPreventAdd; | |||
5557 | aux->fIgnoreUpdate = fIgnoreUpdate; | |||
5558 | aux->fRejectStatus = fRejectStatus; | |||
5559 | ||||
5560 | aStatusCommand.setStatusCode(LOCERR_AGAIN); | |||
5561 | return false; | |||
5562 | } | |||
5563 | ||||
5564 | // Normal control flow: initialize variables, then execute. | |||
5565 | conflictingItemP=NULL__null; | |||
5566 | echoItemP=NULL__null; | |||
5567 | delitemP=NULL__null; | |||
5568 | changedincoming=false; | |||
5569 | changedexisting=false; | |||
5570 | remainsvisible=true; // usually, we want the item to remain visible in the sync set | |||
5571 | ||||
5572 | // get some info out of item (we might need it after item is already consumed) | |||
5573 | syncop=aSyncItemP->getSyncOp(); | |||
5574 | itemtypeid=aSyncItemP->getTypeID(); | |||
5575 | remoteid=aSyncItemP->getRemoteID(); | |||
5576 | // check if datastore is aborted | |||
5577 | if(CheckAborted(aStatusCommand)) | |||
5578 | return false; | |||
5579 | // send event (but no abort checking) | |||
5580 | DB_PROGRESS_EVENT(this,pev_itemreceived,++fItemsReceived,fRemoteNumberOfChanges,0)this->getSession()->NotifySessionProgressEvent(pev_itemreceived ,this->getDSConfig(),++fItemsReceived,fRemoteNumberOfChanges ,0); | |||
5581 | fPreventAdd = false; | |||
5582 | fIgnoreUpdate = false; | |||
5583 | // show | |||
5584 | PDEBUGPRINTFX(DBG_DATA,("%s item operation received",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation received" ,SyncOpNames[syncop]); }; | |||
5585 | // check if receiving commands is allowed at all | |||
5586 | if (fSyncMode==smo_fromserver) { | |||
5587 | // Modifications from client not allowed during update from server only | |||
5588 | aStatusCommand.setStatusCode(403); | |||
5589 | ADDDEBUGITEM(aStatusCommand,"Client command not allowed in one-way/refresh from server"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Client command not allowed in one-way/refresh from server" ); }; | |||
5590 | PDEBUGPRINTFX(DBG_ERROR,("Client command not allowed in one-way/refresh from server")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Client command not allowed in one-way/refresh from server" ); }; | |||
5591 | delete aSyncItemP; | |||
5592 | return false; | |||
5593 | } | |||
5594 | // let item check itself to catch special cases | |||
5595 | // - init variables which are used/modified by item checking | |||
5596 | #ifdef SYSYNC_TARGET_OPTIONS1 | |||
5597 | // init item generation variables | |||
5598 | fItemSizeLimit=fSizeLimit; | |||
5599 | #else | |||
5600 | fItemSizeLimit=-1; // no limit | |||
5601 | #endif | |||
5602 | fCurrentSyncOp = syncop; | |||
5603 | fEchoItemOp = sop_none; | |||
5604 | fItemConflictStrategy=fSessionConflictStrategy; // get default strategy for this item | |||
5605 | fForceConflict = false; | |||
5606 | fDeleteWins = fDSConfigP->fDeleteWins; // default to configured value | |||
5607 | fRejectStatus = -1; // no rejection | |||
5608 | // - now check | |||
5609 | // check reads and possibly modifies: | |||
5610 | // - fEchoItemOp : if not sop_none, the incoming item is echoed back to the remote with the specified syncop | |||
5611 | // - fItemConflictStrategy : might be changed from the pre-set datastore default | |||
5612 | // - fForceConflict : if set, a conflict is forced by adding the corresponding local item (if any) to the list of items to be sent | |||
5613 | // - fDeleteWins : if set, in a replace/delete conflict delete will win (regardless of strategy) | |||
5614 | // - fPreventAdd : if set, attempt to add item from remote (even implicitly trough replace) will cause no add but delete of remote item | |||
5615 | // - fIgnoreUpdate : if set, attempt to update item from remote will be ignored, only adds (also implicit ones) are executed | |||
5616 | // - fRejectStatus : if set>=0, incoming item is irgnored silently(==0) or with error(!=0) | |||
5617 | aSyncItemP->checkItem(this); | |||
5618 | // - create echo item if we need one | |||
5619 | if (fEchoItemOp!=sop_none) { | |||
5620 | // Note: sop_add makes no sense at all. | |||
5621 | // Note: If echo is enabled, conflicts are not checked, as echo makes only sense in | |||
5622 | // cases where we know that a conflict cannot occur or is irrelevant | |||
5623 | // - artifically create a "conflicting" item, that is, one to be sent back to remote | |||
5624 | echoItemP=newItemForRemote(aSyncItemP->getTypeID()); | |||
5625 | // - assign data from incoming item if echo is not a delete | |||
5626 | if (fEchoItemOp!=sop_delete && fEchoItemOp!=sop_archive_delete && fEchoItemOp!=sop_soft_delete) | |||
5627 | echoItemP->replaceDataFrom(*aSyncItemP); | |||
5628 | // - set remote ID (note again: sop_add makes no sense here) | |||
5629 | echoItemP->setRemoteID(aSyncItemP->getRemoteID()); | |||
5630 | // - set sop | |||
5631 | echoItemP->setSyncOp(fEchoItemOp); | |||
5632 | // - now check for possible conflict | |||
5633 | if (!fSlowSync) { | |||
5634 | conflictingItemP = getConflictingItemByRemoteID(aSyncItemP); | |||
5635 | // remove item if there is one that would conflict with the echo | |||
5636 | if (conflictingItemP) dontSendItemAsServer(conflictingItemP); | |||
5637 | conflictingItemP = NULL__null; | |||
5638 | } | |||
5639 | // - add echo to the list of items to be sent (DB takes ownership) | |||
5640 | SendItemAsServer(echoItemP); | |||
5641 | PDEBUGPRINTFX(DBG_DATA,("Echoed item back to remote with sop=%s",SyncOpNames[fEchoItemOp])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Echoed item back to remote with sop=%s" ,SyncOpNames[fEchoItemOp]); }; | |||
5642 | // process item normally (except that we don't check for LUID conflicts) | |||
5643 | } | |||
5644 | // - check if incoming item should be processed at all | |||
5645 | if (fRejectStatus>=0) { | |||
5646 | // Note: a forced conflict can still occur even if item is rejected | |||
5647 | // (this has the effect of unconditionally letting the server item win) | |||
5648 | if (fForceConflict && syncop!=sop_add) { | |||
5649 | conflictingItemP = SendDBVersionOfItemAsServer(aSyncItemP); | |||
5650 | // Note: conflictingitem is always a replace | |||
5651 | if (conflictingItemP) { | |||
5652 | if (syncop==sop_delete) { | |||
5653 | // original was delete, forced conflict means re-adding to remote | |||
5654 | conflictingItemP->setSyncOp(sop_wants_add); | |||
5655 | } | |||
5656 | else { | |||
5657 | // merge here because we'll not process the item further | |||
5658 | conflictingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this); | |||
5659 | } | |||
5660 | } | |||
5661 | } | |||
5662 | // now discard the incoming item | |||
5663 | delete aSyncItemP; | |||
5664 | PDEBUGPRINTFX(fRejectStatus>=400 ? DBG_ERROR : DBG_DATA,("Item rejected with Status=%hd",fRejectStatus)){ if (((fRejectStatus>=400 ? 0x00000002 : 0x00000080) & getDbgMask()) == (fRejectStatus>=400 ? 0x00000002 : 0x00000080 )) getDbgLogger()->setNextMask(fRejectStatus>=400 ? 0x00000002 : 0x00000080).DebugPrintfLastMask ("Item rejected with Status=%hd" ,fRejectStatus); }; | |||
5665 | if (fRejectStatus>0) { | |||
5666 | // rejected with status code (not necessarily error) | |||
5667 | aStatusCommand.setStatusCode(fRejectStatus); | |||
5668 | if (fRejectStatus>=300) { | |||
5669 | // non 200-codes are errors | |||
5670 | ADDDEBUGITEM(aStatusCommand,"Item rejected"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Item rejected"); }; | |||
5671 | return false; | |||
5672 | } | |||
5673 | } | |||
5674 | // silently rejected | |||
5675 | return true; | |||
5676 | } | |||
5677 | // now perform requested operation | |||
5678 | localstatus sta; | |||
5679 | switch (syncop) { | |||
5680 | readonly_delete: | |||
5681 | // read-only handling of delete is like soft delete: remove map entry, but nothing else | |||
5682 | PDEBUGPRINTFX(DBG_DATA,("Read-Only Datastore: Prevented actual deletion, just removing map entry")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only Datastore: Prevented actual deletion, just removing map entry" ); }; | |||
5683 | case sop_soft_delete: | |||
5684 | // Readonly: allowed, as only map is touched | |||
5685 | // soft delete from client is treated as an indication that the item was | |||
5686 | // removed from the client's datastore, but is still in the set | |||
5687 | // of sync data for that client. | |||
5688 | // This means that the map item must be removed. | |||
5689 | // - when the item is hard-deleted on the server, nothing will happen at next sync | |||
5690 | // - when the item is modified on the server, it will be re-added to the client at next sync | |||
5691 | // - when slow sync is performed, the item will be re-added, too. | |||
5692 | // %%%%% Note that this does NOT work as it is now, as adds also occur for non-modified | |||
5693 | // items that have no map AND are visible under current targetFilter. | |||
5694 | // probably we should use a map entry with no remoteID for soft-deleted items later.... | |||
5695 | // Delete Map entry by remote ID | |||
5696 | aSyncItemP->clearLocalID(); // none | |||
5697 | sta=engProcessMap(aSyncItemP->getRemoteID(),NULL__null); | |||
5698 | ok=sta==LOCERR_OK; | |||
5699 | aStatusCommand.setStatusCode(ok ? 200 : sta); | |||
5700 | break; | |||
5701 | case sop_archive_delete: | |||
5702 | if (fReadOnly) goto readonly_delete; // register removal of item in map, but do nothing to data itself | |||
5703 | #ifdef OBJECT_FILTERING1 | |||
5704 | if (!fDSConfigP->fInvisibleFilter.empty()) { | |||
5705 | // turn into replace with all fields unavailable but made to pass invisible filter | |||
5706 | // - make sure that no data field is assigned | |||
5707 | aSyncItemP->cleardata(); | |||
5708 | // - make item pass "invisible" filter | |||
5709 | if (aSyncItemP->makePassFilter(fDSConfigP->fInvisibleFilter.c_str())) { | |||
5710 | // item now passes invisible rule, that is, it is invisible -> replace in DB | |||
5711 | goto archive_delete; | |||
5712 | } | |||
5713 | } | |||
5714 | // fall trough, no archive delete supported | |||
5715 | #endif | |||
5716 | // No archive delete support if there is no filter to detect/generate invisibles | |||
5717 | // before SyncML 1.1 : we could return 210 here and still process the delete op. | |||
5718 | // SyncML 1.1 : we must return 501 (not implemented) here | |||
5719 | aStatusCommand.setStatusCode(501); | |||
5720 | PDEBUGPRINTFX(DBG_ERROR,("Datastore does not support Archive-Delete, error status = 501")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Datastore does not support Archive-Delete, error status = 501" ); }; | |||
5721 | delete aSyncItemP; | |||
5722 | ok=false; | |||
5723 | break; | |||
5724 | case sop_delete: | |||
5725 | // delete item by LUID | |||
5726 | if (fSlowSync) { | |||
5727 | aStatusCommand.setStatusCode(403); | |||
5728 | ADDDEBUGITEM(aStatusCommand,"Delete during slow sync not allowed"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Delete during slow sync not allowed"); }; | |||
5729 | PDEBUGPRINTFX(DBG_ERROR,("Delete during slow sync not allowed")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Delete during slow sync not allowed" ); }; | |||
5730 | delete aSyncItemP; | |||
5731 | ok=false; | |||
5732 | break; | |||
5733 | } | |||
5734 | // check for conflict with replace from server | |||
5735 | // Note: conflict cases do not change local DB, so they are allowed before checking fReadOnly | |||
5736 | if (!echoItemP) conflictingItemP = getConflictingItemByRemoteID(aSyncItemP); // do not check conflicts if we have already created an echo | |||
5737 | // - check if we must force the conflict | |||
5738 | if (!conflictingItemP && fForceConflict) { | |||
5739 | conflictingItemP=SendDBVersionOfItemAsServer(aSyncItemP); | |||
5740 | } | |||
5741 | if (conflictingItemP) { | |||
5742 | // conflict only if other party has replace | |||
5743 | if (conflictingItemP->getSyncOp()==sop_replace || conflictingItemP->getSyncOp()==sop_wants_replace) { | |||
5744 | if (!fDeleteWins) { | |||
5745 | // act as if successfully deleted and cause re-adding of still existing server item | |||
5746 | // - discard deletion | |||
5747 | delete aSyncItemP; | |||
5748 | // - remove map entry for this item (it no longer exists on the client) | |||
5749 | sta = engProcessMap(NULL__null,conflictingItemP->getLocalID()); | |||
5750 | aStatusCommand.setStatusCode(sta==LOCERR_OK ? 200 : sta); | |||
5751 | // - change replace to add (as to-be-replaced item is already deleted on remote) | |||
5752 | conflictingItemP->setSyncOp(sop_add); | |||
5753 | // - remove remote ID (will be assigned a new ID because the item is now re-added) | |||
5754 | conflictingItemP->setRemoteID(""); | |||
5755 | // - no server operation needed | |||
5756 | PDEBUGPRINTFX(DBG_DATA,("Conflict of Client Delete with Server replace -> discarded delete, re-added server item to client")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Conflict of Client Delete with Server replace -> discarded delete, re-added server item to client" ); }; | |||
5757 | ok=true; | |||
5758 | break; | |||
5759 | } | |||
5760 | else { | |||
5761 | // delete preceedes replace | |||
5762 | // - avoid sending item from server | |||
5763 | dontSendItemAsServer(conflictingItemP); | |||
5764 | // - let delete happen | |||
5765 | } | |||
5766 | } | |||
5767 | // if both have deleted the item, we should remove the map | |||
5768 | // and avoid sending a delete to the client | |||
5769 | else if (conflictingItemP->getSyncOp()==sop_delete) { | |||
5770 | // - discard deletion | |||
5771 | delete aSyncItemP; | |||
5772 | // - remove map entry for this item (it no longer exists) | |||
5773 | sta = engProcessMap(NULL__null,conflictingItemP->getLocalID()); | |||
5774 | aStatusCommand.setStatusCode(sta==LOCERR_OK ? 200 : sta); | |||
5775 | // - make sure delete from server is not sent | |||
5776 | dontSendItemAsServer(conflictingItemP); | |||
5777 | PDEBUGPRINTFX(DBG_DATA,("Client and Server have deleted same item -> just removed map entry")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Client and Server have deleted same item -> just removed map entry" ); }; | |||
5778 | ok=true; | |||
5779 | break; | |||
5780 | } | |||
5781 | } | |||
5782 | // real delete is discarded silently when fReadOnly is set | |||
5783 | if (fReadOnly) goto readonly_delete; // register removal of item in map, but do nothing to data itself | |||
5784 | // really delete | |||
5785 | fLocalItemsDeleted++; | |||
5786 | remainsvisible=false; // deleted not visible any more | |||
5787 | do_delete: | |||
5788 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // delete in local database NOW | |||
5789 | CHECK_FOR_AGAIN(LOCAL_ITEM_DELETE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_DELETE ; goto again; }; | |||
5790 | break; | |||
5791 | case sop_copy: | |||
5792 | if (fReadOnly) { | |||
5793 | delete aSyncItemP; // we don't need it | |||
5794 | aStatusCommand.setStatusCode(200); | |||
5795 | PDEBUGPRINTFX(DBG_DATA,("Read-Only: copy command silently discarded")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only: copy command silently discarded" ); }; | |||
5796 | ok=true; | |||
5797 | break; | |||
5798 | } | |||
5799 | // %%% note: this would belong into specific datastore implementation, but is here | |||
5800 | // now for simplicity as copy isn't used heavily het | |||
5801 | // retrieve data from local datastore | |||
5802 | if (!logicRetrieveItemByID(*aSyncItemP,aStatusCommand)) { ok=false; break; } | |||
5803 | // process like add | |||
5804 | /// @todo %%%%%%%%%%%%%%%% NOTE: MISSING SENDING BACK MAP COMMAND for new GUID created | |||
5805 | goto normal_add; | |||
5806 | case sop_add: | |||
5807 | // test for slow sync | |||
5808 | if (fSlowSync) goto sop_slow_add; // add in slow sync is like replace | |||
5809 | normal_add: | |||
5810 | // add as new item to server DB | |||
5811 | aStatusCommand.setStatusCode(201); // item added (if no error occurs) | |||
5812 | if (fReadOnly) { | |||
5813 | delete aSyncItemP; // we don't need it | |||
5814 | PDEBUGPRINTFX(DBG_DATA,("Read-Only: add command silently discarded")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only: add command silently discarded" ); }; | |||
5815 | ok=true; | |||
5816 | break; | |||
5817 | } | |||
5818 | // check if adds are prevented | |||
5819 | if (!fPreventAdd) { | |||
5820 | // add allowed | |||
5821 | fLocalItemsAdded++; | |||
5822 | #ifdef OBJECT_FILTERING1 | |||
5823 | // test if acceptable | |||
5824 | if (!isAcceptable(aSyncItemP,aStatusCommand)) { ok=false; break; } // cannot be accepted | |||
5825 | // Note: making item to pass sync set filter is implemented in derived DB implementation | |||
5826 | // as criteria for passing might be in data that must first be read from the DB | |||
5827 | #endif | |||
5828 | do_add_normal: | |||
5829 | remainsvisible=true; // should remain visible | |||
5830 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW | |||
5831 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_NORMAL)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_NORMAL ; goto again; }; | |||
5832 | if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
5833 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Added item is not visible under current filters -> remove it on client")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Added item is not visible under current filters -> remove it on client" ); }; | |||
5834 | goto removefromremoteandsyncset; | |||
5835 | } | |||
5836 | break; | |||
5837 | } | |||
5838 | goto preventadd; | |||
5839 | archive_delete: | |||
5840 | sop_slow_add: | |||
5841 | aSyncItemP->setSyncOp(sop_replace); // set correct op | |||
5842 | // ...and process like replace | |||
5843 | case sop_reference_only: | |||
5844 | case sop_replace: | |||
5845 | #ifdef OBJECT_FILTERING1 | |||
5846 | // test if acceptable | |||
5847 | if (!isAcceptable(aSyncItemP,aStatusCommand)) { ok=false; break; } // cannot be accepted | |||
5848 | // Note: making item to pass sync set filter is implemented in derived DB implementation | |||
5849 | // as criteria for passing might be in data that must first be read from the DB | |||
5850 | #endif | |||
5851 | // check for conflict with server side modifications | |||
5852 | if (!fSlowSync) { | |||
5853 | if (!echoItemP) conflictingItemP = getConflictingItemByRemoteID(aSyncItemP); | |||
5854 | // - check if we must force the conflict | |||
5855 | if (!conflictingItemP && fForceConflict) { | |||
5856 | conflictingItemP=SendDBVersionOfItemAsServer(aSyncItemP); | |||
5857 | } | |||
5858 | bool deleteconflict; | |||
5859 | deleteconflict=false; | |||
5860 | if (conflictingItemP) { | |||
5861 | // Note: if there is a conflict, this replace cannot be an | |||
5862 | // implicit add, so we don't need to check for fPreventAdd | |||
5863 | // here. | |||
5864 | // Note: if we are in ignoreUpdate mode, the only conflict resolution | |||
5865 | // possible is unconditional server win | |||
5866 | sInt16 cmpRes; | |||
5867 | cmpRes = SYSYNC_NOT_COMPARABLE-999; | |||
5868 | // assume we can resolve the conflict | |||
5869 | aStatusCommand.setStatusCode(419); // default to server win | |||
5870 | ADDDEBUGITEM(aStatusCommand,"Conflict resolved by server"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Conflict resolved by server"); }; | |||
5871 | PDEBUGPRINTFX(DBG_HOT,({ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5872 | "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s",{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5873 | SyncOpNames[aSyncItemP->getSyncOp()],{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5874 | aSyncItemP->getRemoteID(),{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5875 | SyncOpNames[conflictingItemP->getSyncOp()],{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5876 | conflictingItemP->getLocalID(),{ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5877 | conflictingItemP->getRemoteID(){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); } | |||
5878 | )){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ( "Conflict: Remote <%s> with remoteID=%s <--> Local <%s> with localID=%s, remoteID=%s" , SyncOpNames[aSyncItemP->getSyncOp()], aSyncItemP->getRemoteID (), SyncOpNames[conflictingItemP->getSyncOp()], conflictingItemP ->getLocalID(), conflictingItemP->getRemoteID() ); }; | |||
5879 | // we have a conflict, decide what to do | |||
5880 | TConflictResolution crstrategy; | |||
5881 | if (fReadOnly || fIgnoreUpdate) { | |||
5882 | // server always wins and overwrites modified client version | |||
5883 | PDEBUGPRINTFX(DBG_DATA,("Read-Only or IgnoreUpdate: server always wins")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only or IgnoreUpdate: server always wins" ); }; | |||
5884 | crstrategy=cr_server_wins; | |||
5885 | } | |||
5886 | else if (fCacheData) { | |||
5887 | PDEBUGPRINTFX(DBG_DATA,("Caching data: client always wins")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Caching data: client always wins" ); }; | |||
5888 | crstrategy=cr_client_wins; | |||
5889 | } | |||
5890 | else { | |||
5891 | // two-way | |||
5892 | crstrategy = fItemConflictStrategy; // get conflict strategy pre-set for this item | |||
5893 | if (conflictingItemP->getSyncOp()==sop_delete) { | |||
5894 | // server wants to delete item, client wants to replace | |||
5895 | if (fDSConfigP->fTryUpdateDeleted) { | |||
5896 | // if items are not really deleted, but only made invisible, | |||
5897 | // we can assume we can update the "deleted" item | |||
5898 | // BUT ONLY if the conflict strategy is not "server always wins" | |||
5899 | if (crstrategy==cr_server_wins) { | |||
5900 | PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Conflict of Client Replace with Server delete and strategy is server-wins -> delete from client")){ if (((0x00000010 +0x00000001) & getDbgMask()) == (0x00000010 +0x00000001)) getDbgLogger()->setNextMask(0x00000010 +0x00000001 ).DebugPrintfLastMask ("Conflict of Client Replace with Server delete and strategy is server-wins -> delete from client" ); }; | |||
5901 | aStatusCommand.setStatusCode(419); // server wins, client command ignored | |||
5902 | break; // done | |||
5903 | } | |||
5904 | else { | |||
5905 | PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Conflict of Client Replace with Server delete -> try to update already deleted item (as it might still exist in syncset)")){ if (((0x00000010 +0x00000001) & getDbgMask()) == (0x00000010 +0x00000001)) getDbgLogger()->setNextMask(0x00000010 +0x00000001 ).DebugPrintfLastMask ("Conflict of Client Replace with Server delete -> try to update already deleted item (as it might still exist in syncset)" ); }; | |||
5906 | // apply replace (and in case of !fDeleteWins, possible implicit add) | |||
5907 | fPreventAdd=fDeleteWins; // we want implicit add only if delete cannot win | |||
5908 | do_add_deleted: | |||
5909 | remainsvisible=!fDeleteWins; // we want to see the item in the sync set if delete does not win! | |||
5910 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); | |||
5911 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_DELETED)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_DELETED ; goto again; }; | |||
5912 | } | |||
5913 | if (fDeleteWins) { | |||
5914 | if (!ok) { | |||
5915 | // could not update already deleted item | |||
5916 | PDEBUGPRINTFX(DBG_PROTO,("Could not update already deleted server item (seems to be really deleted, not just invisible)")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Could not update already deleted server item (seems to be really deleted, not just invisible)" ); }; | |||
5917 | aStatusCommand.setStatusCode(419); // server wins, client command ignored | |||
5918 | } | |||
5919 | else { | |||
5920 | // update of invisible item successful, but it will still be deleted from client | |||
5921 | // Note: possibly, the update was apparently successful, but only because an UPDATE with no | |||
5922 | // target does not report an error. So effectively, no update might have happened. | |||
5923 | PDEBUGPRINTFX(DBG_PROTO,("Updated already deleted server item, but delete still wins -> client item will be deleted")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Updated already deleted server item, but delete still wins -> client item will be deleted" ); }; | |||
5924 | fLocalItemsUpdated++; | |||
5925 | aStatusCommand.setStatusCode(200); // client command successful (but same item will still be deleted) | |||
5926 | } | |||
5927 | // nothing more to do, let delete happen on the client (conflictingItemP delete will be sent) | |||
5928 | ok=true; | |||
5929 | } | |||
5930 | else { | |||
5931 | // not fDeleteWins - item failed, updated or implicitly added | |||
5932 | if (ok) { | |||
5933 | // update (or implicit add) successful | |||
5934 | if (aStatusCommand.getStatusCode()==201) { | |||
5935 | PDEBUGPRINTFX(DBG_PROTO,("Client Update wins and has re-added already deleted server item -> prevent delete on client")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Client Update wins and has re-added already deleted server item -> prevent delete on client" ); }; | |||
5936 | fLocalItemsAdded++; | |||
5937 | } | |||
5938 | else { | |||
5939 | PDEBUGPRINTFX(DBG_PROTO,("Client Update wins and has updated still existing server item -> prevent delete on client")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Client Update wins and has updated still existing server item -> prevent delete on client" ); }; | |||
5940 | fLocalItemsUpdated++; | |||
5941 | } | |||
5942 | // and client item wins - prevent sending delete to client | |||
5943 | // - don't send delete to client | |||
5944 | conflictingItemP->setSyncOp(sop_none); // just in case... | |||
5945 | dontSendItemAsServer(conflictingItemP); | |||
5946 | } | |||
5947 | } | |||
5948 | // done | |||
5949 | break; | |||
5950 | } | |||
5951 | else { | |||
5952 | // Normal delete conflict processing (assuming deleted items REALLY deleted) | |||
5953 | if (!fDeleteWins) { | |||
5954 | // - client always wins (replace over delete) | |||
5955 | crstrategy=cr_client_wins; | |||
5956 | deleteconflict=true; // flag condition for processing below | |||
5957 | // - change from replace to add, because item is already deleted in server and must be re-added | |||
5958 | fLocalItemsAdded++; | |||
5959 | aSyncItemP->setSyncOp(sop_add); | |||
5960 | PDEBUGPRINTFX(DBG_PROTO,("Conflict of Client Replace with Server delete -> client wins, client item is re-added to server")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict of Client Replace with Server delete -> client wins, client item is re-added to server" ); }; | |||
5961 | } | |||
5962 | else { | |||
5963 | // delete wins, just discard incoming item | |||
5964 | delete aSyncItemP; | |||
5965 | PDEBUGPRINTFX(DBG_PROTO,("Conflict of Client Replace with Server delete -> DELETEWINS() set -> ignore client replace")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict of Client Replace with Server delete -> DELETEWINS() set -> ignore client replace" ); }; | |||
5966 | ok=true; | |||
5967 | break; | |||
5968 | } | |||
5969 | } | |||
5970 | } | |||
5971 | else { | |||
5972 | // replace from client conflicts with replace from server | |||
5973 | // - compare items for further conflict resolution | |||
5974 | // NOTE: it is serveritem.compareWith(clientitem) | |||
5975 | cmpRes = conflictingItemP->compareWith( | |||
5976 | *aSyncItemP,eqm_conflict,this | |||
5977 | #ifdef SYDEBUG2 | |||
5978 | ,PDEBUGTEST(DBG_CONFLICT)(((0x20000000) & getDbgMask()) == (0x20000000)) // show conflict comparisons in normal sync if conflict details are enabled | |||
5979 | #endif | |||
5980 | ); | |||
5981 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared conflicting items with eqm_conflict: remoteItem %s localItem" , cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">") ); } | |||
5982 | "Compared conflicting items with eqm_conflict: remoteItem %s localItem",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared conflicting items with eqm_conflict: remoteItem %s localItem" , cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">") ); } | |||
5983 | cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">"){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared conflicting items with eqm_conflict: remoteItem %s localItem" , cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">") ); } | |||
5984 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Compared conflicting items with eqm_conflict: remoteItem %s localItem" , cmpRes==0 ? "==" : (cmpRes>0 ? "<" : ">") ); }; | |||
5985 | // see if we can determine newer item | |||
5986 | if (crstrategy==cr_newer_wins) { | |||
5987 | if (cmpRes!=0 && conflictingItemP->sortable(*aSyncItemP)) { | |||
5988 | // newer item wins | |||
5989 | // (comparison was: serveritem.compareWith(clientitem), so | |||
5990 | // cmpRes<0 means that client is newer | |||
5991 | PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved by identifying newer item")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict resolved by identifying newer item" ); }; | |||
5992 | if (cmpRes > 0) | |||
5993 | crstrategy=cr_server_wins; // server has newer item | |||
5994 | else | |||
5995 | crstrategy=cr_client_wins; // client has newer item | |||
5996 | } | |||
5997 | else { | |||
5998 | // newer item cannot be determined, duplicate items | |||
5999 | crstrategy=cr_duplicate; | |||
6000 | } | |||
6001 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); } | |||
6002 | "Newer item %sdetermined: %s",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); } | |||
6003 | crstrategy==cr_duplicate ? "NOT " : "",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); } | |||
6004 | crstrategy==cr_client_wins ? "Client item is newer and wins" :{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); } | |||
6005 | (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different"){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); } | |||
6006 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer ans wins" : "item is duplicated if different" ) ); }; | |||
6007 | } | |||
6008 | } | |||
6009 | // modify strategy based on compare | |||
6010 | if (cmpRes==0 && crstrategy==cr_duplicate) { | |||
6011 | // items are equal by definition of item comparison, | |||
6012 | // but obviously both changed, this means that changes should be | |||
6013 | // mergeable | |||
6014 | // So, by deciding arbitrarily that server has won, we will not loose any data | |||
6015 | crstrategy=cr_server_wins; // does not matter, because merge will be attempted | |||
6016 | PDEBUGPRINTFX(DBG_DATA,("Duplication avoided because items are equal by their own definition, just merge")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Duplication avoided because items are equal by their own definition, just merge" ); }; | |||
6017 | } | |||
6018 | // if adds prevented, we cannot duplicate, let server win | |||
6019 | if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins; | |||
6020 | } // not fReadOnly | |||
6021 | // now apply strategy | |||
6022 | if (crstrategy==cr_duplicate) { | |||
6023 | // add items vice versa | |||
6024 | PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved by duplicating items in both databases")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict resolved by duplicating items in both databases" ); }; | |||
6025 | aStatusCommand.setStatusCode(209); | |||
6026 | fConflictsDuplicated++; | |||
6027 | // - set server item such that it will be added as new item to client DB | |||
6028 | conflictingItemP->setSyncOp(sop_add); | |||
6029 | // - break up mapping between client and server item BEFORE adding to server | |||
6030 | // because else adding of item with already existing remoteID can fail. | |||
6031 | // In addition, item now being sent to client may not have a map before | |||
6032 | // it receives a map command from the client! | |||
6033 | sta = engProcessMap(NULL__null,conflictingItemP->getLocalID()); | |||
6034 | if(sta!=LOCERR_OK) { | |||
6035 | PDEBUGPRINTFX(DBG_ERROR,({ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Problem (status=%hd) removing map entry for LocalID='%s'" , sta, conflictingItemP->getLocalID() ); } | |||
6036 | "Problem (status=%hd) removing map entry for LocalID='%s'",{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Problem (status=%hd) removing map entry for LocalID='%s'" , sta, conflictingItemP->getLocalID() ); } | |||
6037 | sta,{ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Problem (status=%hd) removing map entry for LocalID='%s'" , sta, conflictingItemP->getLocalID() ); } | |||
6038 | conflictingItemP->getLocalID(){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Problem (status=%hd) removing map entry for LocalID='%s'" , sta, conflictingItemP->getLocalID() ); } | |||
6039 | )){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ( "Problem (status=%hd) removing map entry for LocalID='%s'" , sta, conflictingItemP->getLocalID() ); }; | |||
6040 | } | |||
6041 | // - add client item as new item to server DB | |||
6042 | fLocalItemsAdded++; | |||
6043 | aSyncItemP->setSyncOp(sop_add); // set correct op | |||
6044 | do_add_duplicate: | |||
6045 | remainsvisible=true; // should remain visible | |||
6046 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW | |||
6047 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_DUPLICATE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_DUPLICATE ; goto again; }; | |||
6048 | break; | |||
6049 | } | |||
6050 | else if (crstrategy==cr_server_wins) { | |||
6051 | // Note: for fReadOnly, this is always the case! | |||
6052 | // server item wins and is sent to client | |||
6053 | PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved: server item replaces client item")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict resolved: server item replaces client item" ); }; | |||
6054 | aStatusCommand.setStatusCode(419); // server wins, client command ignored | |||
6055 | fConflictsServerWins++; | |||
6056 | // - make sure item is set to replace data in client | |||
6057 | conflictingItemP->setSyncOp(sop_replace); | |||
6058 | // - attempt to merge data from loosing item (accumulating fields) | |||
6059 | if (!fReadOnly) { | |||
6060 | conflictingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this); | |||
6061 | } | |||
6062 | if (fIgnoreUpdate) changedexisting=false; // never try to update existing item | |||
6063 | if (changedexisting) { | |||
6064 | // we have merged something, so server must be updated, too | |||
6065 | // Note: after merge, both items are equal. We check if conflictingitem | |||
6066 | // has changed, but if yes, we write the incoming item. Conflicting item | |||
6067 | // will get sent to client later | |||
6068 | PDEBUGPRINTFX(DBG_DATA,("*** Merged some data from loosing client item into winning server item")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("*** Merged some data from loosing client item into winning server item" ); }; | |||
6069 | // set correct status for conflict resultion by merge | |||
6070 | aStatusCommand.setStatusCode(207); // merged | |||
6071 | // process update in local database | |||
6072 | fLocalItemsUpdated++; | |||
6073 | aSyncItemP->setSyncOp(sop_replace); // update | |||
6074 | do_replace_merged: | |||
6075 | remainsvisible=true; // should remain visible | |||
6076 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // update in local database NOW | |||
6077 | CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE_MERGED)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE_MERGED ; goto again; }; | |||
6078 | break; | |||
6079 | } | |||
6080 | else { | |||
6081 | // - item sent by client has lost and can be deleted now | |||
6082 | // %%% possibly add option here to archive item in some way | |||
6083 | // BUT ONLY IF NOT fReadOnly | |||
6084 | delete aSyncItemP; | |||
6085 | } | |||
6086 | } | |||
6087 | else if (crstrategy==cr_client_wins) { | |||
6088 | // client item wins and is sent to server | |||
6089 | PDEBUGPRINTFX(DBG_PROTO,("Conflict resolved: client item replaces server item")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Conflict resolved: client item replaces server item" ); }; | |||
6090 | aStatusCommand.setStatusCode(208); // client wins | |||
6091 | fConflictsClientWins++; | |||
6092 | // - attempt to merge data from loosing item (accumulating fields) | |||
6093 | if (!fCacheData && !deleteconflict) { | |||
6094 | aSyncItemP->mergeWith(*conflictingItemP,changedincoming,changedexisting,this); | |||
6095 | } | |||
6096 | if (changedincoming) { | |||
6097 | // we have merged something, so client must be updated even if it has won | |||
6098 | // Note: after merge, both items are equal. We check if aSyncItemP | |||
6099 | // has changed, but if yes, we make sure the conflicting item gets | |||
6100 | // sent to the client | |||
6101 | PDEBUGPRINTFX(DBG_DATA,("*** Merged some data from loosing server item into winning client item")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("*** Merged some data from loosing server item into winning client item" ); }; | |||
6102 | conflictingItemP->setSyncOp(sop_replace); // update | |||
6103 | // set correct status for conflict resultion by merge | |||
6104 | aStatusCommand.setStatusCode(207); // merged | |||
6105 | // will be sent because it is in the list | |||
6106 | } | |||
6107 | else { | |||
6108 | // - make sure conflicting item from server is NOT sent to client | |||
6109 | conflictingItemP->setSyncOp(sop_none); // just in case... | |||
6110 | dontSendItemAsServer(conflictingItemP); | |||
6111 | aStatusCommand.setStatusCode(200); // ok | |||
6112 | } | |||
6113 | // - replace item in server (or leave it as is, if conflict was with delete) | |||
6114 | if (!deleteconflict) { | |||
6115 | fLocalItemsUpdated++; | |||
6116 | aSyncItemP->setSyncOp(sop_replace); | |||
6117 | } | |||
6118 | do_replace_from_client: | |||
6119 | remainsvisible=true; // should remain visible | |||
6120 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // replace in local database NOW | |||
6121 | CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE_FROM_CLIENT)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE_FROM_CLIENT ; goto again; }; | |||
6122 | break; | |||
6123 | } | |||
6124 | } // replace conflict | |||
6125 | else { | |||
6126 | // normal replace without any conflict | |||
6127 | if (fReadOnly) { | |||
6128 | delete aSyncItemP; // we don't need it | |||
6129 | aStatusCommand.setStatusCode(200); | |||
6130 | PDEBUGPRINTFX(DBG_DATA,("Read-Only: replace command silently discarded")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only: replace command silently discarded" ); }; | |||
6131 | ok=true; | |||
6132 | break; | |||
6133 | } | |||
6134 | // no conflict, just let client replace server's item | |||
6135 | PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,("No Conflict: client item replaces server item")){ if (((0x00000080 +0x20000000) & getDbgMask()) == (0x00000080 +0x20000000)) getDbgLogger()->setNextMask(0x00000080 +0x20000000 ).DebugPrintfLastMask ("No Conflict: client item replaces server item" ); }; | |||
6136 | // - replace item in server (or add if item does not exist and not fPreventAdd) | |||
6137 | aSyncItemP->setSyncOp(sop_replace); | |||
6138 | do_replace: | |||
6139 | remainsvisible=true; // should remain visible | |||
6140 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); | |||
6141 | CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE ; goto again; }; | |||
6142 | if (!ok) { | |||
6143 | // check if this is a 404 or 410 and fPreventAdd | |||
6144 | if (fPreventAdd && (aStatusCommand.getStatusCode()==404 || aStatusCommand.getStatusCode()==410)) | |||
6145 | goto preventadd2; // to-be-replaced item not found and implicit add prevented -> delete from remote | |||
6146 | // simply failed | |||
6147 | break; | |||
6148 | } | |||
6149 | // still visible in sync set? | |||
6150 | if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
6151 | // -> cause item to be deleted on remote | |||
6152 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Item replaced no longer visible in syncset -> returning delete to remote")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Item replaced no longer visible in syncset -> returning delete to remote" ); }; | |||
6153 | goto removefromremoteandsyncset; | |||
6154 | } | |||
6155 | // processed ok | |||
6156 | if (aStatusCommand.getStatusCode()==201) | |||
6157 | fLocalItemsAdded++; | |||
6158 | else | |||
6159 | fLocalItemsUpdated++; | |||
6160 | ok=true; | |||
6161 | break; | |||
6162 | } | |||
6163 | } // normal sync | |||
6164 | else { | |||
6165 | // slow sync (replaces AND adds are treated the same) | |||
6166 | // - first do a strict search for identical item. This is required to | |||
6167 | // prevent that in case of multiple (loosely compared) matches we | |||
6168 | // catch the wrong item and cause a mess at slowsync | |||
6169 | // NOTE: we do compare only relevant fields (eqm_conflict) | |||
6170 | matchingItemP = getMatchingItem(aSyncItemP,eqm_conflict); // separate assignment, only done as part of normal control flow | |||
6171 | if (!matchingItemP) { | |||
6172 | // try again with less strict comparison (eqm_slowsync or eqm_always for firsttimesync) | |||
6173 | DEBUGPRINTFX(DBG_DATA+DBG_MATCH,("Strict search for matching item failed, try with configured EqMode now")){ if (((0x00000080 +0x10000000) & getDbgMask()) == (0x00000080 +0x10000000)) getDbgLogger()->setNextMask(0x00000080 +0x10000000 ).DebugPrintfLastMask ("Strict search for matching item failed, try with configured EqMode now" ); }; | |||
6174 | matchingItemP = getMatchingItem(aSyncItemP,fFirstTimeSync ? eqm_always : eqm_slowsync); | |||
6175 | } | |||
6176 | if (matchingItemP) { | |||
6177 | // both sides already have this item | |||
6178 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,({ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Slow Sync Match detected - localID='%s' matches incoming item" , matchingItemP->getLocalID() ); } | |||
6179 | "Slow Sync Match detected - localID='%s' matches incoming item",{ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Slow Sync Match detected - localID='%s' matches incoming item" , matchingItemP->getLocalID() ); } | |||
6180 | matchingItemP->getLocalID(){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Slow Sync Match detected - localID='%s' matches incoming item" , matchingItemP->getLocalID() ); } | |||
6181 | )){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ( "Slow Sync Match detected - localID='%s' matches incoming item" , matchingItemP->getLocalID() ); }; | |||
6182 | fSlowSyncMatches++; | |||
6183 | aStatusCommand.setStatusCode(syncop==sop_add ? 201 : 200); // default is: simply ok. But if original op was Add, MUST return 201 status (SCTS requires it) | |||
6184 | bool matchingok; | |||
6185 | matchingok = false; | |||
6186 | // - do not update map yet, as we still don't know if client item will | |||
6187 | // possibly be added instead of mapped | |||
6188 | // Note: ONLY in case this is a reference-only item, the map is already updated! | |||
6189 | bool mapupdated; | |||
6190 | mapupdated = syncop==sop_reference_only; | |||
6191 | // - determine which one is winning | |||
6192 | bool needserverupdate; | |||
6193 | bool needclientupdate; | |||
6194 | needserverupdate = false; | |||
6195 | needclientupdate = false; | |||
6196 | // if updates are ignored, we can short-cut here | |||
6197 | // Note: if this is a reference-only item, it was already updated (if needed) before last suspend | |||
6198 | // so skip updating now! | |||
6199 | if (syncop!=sop_reference_only && !fIgnoreUpdate) { | |||
6200 | // Not a reference-only and also updates not suppressed | |||
6201 | // - for a read-only datastore, this defaults to server always winning | |||
6202 | TConflictResolution crstrategy; | |||
6203 | crstrategy = | |||
6204 | fReadOnly ? | |||
6205 | cr_server_wins : // server always wins for read-only | |||
6206 | fItemConflictStrategy; // pre-set strategy for this item | |||
6207 | // Determine what data to use | |||
6208 | if (crstrategy==cr_newer_wins) { | |||
6209 | // use newer | |||
6210 | // Note: comparison is clientitem.compareWith(serveritem) | |||
6211 | // so if result>0, client is newer than server | |||
6212 | sInt16 cmpRes = aSyncItemP->compareWith( | |||
6213 | *matchingItemP,eqm_nocompare,this | |||
6214 | #ifdef SYDEBUG2 | |||
6215 | ,PDEBUGTEST(DBG_CONFLICT+DBG_DETAILS)(((0x20000000 +0x40000000) & getDbgMask()) == (0x20000000 +0x40000000)) // show age comparisons only if we want to see details | |||
6216 | #endif | |||
6217 | ); | |||
6218 | if (cmpRes==1) crstrategy=cr_client_wins; | |||
6219 | else if (cmpRes==-1 || fPreventAdd) crstrategy=cr_server_wins; // server wins if adds prevented | |||
6220 | else crstrategy=cr_duplicate; // fall back to duplication if we can't determine newer item | |||
6221 | PDEBUGPRINTFX(DBG_DATA,({ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); } | |||
6222 | "Newer item %sdetermined: %s",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); } | |||
6223 | crstrategy==cr_duplicate ? "NOT " : "",{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); } | |||
6224 | crstrategy==cr_client_wins ? "Client item is newer and wins" :{ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); } | |||
6225 | (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different"){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); } | |||
6226 | )){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ( "Newer item %sdetermined: %s" , crstrategy==cr_duplicate ? "NOT " : "", crstrategy==cr_client_wins ? "Client item is newer and wins" : (crstrategy==cr_server_wins ? "Server item is newer and wins" : "item is duplicated if different" ) ); }; | |||
6227 | } | |||
6228 | // if adds prevented, we cannot duplicate, let server win | |||
6229 | if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins; | |||
6230 | else if (fCacheData) crstrategy=cr_client_wins; | |||
6231 | // now execute chosen strategy | |||
6232 | if (crstrategy==cr_client_wins) { | |||
6233 | if (fCacheData) { | |||
6234 | // - merge server's data into client item | |||
6235 | PDEBUGPRINTFX(DBG_DATA,("Caching: copying winning client item into loosing server item")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Caching: copying winning client item into loosing server item" ); }; | |||
6236 | aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this, | |||
6237 | TSyncItem::MERGE_OPTION_CHANGE_OTHER); | |||
6238 | } else { | |||
6239 | // - merge server's data into client item | |||
6240 | PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Trying to merge some data from loosing server item into winning client item" ); }; | |||
6241 | aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this); | |||
6242 | // only count if server gets updated | |||
6243 | if (changedexisting) fConflictsClientWins++; | |||
6244 | } | |||
6245 | // Note: changedexisting will cause needserverupdate to be set below | |||
6246 | } | |||
6247 | else if (crstrategy==cr_server_wins) { | |||
6248 | // - merge client data into server item | |||
6249 | PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing client item into winning server item")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Trying to merge some data from loosing client item into winning server item" ); }; | |||
6250 | matchingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this); | |||
6251 | // only count if client gets updated | |||
6252 | if (changedincoming) fConflictsServerWins++; | |||
6253 | // Note: changedincoming will cause needclientupdate to be set below | |||
6254 | } | |||
6255 | else if (crstrategy==cr_duplicate) { | |||
6256 | // test if items are equal enough | |||
6257 | // (Note: for first-sync, compare mode for item matching can be looser | |||
6258 | // (eqm_always), so re-comparison here makes sense) | |||
6259 | if ( | |||
6260 | matchingItemP->compareWith( | |||
6261 | *aSyncItemP,eqm_slowsync,this | |||
6262 | #ifdef SYDEBUG2 | |||
6263 | ,PDEBUGTEST(DBG_CONFLICT+DBG_DETAILS)(((0x20000000 +0x40000000) & getDbgMask()) == (0x20000000 +0x40000000)) // show equality re-test only for details enabled | |||
6264 | #endif | |||
6265 | )!=0 | |||
6266 | ) { | |||
6267 | // items are not really equal in content, so duplicate them on both sides | |||
6268 | PDEBUGPRINTFX(DBG_PROTO,("Matching items are not fully equal, duplicate them on both sides")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Matching items are not fully equal, duplicate them on both sides" ); }; | |||
6269 | fConflictsDuplicated++; | |||
6270 | // - duplicates contain merged data | |||
6271 | matchingItemP->mergeWith(*aSyncItemP,changedexisting,changedincoming,this); | |||
6272 | // - add client item (with server data merged) as new item to server | |||
6273 | fLocalItemsAdded++; | |||
6274 | aSyncItemP->setSyncOp(sop_add); | |||
6275 | aStatusCommand.setStatusCode(201); // item added (if no error occurs) | |||
6276 | do_add_merged: | |||
6277 | string guid; | |||
6278 | remainsvisible=true; // should remain visible | |||
6279 | matchingok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&guid); // add item in local database NOW | |||
6280 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_MERGED)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_MERGED ; goto again; }; | |||
6281 | aSyncItemP=NULL__null; // is already deleted! | |||
6282 | if (matchingok) { // do it only if server add successful, because otherwise we don't have a GUID | |||
6283 | // - make sure same item is ADDED as new item to client | |||
6284 | matchingItemP->setSyncOp(sop_add); // add it, prevent it from re-match (is sop_wants_add now) | |||
6285 | matchingItemP->setLocalID(guid.c_str()); // this is now a pair with the newly added item (not the original one) | |||
6286 | matchingItemP->setRemoteID(""); // we don't know the remote ID yet | |||
6287 | } | |||
6288 | // adding duplicate to server (add) is already done | |||
6289 | changedexisting=false; | |||
6290 | changedincoming=false; | |||
6291 | mapupdated=true; // no need to update map | |||
6292 | needserverupdate=false; // already done | |||
6293 | // client must received the (updated) server item as an add (set above) | |||
6294 | needclientupdate=true; | |||
6295 | } | |||
6296 | } | |||
6297 | } // if not ignoreUpdate | |||
6298 | // Update server map now if required | |||
6299 | // - NOTE THAT THIS IS VERY IMPORTANT TO DO BEFORE any possible | |||
6300 | // replaces, because replacing the matchingItem can only be | |||
6301 | // done via its remoteID, which is, at this moment, probably not | |||
6302 | // valid. After Mapping, it is ensured that the mapped remoteID | |||
6303 | // uniquely identifies the matchingItem. | |||
6304 | if (!mapupdated) { | |||
6305 | // - update map in server | |||
6306 | sta = engProcessMap( | |||
6307 | aSyncItemP->getRemoteID(), // remote ID (LUID) of item received from client | |||
6308 | matchingItemP->getLocalID() // local ID (GUID) of item already stored in server | |||
6309 | ); | |||
6310 | matchingok = sta==LOCERR_OK; | |||
6311 | if (!matchingok) { | |||
6312 | // failed | |||
6313 | ok=false; | |||
6314 | aStatusCommand.setStatusCode(sta); | |||
6315 | break; | |||
6316 | } | |||
6317 | } | |||
6318 | // Now prepare updates of (already mapped) server and client items if needed | |||
6319 | if (changedexisting) { | |||
6320 | // matched item in server's sync set was changed and must be update in server DB | |||
6321 | // update server only if updates during non-first-time slowsync are enabled | |||
6322 | if (fFirstTimeSync || fSessionP->fUpdateServerDuringSlowsync) { | |||
6323 | needserverupdate=true; // note: ineffective if fReadOnly | |||
6324 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Server data was updated by record sent by client, REPLACE it in server DB")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Server data was updated by record sent by client, REPLACE it in server DB" ); }; | |||
6325 | } | |||
6326 | else { | |||
6327 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Server data was updated by record sent by client, but server updates during non-firsttime slowsyncs are disabled")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Server data was updated by record sent by client, but server updates during non-firsttime slowsyncs are disabled" ); }; | |||
6328 | } | |||
6329 | } | |||
6330 | if (changedincoming) { | |||
6331 | // incoming item from remote was changed after receiving and must be reflected | |||
6332 | // back to client | |||
6333 | // NOTE: this can also happen with sop_reference_only items | |||
6334 | // - client will be updated because matchingItemP is still in list | |||
6335 | matchingItemP->setSyncOp(sop_replace); // cancel wants_add state, prevent re-match | |||
6336 | // - matchingItem was retrieved BEFORE map was done, and has no valid remote ID | |||
6337 | // so remote ID must be added now. | |||
6338 | matchingItemP->setRemoteID(aSyncItemP->getRemoteID()); | |||
6339 | // update client only if updates during non-first-time slowsync are enabled | |||
6340 | if (fFirstTimeSync || fSessionP->fUpdateClientDuringSlowsync) { | |||
6341 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Record sent by client was updated with server data, client will receive a REPLACE")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Record sent by client was updated with server data, client will receive a REPLACE" ); }; | |||
6342 | needclientupdate=true; | |||
6343 | } | |||
6344 | else { | |||
6345 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Record sent by client was updated, but client updates during non-firsttime slowsyncs are disabled")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Record sent by client was updated, but client updates during non-firsttime slowsyncs are disabled" ); }; | |||
6346 | } | |||
6347 | } | |||
6348 | // update | |||
6349 | // - check if client must be updated | |||
6350 | if (!needclientupdate) { | |||
6351 | // prevent updating client | |||
6352 | // - make sure matching item from server is NOT sent to client | |||
6353 | matchingItemP->setSyncOp(sop_none); // just in case... | |||
6354 | dontSendItemAsServer(matchingItemP); | |||
6355 | } | |||
6356 | else { | |||
6357 | // check if replaces may be sent to client during slowsync | |||
6358 | if (matchingItemP->getSyncOp()==sop_replace && fSessionP->fNoReplaceInSlowsync) { | |||
6359 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Update of client item suppressed because of <noreplaceinslowsync>")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Update of client item suppressed because of <noreplaceinslowsync>" ); }; | |||
6360 | matchingItemP->setSyncOp(sop_none); // just in case... | |||
6361 | dontSendItemAsServer(matchingItemP); | |||
6362 | } | |||
6363 | else { | |||
6364 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Update of client item enabled (will be sent later)")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Update of client item enabled (will be sent later)" ); }; | |||
6365 | } | |||
6366 | } | |||
6367 | // - check if server must be updated (NEVER for fReadOnly) | |||
6368 | if (!fReadOnly && needserverupdate && aSyncItemP) { | |||
6369 | // update server | |||
6370 | // - update server side (NOTE: processItemAsServer takes ownership, pointer gets invalid!) | |||
6371 | fLocalItemsUpdated++; | |||
6372 | aSyncItemP->setSyncOp(sop_replace); | |||
6373 | do_replace_merged2: | |||
6374 | remainsvisible=true; // should remain visible | |||
6375 | matchingok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // replace item in local database NOW | |||
6376 | CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE_MERGED2)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE_MERGED2 ; goto again; }; | |||
6377 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Updated server item")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Updated server item"); }; | |||
6378 | } | |||
6379 | else { | |||
6380 | // - delete incoming item unprocessed (if not already deleted) | |||
6381 | if (aSyncItemP) delete aSyncItemP; | |||
6382 | } | |||
6383 | // check if we need to actively delete item from the client as it falls out of the filter | |||
6384 | if (!remainsvisible && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) { | |||
6385 | // -> cause item to be deleted on remote | |||
6386 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("Slow sync matched item no longer visible in syncset -> returning delete to remote")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("Slow sync matched item no longer visible in syncset -> returning delete to remote" ); }; | |||
6387 | goto removefromremoteandsyncset; | |||
6388 | } | |||
6389 | ok=matchingok; | |||
6390 | break; | |||
6391 | } | |||
6392 | else { | |||
6393 | PDEBUGPRINTFX(DBG_DATA+DBG_HOT,("No matching item found - add it to local database")){ if (((0x00000080 +0x00000001) & getDbgMask()) == (0x00000080 +0x00000001)) getDbgLogger()->setNextMask(0x00000080 +0x00000001 ).DebugPrintfLastMask ("No matching item found - add it to local database" ); }; | |||
6394 | // this item is not yet on the server, add it normally | |||
6395 | aStatusCommand.setStatusCode(201); // item added (if no error occurs) | |||
6396 | if (fReadOnly) { | |||
6397 | delete aSyncItemP; // we don't need it | |||
6398 | PDEBUGPRINTFX(DBG_DATA,("Read-Only: slow-sync add/replace command silently discarded")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Read-Only: slow-sync add/replace command silently discarded" ); }; | |||
6399 | ok=true; | |||
6400 | break; | |||
6401 | } | |||
6402 | if (fPreventAdd) goto preventadd; | |||
6403 | fLocalItemsAdded++; | |||
6404 | aSyncItemP->setSyncOp(sop_add); // set correct op | |||
6405 | do_add_slow: | |||
6406 | remainsvisible=true; // should remain visible | |||
6407 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // add to local database NOW | |||
6408 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_SLOW)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_SLOW ; goto again; }; | |||
6409 | break; | |||
6410 | } | |||
6411 | } // slow sync | |||
6412 | break; // just in case.... | |||
6413 | preventadd: | |||
6414 | // add not allowed, delete remote item instead | |||
6415 | // - consume original | |||
6416 | delete aSyncItemP; | |||
6417 | preventadd2: | |||
6418 | aStatusCommand.setStatusCode(201); // pretend adding item was successful | |||
6419 | PDEBUGPRINTFX(DBG_DATA,("Prevented explicit add, returning delete to remote")){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("Prevented explicit add, returning delete to remote" ); }; | |||
6420 | ok=true; | |||
6421 | goto removefromremote; // as we have PREVENTED adding the item, it is not in the map | |||
6422 | removefromremoteandsyncset: | |||
6423 | // remove item from local map first | |||
6424 | engProcessMap(remoteid.c_str(),NULL__null); | |||
6425 | removefromremote: | |||
6426 | // have item removed from remote | |||
6427 | delitemP = newItemForRemote(itemtypeid); | |||
6428 | delitemP->setRemoteID(remoteid.c_str()); | |||
6429 | delitemP->setSyncOp(sop_delete); | |||
6430 | SendItemAsServer(delitemP); // pass ownership | |||
6431 | break; | |||
6432 | default : | |||
6433 | SYSYNC_THROW(TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsServer"))throw TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsServer" ); | |||
6434 | } // switch | |||
6435 | if (ok) { | |||
6436 | DB_PROGRESS_EVENT(this,pev_itemprocessed,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted)this->getSession()->NotifySessionProgressEvent(pev_itemprocessed ,this->getDSConfig(),fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted ); | |||
6437 | } | |||
6438 | else { | |||
6439 | // if the DB has a error string to show, add it here | |||
6440 | aStatusCommand.addItemString(lastDBErrorText().c_str()); | |||
6441 | } | |||
6442 | // done | |||
6443 | return ok; | |||
6444 | } // TLocalEngineDS::engProcessRemoteItemAsServer | |||
6445 | ||||
6446 | ||||
6447 | /// @brief called at end of request processing, should be used to save suspend state | |||
6448 | /// @note superdatastore does it itself to have correct order of things happening | |||
6449 | void TLocalEngineDS::engRequestEnded(void) | |||
6450 | { | |||
6451 | #ifdef SUPERDATASTORES1 | |||
6452 | if (fAsSubDatastoreOf) | |||
6453 | return; | |||
6454 | #endif | |||
6455 | // If DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend) | |||
6456 | // before the next request. Note that the we cannot wait for session timeout, as the resume attempt | |||
6457 | // from the client probably arrives much earlier. | |||
6458 | // Note: It is ESSENTIAL not to save the state until sync set is ready, because saving state will | |||
6459 | // cause DB access, and DB access is not permitted while sync set is possibly still loading | |||
6460 | // (possibly in a separate thread!). So dssta_syncmodestable (as in <=3.0.0.2) is NOT enough here! | |||
6461 | if (testState(dssta_syncsetready)) { | |||
6462 | // make sure all unsent items are marked for resume | |||
6463 | engSaveSuspendState(false); // only if not already aborted | |||
6464 | } | |||
6465 | // let datastore prepare for end of request | |||
6466 | dsRequestEnded(); | |||
6467 | // and let it prepare for end of this thread as well | |||
6468 | dsThreadMayChangeNow(); | |||
6469 | } // TLocalEngineDS::engRequestEnded | |||
6470 | ||||
6471 | #endif // SYSYNC_SERVER | |||
6472 | ||||
6473 | ||||
6474 | ||||
6475 | #ifdef SYSYNC_CLIENT1 | |||
6476 | ||||
6477 | // Client Case | |||
6478 | // =========== | |||
6479 | ||||
6480 | ||||
6481 | // process sync operation from server with specified sync item | |||
6482 | // (according to current sync mode of local datastore) | |||
6483 | // - returns true (and unmodified or non-200-successful status) if | |||
6484 | // operation could be processed regularily | |||
6485 | // - returns false (but probably still successful status) if | |||
6486 | // operation was processed with internal irregularities, such as | |||
6487 | // trying to delete non-existant item in datastore with | |||
6488 | // incomplete Rollbacks (which returns status 200 in this case!). | |||
6489 | bool TLocalEngineDS::engProcessRemoteItemAsClient( | |||
6490 | TSyncItem *aSyncItemP, | |||
6491 | TStatusCommand &aStatusCommand // status, must be set to correct status code (ok / error) | |||
6492 | ) | |||
6493 | { | |||
6494 | string remoteid,localid; | |||
6495 | bool ok; | |||
6496 | bool remainsvisible; | |||
6497 | ||||
6498 | // send event and check for user abort | |||
6499 | #ifdef PROGRESS_EVENTS1 | |||
6500 | if (!DB_PROGRESS_EVENT(this,pev_itemreceived,++fItemsReceived,fRemoteNumberOfChanges,0)this->getSession()->NotifySessionProgressEvent(pev_itemreceived ,this->getDSConfig(),++fItemsReceived,fRemoteNumberOfChanges ,0)) { | |||
6501 | fSessionP->AbortSession(500,true,LOCERR_USERABORT); // this also causes datastore to be aborted | |||
6502 | } | |||
6503 | if (!SESSION_PROGRESS_EVENT(fSessionP,pev_suspendcheck,NULL,0,0,0)fSessionP->NotifySessionProgressEvent(pev_suspendcheck,__null ,0,0,0)) { | |||
6504 | fSessionP->SuspendSession(LOCERR_USERSUSPEND); | |||
6505 | } | |||
6506 | #endif | |||
6507 | // check if datastore is aborted | |||
6508 | if (CheckAborted(aStatusCommand)) return false; | |||
6509 | // init behaviour vars (these are normally used by server only, | |||
6510 | // but must be initialized correctly for client as well as descendants might test them | |||
6511 | fPreventAdd = false; | |||
6512 | fIgnoreUpdate = false; | |||
6513 | ||||
6514 | TSyncOperation syncop; | |||
6515 | LocalItemOp op; | |||
6516 | ||||
6517 | TLocalSyncItemAux *aux = static_cast<TLocalSyncItemAux *>(aSyncItemP->getAux(TSyncItem::LOCAL_ENGINE)); | |||
6518 | if (aux) { | |||
6519 | // Resuming the function call: restore variables, jump to store | |||
6520 | // method call. | |||
6521 | remainsvisible = aux->fRemainsVisible; | |||
6522 | syncop = aux->fSyncOp; | |||
6523 | remoteid = aux->fRemoteID; | |||
6524 | op = aux->fOp; | |||
6525 | ||||
6526 | fCurrentSyncOp = aux->fCurrentSyncOp; | |||
6527 | fEchoItemOp = aux->fEchoItemOp; | |||
6528 | fItemConflictStrategy = aux->fItemConflictStrategy; | |||
6529 | fForceConflict = aux->fForceConflict; | |||
6530 | fDeleteWins = aux->fDeleteWins; | |||
6531 | fRejectStatus = aux->fRejectStatus; | |||
6532 | ||||
6533 | PDEBUGPRINTFX(DBG_DATA,("%s item operation resumed",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation resumed" ,SyncOpNames[syncop]); }; | |||
6534 | switch (op) { | |||
6535 | case LOCAL_ITEM_DELETE: goto do_delete; | |||
6536 | case LOCAL_ITEM_ADD_NORMAL: goto do_add; | |||
6537 | case LOCAL_ITEM_REPLACE: goto do_replace; | |||
6538 | ||||
6539 | // Not used in client: | |||
6540 | case LOCAL_ITEM_ADD_DELETED: | |||
6541 | case LOCAL_ITEM_ADD_DUPLICATE: | |||
6542 | case LOCAL_ITEM_REPLACE_MERGED: | |||
6543 | case LOCAL_ITEM_REPLACE_FROM_CLIENT: | |||
6544 | case LOCAL_ITEM_ADD_MERGED: | |||
6545 | case LOCAL_ITEM_REPLACE_MERGED2: | |||
6546 | case LOCAL_ITEM_ADD_SLOW: | |||
6547 | break; | |||
6548 | }; | |||
6549 | } | |||
6550 | if (false) { | |||
6551 | // Prepare for resuming the function call. Will only be reached | |||
6552 | // via goto with "op" set to something identifying the source of | |||
6553 | // the jump. | |||
6554 | again: | |||
6555 | if (!aux) { | |||
6556 | aux = new TLocalSyncItemAux; | |||
6557 | aSyncItemP->setAux(TSyncItem::LOCAL_ENGINE, aux); | |||
6558 | } | |||
6559 | ||||
6560 | // cppcheck-suppress uninitvar | |||
6561 | aux->fRemainsVisible = remainsvisible; | |||
6562 | // cppcheck-suppress uninitvar | |||
6563 | aux->fSyncOp = syncop; | |||
6564 | aux->fRemoteID = remoteid; | |||
6565 | // cppcheck-suppress uninitvar | |||
6566 | aux->fOp = op; | |||
6567 | ||||
6568 | aux->fCurrentSyncOp = fCurrentSyncOp; | |||
6569 | aux->fEchoItemOp = fEchoItemOp; | |||
6570 | aux->fItemConflictStrategy = fItemConflictStrategy; | |||
6571 | aux->fForceConflict = fForceConflict; | |||
6572 | aux->fDeleteWins = fDeleteWins; | |||
6573 | aux->fRejectStatus = fRejectStatus; | |||
6574 | ||||
6575 | aStatusCommand.setStatusCode(LOCERR_AGAIN); | |||
6576 | return false; | |||
6577 | } | |||
6578 | ||||
6579 | // get operation out of item | |||
6580 | syncop=aSyncItemP->getSyncOp(); | |||
6581 | // show | |||
6582 | DEBUGPRINTFX(DBG_DATA,("%s item operation received",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger ()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation received" ,SyncOpNames[syncop]); }; | |||
6583 | // check if receiving commands is allowed at all | |||
6584 | // - must be in correct sync state | |||
6585 | if (!testState(dssta_syncgendone)) { | |||
6586 | // Modifications from server not allowed before client has done sync gen | |||
6587 | // %%% we could possibly relax this one, depending on the DB | |||
6588 | aStatusCommand.setStatusCode(403); | |||
6589 | PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed before client has sent entire <sync>")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Server command not allowed before client has sent entire <sync>" ); }; | |||
6590 | delete aSyncItemP; | |||
6591 | return false; | |||
6592 | } | |||
6593 | // - must not be one-way | |||
6594 | if (fSyncMode==smo_fromclient) { | |||
6595 | // Modifications from server not allowed during update from client only | |||
6596 | aStatusCommand.setStatusCode(403); | |||
6597 | ADDDEBUGITEM(aStatusCommand,"Server command not allowed in one-way/refresh from client"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Server command not allowed in one-way/refresh from client" ); }; | |||
6598 | PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed in one-way/refresh from client")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Server command not allowed in one-way/refresh from client" ); }; | |||
6599 | delete aSyncItemP; | |||
6600 | return false; | |||
6601 | } | |||
6602 | else { | |||
6603 | // silently discard all modifications if readonly | |||
6604 | if (fReadOnly) { | |||
6605 | PDEBUGPRINTFX(DBG_ERROR,("Read-Only: silently discarding all modifications")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Read-Only: silently discarding all modifications" ); }; | |||
6606 | aStatusCommand.setStatusCode(200); // always ok (but never "added"), so server cannot expect Map | |||
6607 | delete aSyncItemP; | |||
6608 | return true; | |||
6609 | } | |||
6610 | // let item check itself to catch special cases | |||
6611 | // - init variables which are used/modified by item checking | |||
6612 | fItemSizeLimit=-1; // no limit | |||
6613 | fCurrentSyncOp = syncop; | |||
6614 | fEchoItemOp = sop_none; | |||
6615 | fItemConflictStrategy=fSessionConflictStrategy; // get default strategy for this item | |||
6616 | fForceConflict = false; | |||
6617 | fDeleteWins = fDSConfigP->fDeleteWins; // default to configured value | |||
6618 | fRejectStatus = -1; // no rejection | |||
6619 | // - now check | |||
6620 | // check reads and possibly modifies: | |||
6621 | // - fEchoItemOp : if not sop_none, the incoming item is echoed back to the remote with the specified syncop | |||
6622 | // - fItemConflictStrategy : might be changed from the pre-set datastore default | |||
6623 | // - fForceConflict : if set, a conflict is forced by adding the corresponding local item (if any) to the list of items to be sent | |||
6624 | // - fDeleteWins : if set, in a replace/delete conflict delete will win (regardless of strategy) | |||
6625 | // - fPreventAdd : if set, attempt to add item from remote (even implicitly trough replace) will cause no add but delete of remote item | |||
6626 | // - fIgnoreUpdate : if set, attempt to update item from remote will be ignored, only adds (also implicit ones) are executed | |||
6627 | // - fRejectStatus : if set>=0, incoming item is irgnored silently(==0) or with error(!=0) | |||
6628 | aSyncItemP->checkItem(this); | |||
6629 | // - check if incoming item should be processed at all | |||
6630 | if (fRejectStatus>=0) { | |||
6631 | // now discard the incoming item | |||
6632 | delete aSyncItemP; | |||
6633 | PDEBUGPRINTFX(fRejectStatus>=400 ? DBG_ERROR : DBG_DATA,("Item rejected with Status=%hd",fRejectStatus)){ if (((fRejectStatus>=400 ? 0x00000002 : 0x00000080) & getDbgMask()) == (fRejectStatus>=400 ? 0x00000002 : 0x00000080 )) getDbgLogger()->setNextMask(fRejectStatus>=400 ? 0x00000002 : 0x00000080).DebugPrintfLastMask ("Item rejected with Status=%hd" ,fRejectStatus); }; | |||
6634 | if (fRejectStatus>0) { | |||
6635 | // rejected with status code (not necessarily error) | |||
6636 | aStatusCommand.setStatusCode(fRejectStatus); | |||
6637 | if (fRejectStatus>=300) { | |||
6638 | // non 200-codes are errors | |||
6639 | ADDDEBUGITEM(aStatusCommand,"Item rejected"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand .addItemString("Item rejected"); }; | |||
6640 | return false; | |||
6641 | } | |||
6642 | } | |||
6643 | // silently rejected | |||
6644 | return true; | |||
6645 | } | |||
6646 | // now perform requested operation | |||
6647 | switch (syncop) { | |||
6648 | case sop_soft_delete: | |||
6649 | case sop_archive_delete: | |||
6650 | case sop_delete: | |||
6651 | // delete item | |||
6652 | fLocalItemsDeleted++; | |||
6653 | do_delete: | |||
6654 | remainsvisible=false; // deleted not visible any more | |||
6655 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // delete in local database NOW | |||
6656 | CHECK_FOR_AGAIN(LOCAL_ITEM_DELETE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_DELETE ; goto again; }; | |||
6657 | break; | |||
6658 | case sop_copy: | |||
6659 | // %%% note: this would belong into specific datastore implementation, but is here | |||
6660 | // now for simplicity as copy isn't used heavily het | |||
6661 | // retrieve data from local datastore | |||
6662 | if (!logicRetrieveItemByID(*aSyncItemP,aStatusCommand)) { | |||
6663 | ok=false; | |||
6664 | break; | |||
6665 | } | |||
6666 | // process like add | |||
6667 | case sop_add: | |||
6668 | // add as new item to client DB | |||
6669 | aStatusCommand.setStatusCode(201); // item added (if no error occurs) | |||
6670 | fLocalItemsAdded++; | |||
6671 | // add to local datastore | |||
6672 | // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!) | |||
6673 | remoteid=aSyncItemP->getRemoteID(); // get remote ID | |||
6674 | // check if this remoteid is already known from last session - if so, this means that | |||
6675 | // this add was re-sent and must NOT be executed | |||
6676 | if (isAddFromLastSession(remoteid.c_str())) { | |||
6677 | aStatusCommand.setStatusCode(418); // already exists | |||
6678 | PDEBUGPRINTFX(DBG_ERROR,("Warning: Item with same server-side ID (GUID) was already added in previous session - ignore add now")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: Item with same server-side ID (GUID) was already added in previous session - ignore add now" ); }; | |||
6679 | delete aSyncItemP; // forget item | |||
6680 | ok=false; | |||
6681 | break; | |||
6682 | } | |||
6683 | #ifdef OBJECT_FILTERING1 | |||
6684 | if (!isAcceptable(aSyncItemP,aStatusCommand)) { | |||
6685 | delete aSyncItemP; // forget item | |||
6686 | ok=false; // cannot be accepted | |||
6687 | break; | |||
6688 | } | |||
6689 | #endif | |||
6690 | do_add: | |||
6691 | remainsvisible=true; // should remain visible | |||
6692 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // add to local database NOW, get back local GUID | |||
6693 | CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_NORMAL)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_NORMAL ; goto again; }; | |||
6694 | if (!ok) break; | |||
6695 | // if added (not replaced), we need to send map | |||
6696 | if (aStatusCommand.getStatusCode()==201) { | |||
6697 | // really added: remember map entry | |||
6698 | DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,({ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6699 | "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6700 | localid.c_str(),{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6701 | remoteid.c_str(){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6702 | )){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); }; | |||
6703 | fPendingAddMaps[localid]=remoteid; | |||
6704 | } | |||
6705 | ok=true; // fine | |||
6706 | break; | |||
6707 | case sop_replace: | |||
6708 | // - replace item in client | |||
6709 | fLocalItemsUpdated++; | |||
6710 | aSyncItemP->setSyncOp(sop_replace); | |||
6711 | #ifdef OBJECT_FILTERING1 | |||
6712 | if (!isAcceptable(aSyncItemP,aStatusCommand)) { | |||
6713 | ok=false; // cannot be accepted | |||
6714 | break; | |||
6715 | } | |||
6716 | #endif | |||
6717 | // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!), | |||
6718 | // in case replace is converted to add and we need to register a map entry. | |||
6719 | remoteid=aSyncItemP->getRemoteID(); // get remote ID | |||
6720 | do_replace: | |||
6721 | remainsvisible=true; // should remain visible | |||
6722 | ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // replace in local database NOW | |||
6723 | CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE ; goto again; }; | |||
6724 | // if added (not replaced), we need to send map | |||
6725 | if (aStatusCommand.getStatusCode()==201) { | |||
6726 | // Note: logicProcessRemoteItem should NOT do an add if we have no remoteid, but return 404. | |||
6727 | // The following check is just additional security. | |||
6728 | // really added: remember map entry if server sent remoteID (normally, it won't for Replace) | |||
6729 | if (!remoteid.empty()) { | |||
6730 | // we can handle that, as remote sent us the remoteID we need to map it correctly | |||
6731 | DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,({ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6732 | "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6733 | localid.c_str(),{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6734 | remoteid.c_str(){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); } | |||
6735 | )){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040 +0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000 ).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'" , localid.c_str(), remoteid.c_str() ); }; | |||
6736 | fPendingAddMaps[localid]=remoteid; | |||
6737 | } | |||
6738 | else { | |||
6739 | // we cannot handle this (we shouldn't have, in logicProcessRemoteItem!!) | |||
6740 | aStatusCommand.setStatusCode(510); | |||
6741 | ok=false; | |||
6742 | } | |||
6743 | } | |||
6744 | break; // fine, ok = irregularity status | |||
6745 | default: | |||
6746 | SYSYNC_THROW(TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsClient"))throw TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsClient" ); | |||
6747 | } // switch | |||
6748 | // processed | |||
6749 | if (ok) { | |||
6750 | DB_PROGRESS_EVENT(this,pev_itemprocessed,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted)this->getSession()->NotifySessionProgressEvent(pev_itemprocessed ,this->getDSConfig(),fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted ); | |||
6751 | } | |||
6752 | else { | |||
6753 | // if the DB has a error string to show, add it here | |||
6754 | aStatusCommand.addItemString(lastDBErrorText().c_str()); | |||
6755 | } | |||
6756 | return ok; | |||
6757 | } | |||
6758 | } // TLocalEngineDS::processRemoteItemAsClient | |||
6759 | ||||
6760 | ||||
6761 | ||||
6762 | // client case: called whenever outgoing Message of Sync Package starts | |||
6763 | void TLocalEngineDS::engClientStartOfSyncMessage(void) | |||
6764 | { | |||
6765 | // is called for all local datastores, even inactive ones, so check state first | |||
6766 | if (testState(dssta_dataaccessstarted) && !isAborted()) { | |||
6767 | // only alerted non-sub datastores will start a <sync> command here, ONCE | |||
6768 | // if there are pending maps, they will be sent FIRST | |||
6769 | if (fRemoteDatastoreP) { | |||
6770 | if (!testState(dssta_clientsyncgenstarted)) { | |||
6771 | // shift state to syncgen started | |||
6772 | // NOTE: if all sync commands can be sent at once, | |||
6773 | // state will change again by issuing <sync>, so | |||
6774 | // it MUST be changed here (not after issuing!) | |||
6775 | changeState(dssta_clientsyncgenstarted,true); | |||
6776 | // - make sure remotedatastore has correct full name | |||
6777 | fRemoteDatastoreP->setFullName(getRemoteDBPath()); | |||
6778 | // an interrupted command at this point is a map command - continueIssue() will take care | |||
6779 | if (!fSessionP->isInterrupedCmdPending() && numUnsentMaps()>0) { | |||
6780 | // Check for pending maps from previous session (even in DS 1.0..1.1 case this is possible) | |||
6781 | fUnconfirmedMaps.clear(); // no unconfirmed maps left... | |||
6782 | fLastSessionMaps.clear(); // ...and no lastSessionMaps yet: all are in pendingMaps | |||
6783 | // if this is a not-resumed slow sync, pending maps must not be sent, but discarded | |||
6784 | if (isSlowSync() && !isResuming()) { | |||
6785 | // forget the pending maps | |||
6786 | PDEBUGPRINTFX(DBG_HOT,("There are %ld cached map entries from last Session, but this is a non-resumed slow sync: discard them",(long)numUnsentMaps())){ if (((0x00000001) & getDbgMask()) == (0x00000001)) getDbgLogger ()->setNextMask(0x00000001).DebugPrintfLastMask ("There are %ld cached map entries from last Session, but this is a non-resumed slow sync: discard them" ,(long)numUnsentMaps()); }; | |||
6787 | fPendingAddMaps.clear(); | |||
6788 | } | |||
6789 | else { | |||
6790 | // - now sending pending (cached) map commands from previous session | |||
6791 | // Note: if map command was already started, the | |||
6792 | // finished(), continueIssue() mechanism will make sure that | |||
6793 | // more commands are generated. This mechanism will also make | |||
6794 | // sure that outgoing package cannot get <final/> until | |||
6795 | // map is completely sent. | |||
6796 | // Note2: subdatastores do not generate their own map commands, | |||
6797 | // but are called by superdatastore for contributing to united map | |||
6798 | if (!isSubDatastore()) { | |||
6799 | PDEBUGBLOCKFMT(("LastSessionMaps","Now sending cached map entries from last Session","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockExpanded ("LastSessionMaps", "Now sending cached map entries from last Session","datastore=%s" ,getName()); | |||
6800 | TMapCommand *mapcmdP = | |||
6801 | new TMapCommand( | |||
6802 | fSessionP, | |||
6803 | this, // local datastore | |||
6804 | fRemoteDatastoreP // remote datastore | |||
6805 | ); | |||
6806 | // issue | |||
6807 | ISSUE_COMMAND_ROOT(fSessionP,mapcmdP){ TSmlCommand* p=mapcmdP; mapcmdP=__null; fSessionP->issueRootPtr (p); }; | |||
6808 | PDEBUGENDBLOCK("LastSessionMaps")getDbgLogger()->DebugCloseBlock( "LastSessionMaps"); | |||
6809 | } | |||
6810 | } | |||
6811 | } | |||
6812 | // Now, send sync command (unless we are a subdatastore, in this case the superdatastore will take care) | |||
6813 | if (!isSubDatastore()) { | |||
6814 | // Note: if sync command was already started, the | |||
6815 | // finished(), continueIssue() mechanism will make sure that | |||
6816 | // more commands are generated | |||
6817 | // Note2: subdatastores do not generate their own sync commands, | |||
6818 | // but switch to dssta_client/serversyncgenstarted for contributing to united sync command | |||
6819 | TSyncCommand *synccmdP = | |||
6820 | new TSyncCommand( | |||
6821 | fSessionP, | |||
6822 | this, // local datastore | |||
6823 | fRemoteDatastoreP // remote datastore | |||
6824 | ); | |||
6825 | // issue | |||
6826 | ISSUE_COMMAND_ROOT(fSessionP,synccmdP){ TSmlCommand* p=synccmdP; synccmdP=__null; fSessionP->issueRootPtr (p); }; | |||
6827 | } | |||
6828 | } | |||
6829 | } | |||
6830 | else { | |||
6831 | PDEBUGPRINTFX(DBG_ERROR,("engClientStartOfSyncMessage can't start sync phase - missing remotedatastore")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("engClientStartOfSyncMessage can't start sync phase - missing remotedatastore" ); }; | |||
6832 | changeState(dssta_idle,true); // force to idle, nothing happened yet | |||
6833 | } | |||
6834 | } // if dsssta_dataaccessstarted and not aborted | |||
6835 | } // TLocalEngineDS::engClientStartOfSyncMessage | |||
6836 | ||||
6837 | ||||
6838 | // Client only: returns number of unsent map items | |||
6839 | sInt32 TLocalEngineDS::numUnsentMaps(void) | |||
6840 | { | |||
6841 | return fPendingAddMaps.size(); | |||
6842 | } // TLocalEngineDS::numUnsentMaps | |||
6843 | ||||
6844 | ||||
6845 | // Client only: called whenever outgoing Message starts that may contain <Map> commands | |||
6846 | // @param[in] aNotYetInMapPackage set if we are still in sync-from-server package | |||
6847 | // @note usually, client starts sending maps while still receiving syncops from server | |||
6848 | void TLocalEngineDS::engClientStartOfMapMessage(bool aNotYetInMapPackage) | |||
6849 | { | |||
6850 | // is called for all local datastores, even inactive ones, so check state first | |||
6851 | if (testState(dssta_syncgendone) && !isAborted()) { | |||
6852 | // datastores that have finished generating their <sync> | |||
6853 | // now can start a <map> command here, once (if not isInterrupedCmdPending(), that is, not a map already started) | |||
6854 | if (fRemoteDatastoreP) { | |||
6855 | // start a new map command if we don't have any yet and we are not done (dssta_clientmapssent) yet | |||
6856 | if (!fSessionP->isInterrupedCmdPending() && !testState(dssta_clientmapssent)) { | |||
6857 | // check if we should start one (that is, if the list is not empty) | |||
6858 | if (numUnsentMaps()>0) { | |||
6859 | // - now sending map command | |||
6860 | // NOTE: if all map commands can be sent at once, | |||
6861 | // fState will be modified again by issuing <map>, so | |||
6862 | // it MUST be set here (not after issuing!) | |||
6863 | // - data access done only if we are in Map package now | |||
6864 | if (!aNotYetInMapPackage) | |||
6865 | changeState(dssta_dataaccessdone); // usually already set in engEndOfSyncFromRemote(), but does not harm here | |||
6866 | // Note: if map command was already started, the | |||
6867 | // finished(), continueIssue() mechanism will make sure that | |||
6868 | // more commands are generated. This mechanism will also make | |||
6869 | // sure that outgoing package cannot get <final/> until | |||
6870 | // map is completely sent. | |||
6871 | // Note2: subdatastores do not generate their own map commands, | |||
6872 | // but superdatastore calls their generateMapItem for contributing to united map | |||
6873 | if (!isSubDatastore()) { | |||
6874 | TMapCommand *mapcmdP = | |||
6875 | new TMapCommand( | |||
6876 | fSessionP, | |||
6877 | this, // local datastore | |||
6878 | fRemoteDatastoreP // remote datastore | |||
6879 | ); | |||
6880 | // issue | |||
6881 | ISSUE_COMMAND_ROOT(fSessionP,mapcmdP){ TSmlCommand* p=mapcmdP; mapcmdP=__null; fSessionP->issueRootPtr (p); }; | |||
6882 | } | |||
6883 | } | |||
6884 | else { | |||
6885 | // we need no map items now, but if this is still sync-from-server-package, | |||
6886 | // we are not done yet | |||
6887 | if (aNotYetInMapPackage) { | |||
6888 | DEBUGPRINTFX(DBG_PROTO,("No map command need to be generated now, but still in <sync> from server package")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("No map command need to be generated now, but still in <sync> from server package" ); }; | |||
6889 | } | |||
6890 | else { | |||
6891 | // we are done now | |||
6892 | DEBUGPRINTFX(DBG_PROTO,("All map commands sending complete")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("All map commands sending complete" ); }; | |||
6893 | changeState(dssta_clientmapssent); | |||
6894 | } | |||
6895 | } | |||
6896 | } | |||
6897 | } // if fRemoteDataStoreP | |||
6898 | } // if >=dssta_syncgendone | |||
6899 | } // TLocalEngineDS::engClientStartOfMapMessage | |||
6900 | ||||
6901 | ||||
6902 | ||||
6903 | // called to mark maps confirmed, that is, we have received ok status for them | |||
6904 | void TLocalEngineDS::engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID) | |||
6905 | { | |||
6906 | // As this is client-only, we don't need to check for tempGUIDs here. | |||
6907 | // Note: superdatastore has an implementation which dispatches by prefix | |||
6908 | TStringToStringMap::iterator pos=fUnconfirmedMaps.find(aLocalID); | |||
6909 | if (pos!=fUnconfirmedMaps.end()) { | |||
6910 | DEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,({ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps" , aLocalID, aRemoteID ); } | |||
6911 | "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps",{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps" , aLocalID, aRemoteID ); } | |||
6912 | aLocalID,{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps" , aLocalID, aRemoteID ); } | |||
6913 | aRemoteID{ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps" , aLocalID, aRemoteID ); } | |||
6914 | )){ if (((0x00000040 +0x80000000) & getDbgMask()) == (0x00000040 +0x80000000)) getDbgLogger()->setNextMask(0x00000040 +0x80000000 ).DebugPrintfLastMask ( "engMarkMapConfirmed: deleting confirmed entry localID='%s', remoteID='%s' from fUnconfirmedMaps" , aLocalID, aRemoteID ); }; | |||
6915 | // move it to lastsessionmap | |||
6916 | fLastSessionMaps[(*pos).first]=(*pos).second; | |||
6917 | // remove it from unconfirmed map | |||
6918 | fUnconfirmedMaps.erase(pos); | |||
6919 | } | |||
6920 | } // TLocalEngineDS::engMarkMapConfirmed | |||
6921 | ||||
6922 | ||||
6923 | ||||
6924 | // Check if the remoteid was used by an add command not | |||
6925 | // fully mapped&confirmed in the previous session | |||
6926 | bool TLocalEngineDS::isAddFromLastSession(cAppCharP aRemoteID) | |||
6927 | { | |||
6928 | TStringToStringMap::iterator pos; | |||
6929 | TStringToStringMap *mapListP; | |||
6930 | ||||
6931 | for (int i=0; i<3; i++) { | |||
6932 | // determine next list to search | |||
6933 | mapListP = i==0 ? &fPendingAddMaps : (i==1 ? &fUnconfirmedMaps : &fLastSessionMaps); | |||
6934 | // search it | |||
6935 | for (pos=mapListP->begin(); pos!=mapListP->end(); ++pos) { | |||
6936 | if (strcmp((*pos).second.c_str(),aRemoteID)==0) | |||
6937 | return true; // remoteID known -> is add from last session | |||
6938 | } | |||
6939 | } | |||
6940 | // not found in any of the lists | |||
6941 | return false; | |||
6942 | } // TLocalEngineDS::isAddFromLastSession | |||
6943 | ||||
6944 | ||||
6945 | ||||
6946 | // - called to generate Map items | |||
6947 | // Returns true if now finished for this datastore | |||
6948 | // also sets fState to dss_done when finished | |||
6949 | bool TLocalEngineDS::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP aLocalIDPrefix) | |||
6950 | { | |||
6951 | #ifdef USE_SML_EVALUATION1 | |||
6952 | sInt32 leavefree = fSessionP->getNotUsableBufferBytes(); | |||
6953 | #else | |||
6954 | sInt32 freeroom = fSessionP->getFreeCommandSize(); | |||
6955 | #endif | |||
6956 | ||||
6957 | TStringToStringMap::iterator pos=fPendingAddMaps.begin(); | |||
6958 | PDEBUGBLOCKFMT(("MapGenerate","Generating Map items...","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockExpanded ("MapGenerate","Generating Map items..." ,"datastore=%s",getName()); | |||
6959 | do { | |||
6960 | // check if already done | |||
6961 | if (pos==fPendingAddMaps.end()) break; // done | |||
6962 | // get ID | |||
6963 | string locID = (*pos).first; | |||
6964 | dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs | |||
6965 | // create prefixed version of ID | |||
6966 | string prefixedLocID; | |||
6967 | AssignString(prefixedLocID, aLocalIDPrefix); // init with prefix (if any) | |||
6968 | prefixedLocID += locID; // append local ID | |||
6969 | // add it to map command | |||
6970 | aMapCommandP->addMapItem(prefixedLocID.c_str(),(*pos).second.c_str()); | |||
6971 | // check if we could send this command | |||
6972 | #ifdef USE_SML_EVALUATION1 | |||
6973 | if ( | |||
6974 | (aMapCommandP->evalIssue( | |||
6975 | fSessionP->peekNextOutgoingCmdID(), | |||
6976 | fSessionP->getOutgoingMsgID() | |||
6977 | ) // what is left in buffer after sending Map so far | |||
6978 | >=leavefree) // all this must still be more than what we MUST leave free | |||
6979 | ) | |||
6980 | #else | |||
6981 | if (freeroom>aMapCommandP->messageSize()) | |||
6982 | #endif | |||
6983 | { | |||
6984 | // yes, it should work | |||
6985 | PDEBUGPRINTFX(DBG_PROTO,({ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Mapitem generated: localID='%s', remoteID='%s'" , prefixedLocID.c_str(), (*pos).second.c_str() ); } | |||
6986 | "Mapitem generated: localID='%s', remoteID='%s'",{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Mapitem generated: localID='%s', remoteID='%s'" , prefixedLocID.c_str(), (*pos).second.c_str() ); } | |||
6987 | prefixedLocID.c_str(),{ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Mapitem generated: localID='%s', remoteID='%s'" , prefixedLocID.c_str(), (*pos).second.c_str() ); } | |||
6988 | (*pos).second.c_str(){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Mapitem generated: localID='%s', remoteID='%s'" , prefixedLocID.c_str(), (*pos).second.c_str() ); } | |||
6989 | )){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ( "Mapitem generated: localID='%s', remoteID='%s'" , prefixedLocID.c_str(), (*pos).second.c_str() ); }; | |||
6990 | // move sent ones to unconfirmed list (Note: use real locID, without prefix!) | |||
6991 | fUnconfirmedMaps[locID]=(*pos).second; | |||
6992 | // remove item from to-be-sent list | |||
6993 | TStringToStringMap::iterator temp_pos = pos++; // make copy and set iterator to next | |||
6994 | fPendingAddMaps.erase(temp_pos); // now entry can be deleted (N.M. Josuttis, pg204) | |||
6995 | } | |||
6996 | else { | |||
6997 | // no room for this item in this message, interrupt now | |||
6998 | // - delete already added item again | |||
6999 | aMapCommandP->deleteLastMapItem(); | |||
7000 | // - interrupt here | |||
7001 | PDEBUGPRINTFX(DBG_PROTO,("Interrupted generating Map items because max message size reached")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Interrupted generating Map items because max message size reached" ); } | |||
7002 | PDEBUGENDBLOCK("MapGenerate")getDbgLogger()->DebugCloseBlock( "MapGenerate"); | |||
7003 | return false; | |||
7004 | } | |||
7005 | } while(true); | |||
7006 | // done | |||
7007 | // if we are dataaccessdone or more -> end of map phase for this datastore | |||
7008 | if (testState(dssta_dataaccessdone)) { | |||
7009 | changeState(dssta_clientmapssent,true); | |||
7010 | PDEBUGPRINTFX(DBG_PROTO,("Finished generating Map items, server has finished <sync>, we are done now")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Finished generating Map items, server has finished <sync>, we are done now" ); } | |||
7011 | } | |||
7012 | #ifdef SYDEBUG2 | |||
7013 | // else if we are not yet dssta_syncgendone -> this is the end of a early pending map send | |||
7014 | else if (!dbgTestState(dssta_syncgendone)) { | |||
7015 | PDEBUGPRINTFX(DBG_PROTO,("Finished sending cached Map items from last session")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Finished sending cached Map items from last session" ); } | |||
7016 | } | |||
7017 | // otherwise, we are not really finished with the maps yet (but with the current map command) | |||
7018 | else { | |||
7019 | PDEBUGPRINTFX(DBG_PROTO,("Finished generating Map items for now, but server still sending <Sync>")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("Finished generating Map items for now, but server still sending <Sync>" ); } | |||
7020 | } | |||
7021 | #endif | |||
7022 | PDEBUGENDBLOCK("MapGenerate")getDbgLogger()->DebugCloseBlock( "MapGenerate"); | |||
7023 | return true; | |||
7024 | } // TLocalEngineDS::engGenerateMapItems | |||
7025 | ||||
7026 | ||||
7027 | #endif // SYSYNC_SERVER | |||
7028 | ||||
7029 | ||||
7030 | ||||
7031 | // - called to mark an already generated (but probably not sent or not yet statused) item | |||
7032 | // as "to-be-resumed", by localID or remoteID (latter only in server case). | |||
7033 | // NOTE: This must be repeatable without side effects, as server must mark/save suspend state | |||
7034 | // after every request (and not just at end of session) | |||
7035 | void TLocalEngineDS::engMarkItemForResume(cAppCharP aLocalID, cAppCharP aRemoteID, bool aUnSent) | |||
7036 | { | |||
7037 | #ifdef SUPERDATASTORES1 | |||
7038 | // if we are acting as a subdatastore, aLocalID might be prefixed | |||
7039 | if (fAsSubDatastoreOf && aLocalID) { | |||
7040 | // remove prefix | |||
7041 | aLocalID = fAsSubDatastoreOf->removeSubDSPrefix(aLocalID, this); | |||
7042 | } | |||
7043 | #endif | |||
7044 | #ifdef SYSYNC_SERVER1 | |||
7045 | // Now mark for resume | |||
7046 | if (IS_SERVER(getSyncAppBase()->isServer()) && aLocalID && *aLocalID) { | |||
7047 | // localID can be a translated localid at this point (for adds), so check for it | |||
7048 | string localid=aLocalID; | |||
7049 | obtainRealLocalID(localid); | |||
7050 | logicMarkItemForResume(localid.c_str(), aRemoteID, aUnSent); | |||
7051 | } | |||
7052 | else | |||
7053 | #endif | |||
7054 | { | |||
7055 | // localID not used or client (which never has tempGUIDs) | |||
7056 | logicMarkItemForResume(aLocalID, aRemoteID, aUnSent); | |||
7057 | } | |||
7058 | } // TLocalEngineDS::engMarkItemForResume | |||
7059 | ||||
7060 | ||||
7061 | // - called to mark an already generated (but probably not sent or not yet statused) item | |||
7062 | // as "to-be-resent", by localID or remoteID (latter only in server case). | |||
7063 | void TLocalEngineDS::engMarkItemForResend(cAppCharP aLocalID, cAppCharP aRemoteID) | |||
7064 | { | |||
7065 | #ifdef SUPERDATASTORES1 | |||
7066 | // if we are acting as a subdatastore, aLocalID might be prefixed | |||
7067 | if (fAsSubDatastoreOf && aLocalID) { | |||
7068 | // remove prefix | |||
7069 | aLocalID = fAsSubDatastoreOf->removeSubDSPrefix(aLocalID, this); | |||
7070 | } | |||
7071 | #endif | |||
7072 | // a need to resend is always a problem with the remote (either explicit non-ok status | |||
7073 | // received or implicit like data size too big to be sent at all due to maxmsgsize or | |||
7074 | // maxobjsize restrictions. | |||
7075 | fRemoteItemsError++; | |||
7076 | // now mark for resend | |||
7077 | #ifdef SYSYNC_SERVER1 | |||
7078 | if (IS_SERVER(getSyncAppBase()->isServer()) && aLocalID && *aLocalID) { | |||
7079 | // localID can be a translated localid at this point (for adds), so check for it | |||
7080 | string localid=aLocalID; | |||
7081 | obtainRealLocalID(localid); | |||
7082 | logicMarkItemForResend(localid.c_str(), aRemoteID); | |||
7083 | } | |||
7084 | else | |||
7085 | #endif | |||
7086 | { | |||
7087 | // localID not used or client (which never has tempGUIDs) | |||
7088 | logicMarkItemForResend(aLocalID, aRemoteID); | |||
7089 | } | |||
7090 | } // TLocalEngineDS::engMarkItemForResend | |||
7091 | ||||
7092 | ||||
7093 | ||||
7094 | ||||
7095 | ||||
7096 | // @brief save everything needed to resume later, in case we get suspended | |||
7097 | /// - Might be called multiple times during a session, must make sure every time | |||
7098 | /// that the status is correct, that is, previous suspend state is erased | |||
7099 | localstatus TLocalEngineDS::engSaveSuspendState(bool aAnyway) | |||
7100 | { | |||
7101 | // only save here if not aborted already (aborting saves the state immediately) | |||
7102 | // or explicitly requested | |||
7103 | if (aAnyway || !isAborted()) { | |||
7104 | // only save if DS 1.2 and supported by DB | |||
7105 | if ((fSessionP->getSyncMLVersion()>=syncml_vers_1_2) && dsResumeSupportedInDB()) { | |||
7106 | PDEBUGBLOCKFMT(("SaveSuspendState","Saving state for suspend/resume","datastore=%s",getName()))getDbgLogger()->DebugOpenBlockExpanded ("SaveSuspendState" ,"Saving state for suspend/resume","datastore=%s",getName()); | |||
7107 | // save alert state (if not explicitly prevented) | |||
7108 | fResumeAlertCode=fPreventResuming ? 0 : fAlertCode; | |||
7109 | if (fResumeAlertCode) { | |||
7110 | if (fPartialItemState!=pi_state_save_outgoing) { | |||
7111 | // ONLY if we have no request for saving an outgoing item state already, | |||
7112 | // we possibly need to save a pending incoming item | |||
7113 | // if there is an incompletely received item, let it update Partial Item (fPIxxx) state | |||
7114 | // (if it is an item of this datastore, that is). | |||
7115 | if (fSessionP->fIncompleteDataCommandP) | |||
7116 | fSessionP->fIncompleteDataCommandP->updatePartialItemState(this); | |||
7117 | } | |||
7118 | // this makes sure that only ungenerated (but to-be generated) items will be | |||
7119 | // marked for resume. Items that have been generated in this session (but might | |||
7120 | // have been marked for resume in a previous session must no longer be marked | |||
7121 | // after this call. | |||
7122 | // This also includes saving state for a partially sent item so we could resume it (fPIxxx) | |||
7123 | logicMarkOnlyUngeneratedForResume(); | |||
7124 | // then, we need to additionally mark those items for resume which have been | |||
7125 | // generated, but not yet sent or sent but not received status so far. | |||
7126 | fSessionP->markPendingForResume(this); | |||
7127 | } | |||
7128 | // let datastore make all this persistent | |||
7129 | // NOTE: this must happen even if we have no suspend state here, | |||
7130 | // as marked-for-resends need to be saved here as well. | |||
7131 | localstatus sta=logicSaveResumeMarks(); | |||
7132 | if (sta!=LOCERR_OK) { | |||
7133 | PDEBUGPRINTFX(DBG_ERROR,("Error saving suspend state with logicSaveResumeMarks(), status=%hd",sta)){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger ()->setNextMask(0x00000002).DebugPrintfLastMask ("Error saving suspend state with logicSaveResumeMarks(), status=%hd" ,sta); }; | |||
7134 | } | |||
7135 | PDEBUGENDBLOCK("SaveSuspendState")getDbgLogger()->DebugCloseBlock( "SaveSuspendState"); | |||
7136 | return sta; | |||
7137 | } | |||
7138 | // resume not supported due to datastore or SyncML version<1.2 -> ok anyway | |||
7139 | PDEBUGPRINTFX(DBG_PROTO,("SaveSuspendState not possible (SyncML<1.2 or not supported by DB)")){ if (((0x00000010) & getDbgMask()) == (0x00000010)) getDbgLogger ()->setNextMask(0x00000010).DebugPrintfLastMask ("SaveSuspendState not possible (SyncML<1.2 or not supported by DB)" ); }; | |||
7140 | } | |||
7141 | return LOCERR_OK; | |||
7142 | } // TLocalEngineDS::engSaveSuspendState | |||
7143 | ||||
7144 | ||||
7145 | ||||
7146 | } // namespace sysync | |||
7147 | ||||
7148 | /* end of TLocalEngineDS implementation */ | |||
7149 | ||||
7150 | // eof |