File: | libsynthesis/src/sysync/localengineds.cpp |
Warning: | line 6521, column 5 Value stored to 'remainsvisible' is never read |
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; |
Value stored to 'remainsvisible' is never read | |
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 |