Bug Summary

File:libsynthesis/src/sysync/localengineds.cpp
Warning:line 2757, column 9
Called C++ object pointer is null

Annotated Source Code

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
25using namespace sysync;
26
27namespace sysync {
28
29#ifdef SYDEBUG2
30
31cAppCharP 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
58static 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
80class TLDSfuncs {
81public:
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
544const uInt8 param_FilterArg[] = { VAL(fty_string)( (uInt8)fty_string) };
545const uInt8 param_DateArg[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp) };
546const uInt8 param_IntArg[] = { VAL(fty_integer)( (uInt8)fty_integer) };
547const uInt8 param_StrArg[] = { VAL(fty_string)( (uInt8)fty_string) };
548const uInt8 param_OneInteger[] = { VAL(fty_integer)( (uInt8)fty_integer) };
549
550const 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
596const 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
605const uInt8 param_OneStr[] = { VAL(fty_string)( (uInt8)fty_string) };
606// const uInt8 param_OneInt[] = { VAL(fty_integer) };
607const uInt8 param_TwoInt[] = { VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer) };
608const uInt8 param_SetRecordFilter[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer) };
609
610const 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
618static 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
628const 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] = {
648const 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
657TTypeSupportConfig::TTypeSupportConfig(const char* aName, TConfigElement *aParentElement) :
658 TConfigElement(aName,aParentElement)
659{
660 clear();
661} // TTypeSupportConfig::TTypeSupportConfig
662
663
664TTypeSupportConfig::~TTypeSupportConfig()
665{
666 clear();
667} // TTypeSupportConfig::~TTypeSupportConfig
668
669
670// init defaults
671void 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
689bool 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
747bool 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
859void 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
876TLocalDSConfig::TLocalDSConfig(const char* aName, TConfigElement *aParentElement) :
877 TConfigElement(aName,aParentElement),
878 fTypeSupport("typesupport",this)
879{
880 clear();
881} // TLocalDSConfig::TLocalDSConfig
882
883
884TLocalDSConfig::~TLocalDSConfig()
885{
886 // nop so far
887} // TLocalDSConfig::~TLocalDSConfig
888
889
890// init defaults
891void 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
943bool 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
1037void 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
1076void 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
1134void 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
1150uInt16 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!)
1173void 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
1317TLocalEngineDS::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
1341TLocalEngineDS::~TLocalEngineDS()
1342{
1343 // reset everything
1344 InternalResetDataStore();
1345} // TLocalEngineDS::~TLocalEngineDS
1346
1347
1348#ifdef SYDEBUG2
1349
1350// return datastore state name
1351cAppCharP TLocalEngineDS::getDSStateName(void)
1352{
1353 return LocalDSStateNames[fLocalDSState];
1354} // TLocalEngineDS::getDSStateName
1355
1356
1357// return datastore state name
1358cAppCharP TLocalEngineDS::getDSStateName(TLocalEngineDSState aState)
1359{
1360 return LocalDSStateNames[aState];
1361} // TLocalEngineDS::getDSStateName
1362
1363#endif
1364
1365// reset datastore (after use)
1366void 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.
1383uInt16 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
1402string 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)
1419bool 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)
1533void 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
1542void 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
1552TTypeVariantDescriptor 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)
1575void 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)
1602void 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
1625void 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
1683void 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
1694void 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
1705localstatus 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
1737TConflictResolution 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
1747void 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
1769void 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
1803bool 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
1878const 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
2058endFilter:
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
2069void 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
2115const 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
2212localstatus 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
2293localstatus 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
2404error:
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
2412TAlertCommand *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;
1
'alertcmdP' initialized to a null pointer value
2426 localstatus sta=LOCERR_OK;
2427
2428 SYSYNC_TRYtry {
2429 if (IS_SERVER(getSyncAppBase()->isServer())) {
2
Taking false branch
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)
3
Assuming the condition is false
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) {
4
Assuming the condition is false
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) {
5
Assuming 'sta' is equal to LOCERR_OK
6
Taking false branch
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) {
7
Assuming 'aTargetURIOptions' is null
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())) {
8
Taking false branch
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())) {
9
Taking false branch
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())) {
10
Taking false branch
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())) {
11
Taking false branch
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())) {
12
Taking false branch
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) {
13
Taking true branch
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())) {
14
Taking false branch
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) {
15
Assuming 'prevMode' is not equal to 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()) {
16
Taking false branch
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) {
17
Taking true branch
2643 // Sync type supported
2644 // - set new state to alerted
2645 if (IS_CLIENT(!getSyncAppBase()->isServer())) {
18
Taking false branch
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 (
20
Taking true branch
2676 (
2677 (
2678 (!fLastRemoteAnchor.empty() &&
19
Assuming the condition is false
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())) {
21
Taking false branch
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())) {
22
Taking true branch
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) &&
23
Assuming the condition is false
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);
24
Called C++ object pointer is null
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
2813bool 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
2883localstatus 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
3097bool 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
3140void 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.
3163bool 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
3408localstatus 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()
3427localstatus 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()
3442localstatus 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)
3489localstatus 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
3604localstatus 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
3619localstatus 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
3639uInt16 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
3671localstatus 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
3687localstatus 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
3729localstatus 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
3789SmlDevInfSyncCapPtr_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
3878SmlDevInfDatastorePtr_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
3996void 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)
4010bool 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)
4115bool 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
4132void 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.
4182void 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
4225bool 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
4240bool 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
4258localstatus 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);
4319endchange:
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.
4335bool TLocalEngineDS::isAborted(void)
4336{
4337 return fAbortStatusCode!=0 || (fSessionP->isSuspending() && !testState(dssta_clientmapssent));
4338} // TLocalEngineDS::isAborted
4339
4340
4341// abort sync with this datastore
4342void 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
4392bool 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
4408void 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
4495void 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
4515void 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)
4527void 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
4609localstatus 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
4617localstatus 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
4630void 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
4722TSyncOpCommand *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
4827TSyncItem *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)
4852const 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)
4860void 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
4911bool 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
4972bool 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
4990bool 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
5005bool 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
5017bool 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
5044bool 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
5061class SyncOpItemAux : public SmlItemAux_t {
5062 static void freeAuxImpl(SmlItemAuxPtr_t ptr);
5063public:
5064 SyncOpItemAux();
5065
5066 TSyncItemType *remoteTypeP;
5067 TSyncItemType *localTypeP;
5068 TFmtTypes fmt;
5069 TSyncItem *syncitemP;
5070};
5071
5072SyncOpItemAux::SyncOpItemAux()
5073{
5074 freeAux = freeAuxImpl;
5075}
5076
5077void SyncOpItemAux::freeAuxImpl(SmlItemAuxPtr_t ptr)
5078{
5079 delete static_cast<SyncOpItemAux *>(ptr);
5080}
5081
5082// process SyncML SyncOp command for this datastore
5083bool 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")
5339TSyncItem *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
5370localstatus 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
5396enum 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
5410struct 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!).
5442bool 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
6449void 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!).
6489bool TLocalEngineDS::engProcessRemoteItemAsClient(
6490 TSyncItem *aSyncItemP,
6491 TStatusCommand &aStatusCommand // status, must be set to correct status code (ok / error)
6492)
6493{
6494 string remoteid,localid;
6495 bool ok;
6496 bool remainsvisible;
6497
6498 // send event and check for user abort
6499 #ifdef PROGRESS_EVENTS1
6500 if (!DB_PROGRESS_EVENT(this,pev_itemreceived,++fItemsReceived,fRemoteNumberOfChanges,0)this->getSession()->NotifySessionProgressEvent(pev_itemreceived
,this->getDSConfig(),++fItemsReceived,fRemoteNumberOfChanges
,0)
) {
6501 fSessionP->AbortSession(500,true,LOCERR_USERABORT); // this also causes datastore to be aborted
6502 }
6503 if (!SESSION_PROGRESS_EVENT(fSessionP,pev_suspendcheck,NULL,0,0,0)fSessionP->NotifySessionProgressEvent(pev_suspendcheck,__null
,0,0,0)
) {
6504 fSessionP->SuspendSession(LOCERR_USERSUSPEND);
6505 }
6506 #endif
6507 // check if datastore is aborted
6508 if (CheckAborted(aStatusCommand)) return false;
6509 // init behaviour vars (these are normally used by server only,
6510 // but must be initialized correctly for client as well as descendants might test them
6511 fPreventAdd = false;
6512 fIgnoreUpdate = false;
6513
6514 TSyncOperation syncop;
6515 LocalItemOp op;
6516
6517 TLocalSyncItemAux *aux = static_cast<TLocalSyncItemAux *>(aSyncItemP->getAux(TSyncItem::LOCAL_ENGINE));
6518 if (aux) {
6519 // Resuming the function call: restore variables, jump to store
6520 // method call.
6521 remainsvisible = aux->fRemainsVisible;
6522 syncop = aux->fSyncOp;
6523 remoteid = aux->fRemoteID;
6524 op = aux->fOp;
6525
6526 fCurrentSyncOp = aux->fCurrentSyncOp;
6527 fEchoItemOp = aux->fEchoItemOp;
6528 fItemConflictStrategy = aux->fItemConflictStrategy;
6529 fForceConflict = aux->fForceConflict;
6530 fDeleteWins = aux->fDeleteWins;
6531 fRejectStatus = aux->fRejectStatus;
6532
6533 PDEBUGPRINTFX(DBG_DATA,("%s item operation resumed",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation resumed"
,SyncOpNames[syncop]); }
;
6534 switch (op) {
6535 case LOCAL_ITEM_DELETE: goto do_delete;
6536 case LOCAL_ITEM_ADD_NORMAL: goto do_add;
6537 case LOCAL_ITEM_REPLACE: goto do_replace;
6538
6539 // Not used in client:
6540 case LOCAL_ITEM_ADD_DELETED:
6541 case LOCAL_ITEM_ADD_DUPLICATE:
6542 case LOCAL_ITEM_REPLACE_MERGED:
6543 case LOCAL_ITEM_REPLACE_FROM_CLIENT:
6544 case LOCAL_ITEM_ADD_MERGED:
6545 case LOCAL_ITEM_REPLACE_MERGED2:
6546 case LOCAL_ITEM_ADD_SLOW:
6547 break;
6548 };
6549 }
6550 if (false) {
6551 // Prepare for resuming the function call. Will only be reached
6552 // via goto with "op" set to something identifying the source of
6553 // the jump.
6554 again:
6555 if (!aux) {
6556 aux = new TLocalSyncItemAux;
6557 aSyncItemP->setAux(TSyncItem::LOCAL_ENGINE, aux);
6558 }
6559
6560 // cppcheck-suppress uninitvar
6561 aux->fRemainsVisible = remainsvisible;
6562 // cppcheck-suppress uninitvar
6563 aux->fSyncOp = syncop;
6564 aux->fRemoteID = remoteid;
6565 // cppcheck-suppress uninitvar
6566 aux->fOp = op;
6567
6568 aux->fCurrentSyncOp = fCurrentSyncOp;
6569 aux->fEchoItemOp = fEchoItemOp;
6570 aux->fItemConflictStrategy = fItemConflictStrategy;
6571 aux->fForceConflict = fForceConflict;
6572 aux->fDeleteWins = fDeleteWins;
6573 aux->fRejectStatus = fRejectStatus;
6574
6575 aStatusCommand.setStatusCode(LOCERR_AGAIN);
6576 return false;
6577 }
6578
6579 // get operation out of item
6580 syncop=aSyncItemP->getSyncOp();
6581 // show
6582 DEBUGPRINTFX(DBG_DATA,("%s item operation received",SyncOpNames[syncop])){ if (((0x00000080) & getDbgMask()) == (0x00000080)) getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("%s item operation received"
,SyncOpNames[syncop]); }
;
6583 // check if receiving commands is allowed at all
6584 // - must be in correct sync state
6585 if (!testState(dssta_syncgendone)) {
6586 // Modifications from server not allowed before client has done sync gen
6587 // %%% we could possibly relax this one, depending on the DB
6588 aStatusCommand.setStatusCode(403);
6589 PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed before client has sent entire <sync>")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Server command not allowed before client has sent entire <sync>"
); }
;
6590 delete aSyncItemP;
6591 return false;
6592 }
6593 // - must not be one-way
6594 if (fSyncMode==smo_fromclient) {
6595 // Modifications from server not allowed during update from client only
6596 aStatusCommand.setStatusCode(403);
6597 ADDDEBUGITEM(aStatusCommand,"Server command not allowed in one-way/refresh from client"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand
.addItemString("Server command not allowed in one-way/refresh from client"
); }
;
6598 PDEBUGPRINTFX(DBG_ERROR,("Server command not allowed in one-way/refresh from client")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Server command not allowed in one-way/refresh from client"
); }
;
6599 delete aSyncItemP;
6600 return false;
6601 }
6602 else {
6603 // silently discard all modifications if readonly
6604 if (fReadOnly) {
6605 PDEBUGPRINTFX(DBG_ERROR,("Read-Only: silently discarding all modifications")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Read-Only: silently discarding all modifications"
); }
;
6606 aStatusCommand.setStatusCode(200); // always ok (but never "added"), so server cannot expect Map
6607 delete aSyncItemP;
6608 return true;
6609 }
6610 // let item check itself to catch special cases
6611 // - init variables which are used/modified by item checking
6612 fItemSizeLimit=-1; // no limit
6613 fCurrentSyncOp = syncop;
6614 fEchoItemOp = sop_none;
6615 fItemConflictStrategy=fSessionConflictStrategy; // get default strategy for this item
6616 fForceConflict = false;
6617 fDeleteWins = fDSConfigP->fDeleteWins; // default to configured value
6618 fRejectStatus = -1; // no rejection
6619 // - now check
6620 // check reads and possibly modifies:
6621 // - fEchoItemOp : if not sop_none, the incoming item is echoed back to the remote with the specified syncop
6622 // - fItemConflictStrategy : might be changed from the pre-set datastore default
6623 // - fForceConflict : if set, a conflict is forced by adding the corresponding local item (if any) to the list of items to be sent
6624 // - fDeleteWins : if set, in a replace/delete conflict delete will win (regardless of strategy)
6625 // - fPreventAdd : if set, attempt to add item from remote (even implicitly trough replace) will cause no add but delete of remote item
6626 // - fIgnoreUpdate : if set, attempt to update item from remote will be ignored, only adds (also implicit ones) are executed
6627 // - fRejectStatus : if set>=0, incoming item is irgnored silently(==0) or with error(!=0)
6628 aSyncItemP->checkItem(this);
6629 // - check if incoming item should be processed at all
6630 if (fRejectStatus>=0) {
6631 // now discard the incoming item
6632 delete aSyncItemP;
6633 PDEBUGPRINTFX(fRejectStatus>=400 ? DBG_ERROR : DBG_DATA,("Item rejected with Status=%hd",fRejectStatus)){ if (((fRejectStatus>=400 ? 0x00000002 : 0x00000080) &
getDbgMask()) == (fRejectStatus>=400 ? 0x00000002 : 0x00000080
)) getDbgLogger()->setNextMask(fRejectStatus>=400 ? 0x00000002
: 0x00000080).DebugPrintfLastMask ("Item rejected with Status=%hd"
,fRejectStatus); }
;
6634 if (fRejectStatus>0) {
6635 // rejected with status code (not necessarily error)
6636 aStatusCommand.setStatusCode(fRejectStatus);
6637 if (fRejectStatus>=300) {
6638 // non 200-codes are errors
6639 ADDDEBUGITEM(aStatusCommand,"Item rejected"){ if ((((0x00000001) & getDbgMask()) == (0x00000001))) aStatusCommand
.addItemString("Item rejected"); }
;
6640 return false;
6641 }
6642 }
6643 // silently rejected
6644 return true;
6645 }
6646 // now perform requested operation
6647 switch (syncop) {
6648 case sop_soft_delete:
6649 case sop_archive_delete:
6650 case sop_delete:
6651 // delete item
6652 fLocalItemsDeleted++;
6653 do_delete:
6654 remainsvisible=false; // deleted not visible any more
6655 ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible); // delete in local database NOW
6656 CHECK_FOR_AGAIN(LOCAL_ITEM_DELETE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_DELETE
; goto again; }
;
6657 break;
6658 case sop_copy:
6659 // %%% note: this would belong into specific datastore implementation, but is here
6660 // now for simplicity as copy isn't used heavily het
6661 // retrieve data from local datastore
6662 if (!logicRetrieveItemByID(*aSyncItemP,aStatusCommand)) {
6663 ok=false;
6664 break;
6665 }
6666 // process like add
6667 case sop_add:
6668 // add as new item to client DB
6669 aStatusCommand.setStatusCode(201); // item added (if no error occurs)
6670 fLocalItemsAdded++;
6671 // add to local datastore
6672 // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!)
6673 remoteid=aSyncItemP->getRemoteID(); // get remote ID
6674 // check if this remoteid is already known from last session - if so, this means that
6675 // this add was re-sent and must NOT be executed
6676 if (isAddFromLastSession(remoteid.c_str())) {
6677 aStatusCommand.setStatusCode(418); // already exists
6678 PDEBUGPRINTFX(DBG_ERROR,("Warning: Item with same server-side ID (GUID) was already added in previous session - ignore add now")){ if (((0x00000002) & getDbgMask()) == (0x00000002)) getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Warning: Item with same server-side ID (GUID) was already added in previous session - ignore add now"
); }
;
6679 delete aSyncItemP; // forget item
6680 ok=false;
6681 break;
6682 }
6683 #ifdef OBJECT_FILTERING1
6684 if (!isAcceptable(aSyncItemP,aStatusCommand)) {
6685 delete aSyncItemP; // forget item
6686 ok=false; // cannot be accepted
6687 break;
6688 }
6689 #endif
6690 do_add:
6691 remainsvisible=true; // should remain visible
6692 ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // add to local database NOW, get back local GUID
6693 CHECK_FOR_AGAIN(LOCAL_ITEM_ADD_NORMAL)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_ADD_NORMAL
; goto again; }
;
6694 if (!ok) break;
6695 // if added (not replaced), we need to send map
6696 if (aStatusCommand.getStatusCode()==201) {
6697 // really added: remember map entry
6698 DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,({ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6699 "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6700 localid.c_str(),{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6701 remoteid.c_str(){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6702 )){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: add command: adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
;
6703 fPendingAddMaps[localid]=remoteid;
6704 }
6705 ok=true; // fine
6706 break;
6707 case sop_replace:
6708 // - replace item in client
6709 fLocalItemsUpdated++;
6710 aSyncItemP->setSyncOp(sop_replace);
6711 #ifdef OBJECT_FILTERING1
6712 if (!isAcceptable(aSyncItemP,aStatusCommand)) {
6713 ok=false; // cannot be accepted
6714 break;
6715 }
6716 #endif
6717 // - get remoteid BEFORE processing item (as logicProcessRemoteItem consumes the item!!),
6718 // in case replace is converted to add and we need to register a map entry.
6719 remoteid=aSyncItemP->getRemoteID(); // get remote ID
6720 do_replace:
6721 remainsvisible=true; // should remain visible
6722 ok=logicProcessRemoteItem(aSyncItemP,aStatusCommand,remainsvisible,&localid); // replace in local database NOW
6723 CHECK_FOR_AGAIN(LOCAL_ITEM_REPLACE)if (aStatusCommand.getStatusCode() == LOCERR_AGAIN) { op = LOCAL_ITEM_REPLACE
; goto again; }
;
6724 // if added (not replaced), we need to send map
6725 if (aStatusCommand.getStatusCode()==201) {
6726 // Note: logicProcessRemoteItem should NOT do an add if we have no remoteid, but return 404.
6727 // The following check is just additional security.
6728 // really added: remember map entry if server sent remoteID (normally, it won't for Replace)
6729 if (!remoteid.empty()) {
6730 // we can handle that, as remote sent us the remoteID we need to map it correctly
6731 DEBUGPRINTFX(DBG_ADMIN+DBG_DETAILS,({ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6732 "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'",{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6733 localid.c_str(),{ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6734 remoteid.c_str(){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
6735 )){ if (((0x00000040 +0x40000000) & getDbgMask()) == (0x00000040
+0x40000000)) getDbgLogger()->setNextMask(0x00000040 +0x40000000
).DebugPrintfLastMask ( "engProcessRemoteItemAsClient: replace command for unknown localID but with remoteID -> adding new entry to fPendingAddMaps - localid='%s', remoteID='%s'"
, localid.c_str(), remoteid.c_str() ); }
;
6736 fPendingAddMaps[localid]=remoteid;
6737 }
6738 else {
6739 // we cannot handle this (we shouldn't have, in logicProcessRemoteItem!!)
6740 aStatusCommand.setStatusCode(510);
6741 ok=false;
6742 }
6743 }
6744 break; // fine, ok = irregularity status
6745 default:
6746 SYSYNC_THROW(TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsClient"))throw TSyncException("Unknown sync op in TLocalEngineDS::processRemoteItemAsClient"
)
;
6747 } // switch
6748 // processed
6749 if (ok) {
6750 DB_PROGRESS_EVENT(this,pev_itemprocessed,fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted)this->getSession()->NotifySessionProgressEvent(pev_itemprocessed
,this->getDSConfig(),fLocalItemsAdded,fLocalItemsUpdated,fLocalItemsDeleted
)
;
6751 }
6752 else {
6753 // if the DB has a error string to show, add it here
6754 aStatusCommand.addItemString(lastDBErrorText().c_str());
6755 }
6756 return ok;
6757 }
6758} // TLocalEngineDS::processRemoteItemAsClient
6759
6760
6761
6762// client case: called whenever outgoing Message of Sync Package starts
6763void 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
6839sInt32 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
6848void 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
6904void 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
6926bool 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
6949bool 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)
7035void 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).
7063void 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
7099localstatus 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