Imported Upstream version 1.0beta1
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / superdatastore.cpp
1 /*
2  *  File:         SuperDataStore.h
3  *
4  *  Author:                       Lukas Zeller (luz@synthesis.ch)
5  *
6  *  TSuperDataStore
7  *    "Virtual" datastore consisting of an union of other
8  *    datastores, for example a vCal datastore based on
9  *    two separate vEvent and vTodo datastores.
10  *
11  *  Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
12  *
13  *  2002-08-05 : luz : created
14  *
15  */
16
17 // includes
18 #include "prefix_file.h"
19 #include "sysync.h"
20 #include "syncappbase.h"
21 #include "superdatastore.h"
22
23 #ifdef SUPERDATASTORES
24
25
26 using namespace sysync;
27
28
29 // sub-datastore link config
30 // =========================
31
32
33 // config constructor
34 TSubDSLinkConfig::TSubDSLinkConfig(TLocalDSConfig *aLocalDSConfigP, TConfigElement *aParentElementP) :
35   TConfigElement(aLocalDSConfigP->getName(),aParentElementP)
36 {
37   clear();
38   fLinkedDSConfigP=aLocalDSConfigP;
39 } // TSubDSLinkConfig::TSubDSLinkConfig
40
41
42 // config destructor
43 TSubDSLinkConfig::~TSubDSLinkConfig()
44 {
45   clear();
46 } // TSubDSLinkConfig::~TSubDSLinkConfig
47
48
49 // init defaults
50 void TSubDSLinkConfig::clear(void)
51 {
52   // init defaults
53   fDispatchFilter.erase();
54   fGUIDPrefix.erase();
55   // clear inherited
56   inherited::clear();
57 } // TSubDSLinkConfig::clear
58
59
60 #ifndef HARDCODED_CONFIG
61
62 // remote rule config element parsing
63 bool TSubDSLinkConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
64 {
65   // checking the elements
66   // - identification of remote
67   if (strucmp(aElementName,"dispatchfilter")==0)
68     expectString(fDispatchFilter);
69   else if (strucmp(aElementName,"guidprefix")==0)
70     expectString(fGUIDPrefix);
71   // - not known here
72   else
73     return inherited::localStartElement(aElementName,aAttributes,aLine);
74   // ok
75   return true;
76 } // TSubDSLinkConfig::localStartElement
77
78 #endif
79
80
81 // superdatastore config
82 // =====================
83
84 TSuperDSConfig::TSuperDSConfig(const char* aName, TConfigElement *aParentElement) :
85   TLocalDSConfig(aName,aParentElement)
86 {
87   clear();
88 } // TSuperDSConfig::TSuperDSConfig
89
90
91 TSuperDSConfig::~TSuperDSConfig()
92 {
93   clear();
94 } // TSuperDSConfig::~TSuperDSConfig
95
96
97 // init defaults
98 void TSuperDSConfig::clear(void)
99 {
100   // init defaults
101   // - no datastore links
102   TSubDSConfigList::iterator pos;
103   for(pos=fSubDatastores.begin();pos!=fSubDatastores.end();pos++)
104     delete *pos;
105   fSubDatastores.clear();
106   // clear inherited
107   inherited::clear();
108 } // TSuperDSConfig::clear
109
110
111 #ifndef HARDCODED_CONFIG
112
113 // config element parsing
114 bool TSuperDSConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
115 {
116   // checking the elements
117   // - links to sub-datastores
118   if (strucmp(aElementName,"contains")==0) {
119     // definition of a new datastore
120     const char* nam = getAttr(aAttributes,"datastore");
121     if (!nam)
122       return fail("'contains' missing 'datastore' attribute");
123     // search sub-datastore
124     TLocalDSConfig *subdscfgP =
125       static_cast<TSessionConfig *>(getParentElement())->getLocalDS(nam);
126     if (!subdscfgP)
127       return fail("unknown datastore '%s' specified",nam);
128     // create new datastore link
129     TSubDSLinkConfig *dslinkcfgP =
130       new TSubDSLinkConfig(subdscfgP,this);
131     // - save in list
132     fSubDatastores.push_back(dslinkcfgP);
133     // - let element handle parsing
134     expectChildParsing(*dslinkcfgP);
135   }
136   // - none known here
137   else
138     return TLocalDSConfig::localStartElement(aElementName,aAttributes,aLine);
139   // ok
140   return true;
141 } // TSuperDSConfig::localStartElement
142
143 #endif
144
145 // resolve
146 void TSuperDSConfig::localResolve(bool aLastPass)
147 {
148   if (aLastPass) {
149     // check for required settings
150     // %%% tbd
151   }
152   // resolve inherited
153   inherited::localResolve(aLastPass);
154 } // TSuperDSConfig::localResolve
155
156
157 // - create appropriate datastore from config, calls addTypeSupport as well
158 TLocalEngineDS *TSuperDSConfig::newLocalDataStore(TSyncSession *aSessionP)
159 {
160         // Synccap defaults to normal set supported by the engine by default
161         TSuperDataStore *sdsP =
162                  new TSuperDataStore(this,aSessionP,getName(),aSessionP->getSyncCapMask());
163         // add type support
164         addTypes(sdsP,aSessionP);
165         return sdsP;
166 } // TLocalDSConfig::newLocalDataStore
167
168
169
170
171
172 /*
173  * Implementation of TSuperDataStore
174  */
175
176 /* public TSuperDataStore members */
177
178
179 TSuperDataStore::TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask) :
180   TLocalEngineDS(aDSConfigP, aSessionP, aName, aCommonSyncCapMask)
181 {
182   // set config ptr
183   fDSConfigP = aDSConfigP;
184   if (!fDSConfigP)
185     SYSYNC_THROW(TSyncException(DEBUGTEXT("TSuperDataStore::TSuperDataStore called with NULL config","lds1")));
186   // reset first
187   InternalResetDataStore();
188   // create links to subdatastores
189   TSubDSConfigList::iterator pos;
190   TSubDatastoreLink link;
191   for(pos=aDSConfigP->fSubDatastores.begin();pos!=aDSConfigP->fSubDatastores.end();pos++) {
192     // start not yet pending
193     link.fStartPending=false;
194     // set link to subdatastore link config
195     link.fDSLinkConfigP=*pos;
196     // find actual datastore by "handle" (= config pointer)
197     link.fDatastoreLinkP=aSessionP->findLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
198     // if actual datastore does not yet exist, create it now. This can be
199     // the case for clients, where datastores are only instantiated when
200     // directly addressed by a SyncRequest (which will not happen for
201     // subdatastores normally)
202     if (!link.fDatastoreLinkP) {
203       // add
204       link.fDatastoreLinkP=aSessionP->addLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
205     }
206     // save link
207     fSubDSLinks.push_back(link);
208   }
209   // Important: We need to get the iterator now again, as the implicit
210   //   iterator init via InternalResetDataStore() is invalid because the list was empty then.
211   fCurrentGenDSPos=fSubDSLinks.begin();
212 } // TSuperDataStore::TSuperDataStore
213
214
215 void TSuperDataStore::InternalResetDataStore(void)
216 {
217   // init
218   fFirstTimeSync=false;
219   TSubDSLinkList::iterator pos;
220   // cancel all pending starts
221   for(pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
222     pos->fStartPending=false;
223   }
224   fSuperStartPending = false;
225   // make sure this is set in case startSync() is not called before generateSyncCommands()
226   fCurrentGenDSPos=fSubDSLinks.begin();
227 } // TSuperDataStore::InternalResetDataStore
228
229
230 TSuperDataStore::~TSuperDataStore()
231 {
232   InternalResetDataStore();
233 } // TSuperDataStore::~TSuperDataStore
234
235
236
237 // Session events, which need some distribution to subdatastores
238 // =============================================================
239
240 // Methods overriding TLocalEngineDS
241 // ----------------------------------
242
243
244 // obtain Sync Cap mask, must be lowest common mask of all subdatastores
245 uInt32 TSuperDataStore::getSyncCapMask(void)
246 {
247   // AND of all subdatastores
248   uInt32 capmask = ~0; // all bits set
249   TSubDSLinkList::iterator pos;
250   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
251     capmask = capmask & pos->fDatastoreLinkP->getSyncCapMask();
252   }
253   return capmask;
254 } // TSuperDataStore::getSyncCapMask
255
256
257 // process Sync alert from remote party: check if alert code is supported,
258 // check if slow sync is needed due to anchor mismatch
259 // - server case: also generate appropriate Alert acknowledge command
260 TAlertCommand *TSuperDataStore::engProcessSyncAlert(
261   TSuperDataStore *aAsSubDatastoreOf, // if acting as subdatastore
262   uInt16 aAlertCode,                  // the alert code
263   const char *aLastRemoteAnchor,      // last anchor of client
264   const char *aNextRemoteAnchor,      // next anchor of client
265   const char *aTargetURI,             // target URI as sent by remote, no processing at all
266   const char *aIdentifyingTargetURI,  // target URI that was used to identify datastore
267   const char *aTargetURIOptions,      // option string contained in target URI
268   SmlFilterPtr_t aTargetFilter,       // DS 1.2 filter, NULL if none
269   const char *aSourceURI,             // source URI
270   TStatusCommand &aStatusCommand      // status that might be modified
271 )
272 {
273   TAlertCommand *alertcmdP=NULL;
274
275   TAlertCommand *subalertcmdP=NULL;
276   TStatusCommand substatus(fSessionP);
277
278   SYSYNC_TRY {
279     // alert all subdatastores
280     TSubDSLinkList::iterator pos;
281     for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
282       subalertcmdP=pos->fDatastoreLinkP->engProcessSyncAlert(
283         this,                   // as subdatastore of myself
284         aAlertCode,             // the alert code
285         aLastRemoteAnchor,      // last anchor of client
286         aNextRemoteAnchor,      // next anchor of client
287         aTargetURI,             // target URI as sent by remote, no processing at all
288         aIdentifyingTargetURI,  // target URI (without possible CGI)
289         aTargetURIOptions,      // filtering CGI (NULL or empty if none)
290         aTargetFilter,          // DS 1.2 filter, NULL if none
291         aSourceURI,             // source URI
292         substatus               // status that might be modified
293       );
294       if (subalertcmdP) {
295         // get rid of this, we don't need it
296         delete subalertcmdP;
297       }
298       else {
299         // basic problem with one of the subdatastores
300         // - propagate error code
301         aStatusCommand.setStatusCode(substatus.getStatusCode());
302         // - cancel alert
303         return NULL;
304       }
305       // this one is pending for start
306       pos->fStartPending=true;
307     }
308     // set flag to indicate this subdatastore has init pending
309     // Now all subdatastores should be successfully alerted and have current anchor infos ready,
310     // so we can call inherited (which will obtain combined anchors from our logicInitSyncAnchors)
311     alertcmdP = inherited::engProcessSyncAlert(
312       aAsSubDatastoreOf,      // as indicated by caller (normally, superdatastore is not subdatastore of another superdatastore, but...)
313       aAlertCode,             // the alert code
314       aLastRemoteAnchor,      // last anchor of client
315       aNextRemoteAnchor,      // next anchor of client
316       aTargetURI,             // target URI as sent by remote, no processing at all
317       aIdentifyingTargetURI,  // target URI (without possible CGI)
318       aTargetURIOptions,      // filtering CGI (NULL or empty if none)
319       aTargetFilter,          // DS 1.2 filter, NULL if none
320       aSourceURI,             // source URI
321       aStatusCommand          // status that might be modified
322     );
323     // entire superdatastore is pending for start
324     fSuperStartPending=true;
325   }
326   SYSYNC_CATCH (...)
327     // clean up locally owned objects
328     if (alertcmdP) delete alertcmdP;
329     if (subalertcmdP) delete subalertcmdP;
330     SYSYNC_RETHROW;
331   SYSYNC_ENDCATCH
332   return alertcmdP;
333 } // TSuperDataStore::engProcessSyncAlert
334
335
336 // process status received for sync alert
337 bool TSuperDataStore::engHandleAlertStatus(TSyError aStatusCode)
338 {
339   // show it to all subdatastores
340   TSubDSLinkList::iterator pos;
341   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
342     pos->fDatastoreLinkP->engHandleAlertStatus(aStatusCode);
343   }
344   // all subdatastores have seen the alert status, so let superdatastore handle it as well
345   return TLocalEngineDS::engHandleAlertStatus(aStatusCode);
346 } // TSuperDataStore::engHandleAlertStatus
347
348
349 // Set remote datastore for local
350 void TSuperDataStore::engSetRemoteDatastore(
351   TRemoteDataStore *aRemoteDatastoreP  // the remote datastore involved
352 )
353 {
354   // set all subdatastores to (same) remote datastore as superdatastore itself
355   TSubDSLinkList::iterator pos;
356   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
357     pos->fDatastoreLinkP->engSetRemoteDatastore(aRemoteDatastoreP);
358   }
359   // set in superdatastore as well
360   TLocalEngineDS::engSetRemoteDatastore(aRemoteDatastoreP);
361 } // TSuperDataStore::engSetRemoteDatastore
362
363
364 // set Sync types needed for sending local data to remote DB
365 void TSuperDataStore::setSendTypeInfo(
366   TSyncItemType *aLocalSendToRemoteTypeP,
367   TSyncItemType *aRemoteReceiveFromLocalTypeP
368 )
369 {
370   // set all subdatastores to (same) types as superdatastore itself
371   TSubDSLinkList::iterator pos;
372   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
373     pos->fDatastoreLinkP->setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
374   }
375   // set in superdatastore as well
376   TLocalEngineDS::setSendTypeInfo(aLocalSendToRemoteTypeP,aRemoteReceiveFromLocalTypeP);
377 } // TSuperDataStore::setSendTypeInfo
378
379
380 // set Sync types needed for receiving remote data in local DB
381 void TSuperDataStore::setReceiveTypeInfo(
382   TSyncItemType *aLocalReceiveFromRemoteTypeP,
383   TSyncItemType *aRemoteSendToLocalTypeP
384 )
385 {
386   // set all subdatastores to (same) types as superdatastore itself
387   TSubDSLinkList::iterator pos;
388   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
389     pos->fDatastoreLinkP->setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
390   }
391   // set in superdatastore as well
392   TLocalEngineDS::setReceiveTypeInfo(aLocalReceiveFromRemoteTypeP,aRemoteSendToLocalTypeP);
393 } // TSuperDataStore::setReceiveTypeInfo
394
395
396 // init usage of datatypes set with setSendTypeInfo/setReceiveTypeInfo
397 localstatus TSuperDataStore::initDataTypeUse(void)
398 {
399   localstatus sta=LOCERR_OK;
400
401   // set all subdatastores to (same) types as superdatastore itself
402   TSubDSLinkList::iterator pos;
403   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
404     sta = pos->fDatastoreLinkP->initDataTypeUse();
405     if (sta!=LOCERR_OK)
406       return sta; // failed
407   }
408   // set in superdatastore as well
409   return TLocalEngineDS::initDataTypeUse();
410 } // TSuperDataStore::initDataTypeUse
411
412
413 // SYNC command bracket start (check credentials if needed)
414 bool TSuperDataStore::engProcessSyncCmd(
415   SmlSyncPtr_t aSyncP,                // the Sync element
416   TStatusCommand &aStatusCommand,     // status that might be modified
417   bool &aQueueForLater // will be set if command must be queued for later (re-)execution
418 )
419 {
420   // start sync for all subdatastores
421   bool ok=true;
422   bool doqueue;
423   TSubDSLinkList::iterator pos;
424   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
425     // Note: this will cause subdatastores to call their own startSync,
426     //   so we do NOT need to iterate over subdatastores in our startSync!
427     doqueue=false;
428     // if in init phase (entire superdatastore pending to start)
429     // only call subdatastores that are still pending for start, too
430     ok=true;
431     if (!fSuperStartPending || pos->fStartPending) {
432       ok=pos->fDatastoreLinkP->engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
433       if (!doqueue) {
434         // this one is now initialized. Do not do it again until all others are initialized, too
435         pos->fStartPending=false;
436       }
437       else {
438         // we must queue the entire command for later (but some subdatastores might be excluded then)
439         aQueueForLater=true; // queue if one of the subdatastores needs it
440       }
441     }
442     if (!ok) return false;
443   }
444   // start sync myself
445   ok=TLocalEngineDS::engProcessSyncCmd(aSyncP,aStatusCommand,doqueue);
446   if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
447   // if we reach this w/o queueing, start is no longer pending
448   if (!aQueueForLater) fSuperStartPending=false;
449   // done
450   return ok;
451 } // TSuperDataStore::processSyncCmd
452
453
454 // SYNC command bracket end (but another might follow in next message)
455 bool TSuperDataStore::engProcessSyncCmdEnd(bool &aQueueForLater)
456 {
457   // signal sync end to all subdatastores
458   bool ok=true;
459   bool doqueue;
460   TSubDSLinkList::iterator pos;
461   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
462     doqueue=false;
463     ok=pos->fDatastoreLinkP->engProcessSyncCmdEnd(doqueue);
464     if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
465     if (!ok) return false;
466   }
467   // signal it to myself
468   ok=TLocalEngineDS::engProcessSyncCmdEnd(doqueue);
469   if (doqueue) aQueueForLater=true; // queue if one of the subdatastores needs it
470   return ok;
471 } // TSuperDataStore::engProcessSyncCmdEnd
472
473
474 #ifdef SYSYNC_SERVER
475
476 // process map
477 localstatus TSuperDataStore::engProcessMap(cAppCharP aRemoteID, cAppCharP aLocalID)
478 {
479   TSubDatastoreLink *linkP = NULL;
480   localstatus sta = LOCERR_OK;
481
482   // item has local ID, we can find datastore by prefix
483   linkP = findSubLinkByLocalID(aLocalID);
484   if (!linkP) {
485     sta = 404; // not found
486     goto done;
487   }
488   // let subdatastore process (and only show subDS part of localID)
489   sta=linkP->fDatastoreLinkP->engProcessMap(
490     aRemoteID,
491     aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size()
492   );
493 done:
494   return sta;
495 } // TSuperDataStore::engProcessMap
496
497 #endif // SYSYNC_SERVER
498
499
500 // called to process incoming item operation
501 // Method takes ownership of syncitemP in all cases
502 // - returns true (and unmodified or non-200-successful status) if
503 //   operation could be processed regularily
504 // - returns false (but probably still successful status) if
505 //   operation was processed with internal irregularities, such as
506 //   trying to delete non-existant item in datastore with
507 //   incomplete Rollbacks (which returns status 200 in this case!).
508 bool TSuperDataStore::engProcessRemoteItem(
509   TSyncItem *syncitemP,
510   TStatusCommand &aStatusCommand
511 )
512 {
513   bool regular=true;
514   string datatext;
515   #ifdef SYSYNC_SERVER
516   TSyncItem *itemcopyP;
517   #endif
518
519   // show
520   PDEBUGBLOCKFMT((
521     "SuperProcessItem", "Processing incoming item in superdatastore",
522     "datastore=%s|SyncOp=%s|RemoteID=%s|LocalID=%s",
523     getName(),
524     SyncOpNames[syncitemP->getSyncOp()],
525     syncitemP->getRemoteID(),
526     syncitemP->getLocalID()
527   ));
528   // let appropriate subdatastore handle the command
529   TSubDatastoreLink *linkP = NULL;
530   TSyncOperation sop=syncitemP->getSyncOp();
531   string remid;
532   TSubDSLinkList::iterator pos;
533   #ifdef SYSYNC_SERVER
534   if (IS_SERVER) {
535     switch (sop) {
536       // Server case
537       case sop_wants_replace:
538       case sop_replace:
539       case sop_wants_add:
540       case sop_add:
541         // item has no local ID, we need to apply filters to item data
542         PDEBUGPRINTFX(DBG_DATA,("Checkin subdatastore filters to find where it belongs"));
543         linkP = findSubLinkByData(*syncitemP);
544         if (!linkP) goto nods;
545         PDEBUGPRINTFX(DBG_DATA,(
546           "Found item belongs to subdatastore '%s'",
547           linkP->fDatastoreLinkP->getName()
548         ));
549         // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
550         syncitemP->clearLocalID();
551         // remembert because we might need it below for move-replace
552         remid=syncitemP->getRemoteID();
553         // let subdatastore process
554         regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
555         // now check if replace was treated as add, if yes, this indicates
556         // that this might be a move between subdatastores
557         if (
558           (sop==sop_replace || sop==sop_wants_replace) &&
559           !fSlowSync && aStatusCommand.getStatusCode()==201
560         ) {
561           // this is probably a move from another datastore by changing an attribute
562           // that dispatches datastores (such as a vEvent changed to a vToDo)
563           // - so we delete all items with this remote ID in all other datastores
564           PDEBUGPRINTFX(DBG_DATA,("Replace could be a move between subdatastores, trying to delete all items with same remoteID in other subdatastores"));
565           TStatusCommand substatus(fSessionP);
566           for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
567             if (&(*pos) != linkP) {
568               // all but original datastore
569               substatus.setStatusCode(200);
570               itemcopyP = new TSyncItem();
571               // - only remote ID and syncop are relevant, leave everything else empty
572               itemcopyP->setRemoteID(remid.c_str());
573               itemcopyP->setSyncOp(sop_delete);
574               // - now try to delete. This might fail if replace above wasn't a move
575               //   itemcopyP is consumed
576               PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
577                 "Trying to delete item with remoteID='%s' from subdatastore '%s'",
578                 itemcopyP->getRemoteID(),
579                 linkP->fDatastoreLinkP->getName()
580               ));
581               regular=pos->fDatastoreLinkP->engProcessRemoteItem(itemcopyP,substatus);
582               #ifdef SYDEBUG
583               if (regular) {
584                 // deleted ok
585                 PDEBUGPRINTFX(DBG_DATA,(
586                   "Found item in '%s', deleted here (and moved to '%s')",
587                   pos->fDatastoreLinkP->getName(),
588                   linkP->fDatastoreLinkP->getName()
589                 ));
590               }
591               #endif
592             }
593           }
594           PDEBUGPRINTFX(DBG_DATA,("End of (possible) move-replace between subdatastores"));
595           regular=true; // fully ok, no matter if delete above has succeeded or not
596         }
597         goto done;
598       case sop_archive_delete:
599       case sop_soft_delete:
600       case sop_delete:
601       case sop_copy:
602         // item has no local ID AND no data, only a remoteID:
603         // we must try to read item from all subdatastores by remoteID until
604         // one is found
605         // get an empty item of correct type to call logicRetrieveItemByID
606         itemcopyP = getLocalReceiveType()->newSyncItem(getRemoteSendType(),this);
607         // - only remote ID is relevant, leave everything else empty
608         itemcopyP->setRemoteID(syncitemP->getRemoteID());
609         // try to read item from all subdatastores
610         for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
611           linkP = &(*pos);
612           // always start with 200
613           aStatusCommand.setStatusCode(200);
614           // now try to read
615           PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
616             "Trying to read item by remoteID='%s' from subdatastore '%s' to see if it is there",
617             itemcopyP->getRemoteID(),
618             linkP->fDatastoreLinkP->getName()
619           ));
620           regular=linkP->fDatastoreLinkP->logicRetrieveItemByID(*itemcopyP,aStatusCommand);
621           // must be ok AND not 404 (item not found)
622           if (regular && aStatusCommand.getStatusCode()!=404) {
623             PDEBUGPRINTFX(DBG_DATA,(
624               "Item found in subdatastore '%s', deleting it there",
625               linkP->fDatastoreLinkP->getName()
626             ));
627             // now we can delete or copy, consuming original item
628             regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
629             // delete duplicated item as well
630             delete itemcopyP;
631             // done
632             regular=true;
633             goto done;
634           }
635         }
636         // none of the datastores could process this item --> error
637         // - delete duplicated item
638         delete itemcopyP;
639         // - make sure delete reports 200 for incomplete-rollback-datastores
640         if (aStatusCommand.getStatusCode()==404 && sop!=sop_copy) {
641           // not finding an item for delete might be ok for remote...
642           if (fSessionP->getSessionConfig()->fDeletingGoneOK) {
643             // 404/410: item not found, could be because previous aborted session has
644             // already committed deletion of that item -> behave as if delete was ok
645             PDEBUGPRINTFX(DBG_DATA,("to-be-deleted item was not found, but do NOT report %hd",aStatusCommand.getStatusCode()));
646             aStatusCommand.setStatusCode(200);
647           }
648           // ...but it is a internal irregularity, fall thru to return false
649         }
650         // is an internal irregularity
651         regular=false;
652         goto done;
653     case sop_reference_only:
654     case sop_move:
655     case sop_none:
656     case numSyncOperations:
657       // nothing to do or shouldn't happen
658       break;
659     } // switch
660   } // server
661   #endif // SYSYNC_SERVER
662   #ifdef SYSYNC_CLIENT
663   if (IS_CLIENT) {
664     switch (sop) {
665       // Client case
666       case sop_wants_replace:
667       case sop_replace:
668       case sop_archive_delete:
669       case sop_soft_delete:
670       case sop_delete:
671       case sop_copy:
672         // item has local ID, we can find datastore by prefix
673         linkP = findSubLinkByLocalID(syncitemP->getLocalID());
674         if (!linkP) goto nods;
675         // remove prefix before letting subdatastore process it
676         syncitemP->fLocalID.erase(0,linkP->fDSLinkConfigP->fGUIDPrefix.size());
677         // now let subdatastore process
678         regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
679         goto done;
680       case sop_wants_add:
681       case sop_add:
682         // item has no local ID, we need to apply filters to item data
683         linkP = findSubLinkByData(*syncitemP);
684         if (!linkP) goto nods;
685         // make sure item does not have a local ID (which would be wrong because of prefixes anyway)
686         syncitemP->clearLocalID();
687         // let subdatastore process
688         regular=linkP->fDatastoreLinkP->engProcessRemoteItem(syncitemP,aStatusCommand);
689         goto done;
690     case sop_reference_only:
691     case sop_move:
692     case sop_none:
693     case numSyncOperations:
694       // nothing to do or shouldn't happen
695       break;
696     } // switch
697   } // client
698   #endif // SYSYNC_CLIENT
699 nods:
700   // no datastore or unknown command, general DB error
701   aStatusCommand.setStatusCode(510);
702   PDEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::processRemoteItem Fatal: Item cannot be processed by any subdatastore"));
703   // consume item
704   delete syncitemP;
705   regular=false;
706   goto done;
707 done:
708   PDEBUGENDBLOCK("SuperProcessItem");
709   return regular;
710 } // TSuperDataStore::engProcessRemoteItem
711
712
713
714 // - must return true if this datastore is finished with <sync>
715 //   (if all datastores return true,
716 //   session is allowed to finish sync packet with outgoing message
717 bool TSuperDataStore::isSyncDone(void)
718 {
719   // check subdatastores
720   bool done=true;
721   TSubDSLinkList::iterator pos;
722   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
723     done=done && pos->fDatastoreLinkP->isSyncDone();
724   }
725   // check myself
726   return done && TLocalEngineDS::isSyncDone();
727 } // TSuperDataStore::isSyncDone
728
729
730 // abort sync with this super datastore (that is, with all subdatastores as well)
731 void TSuperDataStore::engAbortDataStoreSync(TSyError aStatusCode, bool aLocalProblem, bool aResumable)
732 {
733   // abort subdatastores
734   TSubDSLinkList::iterator pos;
735   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
736     pos->fDatastoreLinkP->engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
737   }
738   // set code in my own ancestor
739   TLocalEngineDS::engAbortDataStoreSync(aStatusCode,aLocalProblem,aResumable);
740 } // TSuperDataStore::engAbortDataStoreSync
741
742
743 // - must return true if this datastore is finished with <sync>
744 //   (if all datastores return true,
745 //   session is allowed to finish sync packet with outgoing message
746 bool TSuperDataStore::isAborted(void)
747 {
748   // check subdatastores
749   TSubDSLinkList::iterator pos;
750   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
751     if (pos->fDatastoreLinkP->isAborted()) return true; // one aborted, super aborted as well
752   }
753   // check myself
754   return TLocalEngineDS::isAborted();
755 } // TSuperDataStore::isAborted
756
757
758 // called at very end of sync session, when everything is done
759 // Note: is also called before deleting a datastore (so aborted sessions
760 //   can do cleanup and/or statistics display as well)
761 void TSuperDataStore::engFinishDataStoreSync(localstatus aErrorStatus)
762 {
763   // inform all subdatastores
764   TSubDSLinkList::iterator pos;
765   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
766     pos->fDatastoreLinkP->engFinishDataStoreSync(aErrorStatus);
767   }
768   // call inherited
769   inherited::engFinishDataStoreSync(aErrorStatus);
770 } // TSuperDataStore::engFinishDataStoreSync
771
772
773 // Internal events during sync to access local database
774 // ====================================================
775
776 // Methods overriding TLocalEngineDS
777 // ----------------------------------
778
779
780 // Abstracts of TLocalEngineDS
781 // ----------------------------
782
783
784 // called at sync alert (before generating for client, after receiving for server)
785 // - obtains combined anchor from subdatastores
786 // - combines them into a common anchor (if possible)
787 // - updates fFirstTimeSync as well
788 localstatus TSuperDataStore::engInitSyncAnchors(
789   cAppCharP aDatastoreURI,      // local datastore URI
790   cAppCharP aRemoteDBID         // ID of remote datastore (to find session information in local DB)
791 )
792 {
793   bool allanchorsequal=true;
794   localstatus sta=LOCERR_OK;
795
796   // superdatastore has no own anchors, so collect data from subdatastores
797   fFirstTimeSync=false;
798   TSubDSLinkList::iterator pos;
799   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
800     if (pos==fSubDSLinks.begin()) {
801       /* not needed, because engInitSyncAnchors() will be called only after all subdatastore's
802          engProcessSyncAlert() was called, which in turn contains a call to engInitSyncAnchors()
803          This means we can safely assume we have the fLastRemoteAnchor/fNextLocalAnchor info
804          ready here.
805          In fact, calling this here AGAIN had the effect that the state of the first
806          subdatastore would be set BACK to dssta_adminready (instead of the required dssta_syncmodestable,
807          and in turn when the first sync command arrived, it would be rejected with "SYNC received too early".
808       // init anchors of subdatastore
809       sta = pos->fDatastoreLinkP->engInitSyncAnchors(aDatastoreURI, aRemoteDBID);
810       if (sta!=LOCERR_OK)
811         break; // exit, we cannot init
812       */
813       // assign references of first datastore
814       // - this must be same from all subdatastores
815       fLastRemoteAnchor=pos->fDatastoreLinkP->fLastRemoteAnchor;
816       // - these are used from the first datastore, and might differ (a few seconds,
817       //   that is) for other datastores
818       fLastLocalAnchor=pos->fDatastoreLinkP->fLastLocalAnchor;
819       fNextLocalAnchor=pos->fDatastoreLinkP->fNextLocalAnchor;
820     }
821     else {
822       // see if all are equal
823       allanchorsequal = allanchorsequal &&
824         pos->fDatastoreLinkP->fLastRemoteAnchor == fLastRemoteAnchor;
825     }
826     // also combine firstTimeSync (first time if it's first for any of the subdatastores)
827     fFirstTimeSync = fFirstTimeSync || pos->fDatastoreLinkP->fFirstTimeSync;
828   }
829   // make sure common anchor is valid only if all of the subdatastores have equal anchors
830   if (sta!=LOCERR_OK || fFirstTimeSync || !allanchorsequal) {
831     fLastLocalAnchor.empty();
832     fLastRemoteAnchor.empty();
833   }
834   // superdatastore gets adminready when all subdatastores have successfully done engInitSyncAnchors()
835   if (sta==LOCERR_OK) {
836     changeState(dssta_adminready); // admin data is now ready
837   }
838   // return status
839   return sta;
840 } // TSuperDataStore::engInitSyncAnchors
841
842
843 // - called at start of first <Sync> command (prepare DB for reading/writing)
844 bool TSuperDataStore::startSync(TStatusCommand &aStatusCommand)
845 {
846   DEBUGPRINTFX(DBG_HOT,("TSuperDataStore::startSync"));
847   // make sure we start generating with first datastore
848   fCurrentGenDSPos=fSubDSLinks.begin();
849   // NOTE: Do NOT iterate subdatastores, because these were called already
850   // by engProcessSyncCmd (server case) or engProcessSyncAlert (client case)
851   return true; // ok
852 } // TSuperDataStore::startSync
853
854
855 /// check is datastore is completely started.
856 /// @param[in] aWait if set, call will not return until either started state is reached
857 ///   or cannot be reached within the maximally allowed request processing time left.
858 bool TSuperDataStore::engIsStarted(bool aWait)
859 {
860   // check subdatastores
861   bool ready=true;
862   TSubDSLinkList::iterator pos;
863   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
864     ready=ready && pos->fDatastoreLinkP->engIsStarted(aWait);
865   }
866   // check myself
867   return ready && inherited::engIsStarted(aWait);
868 } // TSuperDataStore::engIsStarted
869
870
871 // remove prefix for given subDatastore
872 // @param[in] aIDWithPrefix points to ID with prefix
873 // @return NULL if either datastore not found or prefix not present in aIDWithPrefix
874 // @return pointer to first char in aIDWithPrefix which is not part of the prefix
875 cAppCharP TSuperDataStore::removeSubDSPrefix(cAppCharP aIDWithPrefix, TLocalEngineDS *aLocalDatastoreP)
876 {
877   if (!aIDWithPrefix) return NULL;
878   TSubDSLinkList::iterator pos;
879   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
880     if (pos->fDatastoreLinkP == aLocalDatastoreP) {
881       // check the prefix
882       if (strnncmp(
883         aIDWithPrefix,
884         pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
885         pos->fDSLinkConfigP->fGUIDPrefix.size()
886       ) ==0)
887         return aIDWithPrefix+pos->fDSLinkConfigP->fGUIDPrefix.size(); // return start of subDS ID
888       else
889         return aIDWithPrefix; // datastore found, but prefix is not there, return unmodified
890     }
891   }
892   return NULL;
893 } // TSuperDataStore::removeSubDSPrefix
894
895
896 // private helper: find subdatastore which matches prefix of given localID
897 TSubDatastoreLink *TSuperDataStore::findSubLinkByLocalID(const char *aLocalID)
898 {
899   TSubDSLinkList::iterator pos;
900   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
901     if (strnncmp(
902       aLocalID,
903       pos->fDSLinkConfigP->fGUIDPrefix.c_str(),
904       pos->fDSLinkConfigP->fGUIDPrefix.size()
905     ) ==0) {
906       // found
907       return &(*pos);
908     }
909   }
910   return NULL; // not found
911 } // TSuperDataStore::findSubLinkByLocalID
912
913
914 // private helper: find subdatastore which can accept item data
915 TSubDatastoreLink *TSuperDataStore::findSubLinkByData(TSyncItem &aSyncItem)
916 {
917   TSubDSLinkList::iterator pos;
918   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
919     PDEBUGPRINTFX(DBG_DATA+DBG_DETAILS,(
920       "Testing item data against <dispatchfilter> of subdatastore '%s'",
921       pos->fDatastoreLinkP->getName()
922     ));
923     if (aSyncItem.testFilter(pos->fDSLinkConfigP->fDispatchFilter.c_str())) {
924       // found
925       return &(*pos);
926     }
927   }
928   return NULL; // not found
929 } // TSuperDataStore::findSubLinkByData
930
931
932 // only dummy, creates error if called
933 bool TSuperDataStore::logicRetrieveItemByID(
934   TSyncItem &aSyncItem, // item to be filled with data from server. Local or Remote ID must already be set
935   TStatusCommand &aStatusCommand // status, must be set on error or non-200-status
936 )
937 {
938   aStatusCommand.setStatusCode(500);
939   DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicRetrieveItemByID called, which should never happen!!!!!!"));
940   return false; // not ok
941 } // TSuperDataStore::logicRetrieveItemByID
942
943
944 // only dummy, creates error if called
945 // - Method takes ownership of syncitemP in all cases
946 bool TSuperDataStore::logicProcessRemoteItem(
947   TSyncItem *syncitemP,
948   TStatusCommand &aStatusCommand,
949   bool &aVisibleInSyncset, // on entry: tells if resulting item SHOULD be visible; on exit: set if processed item remains visible in the sync set.
950   string *aGUID // GUID is stored here if not NULL
951 )
952 {
953   delete syncitemP; // consume
954   aStatusCommand.setStatusCode(500);
955   DEBUGPRINTFX(DBG_ERROR,("TSuperDataStore::logicProcessRemoteItem called, which should never happen!!!!!!"));
956   return false; // not ok
957 } // TSuperDataStore::logicProcessRemoteItem
958
959
960 // - returns true if DB implementation can filter during database fetch
961 //   (otherwise, fetched items must be filtered after being read from DB)
962 bool TSuperDataStore::engFilteredFetchesFromDB(bool aFilterChanged)
963 {
964   // only if all subdatastores support it
965   bool yes=true;
966   TSubDSLinkList::iterator pos;
967   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
968     yes = yes && pos->fDatastoreLinkP->engFilteredFetchesFromDB(aFilterChanged);
969   }
970   return yes;
971 } // TSuperDataStore::engFilteredFetchesFromDB
972
973
974 // - called for SyncML 1.1 if remote wants number of changes.
975 //   Must return -1 if no NOC value can be returned
976 sInt32 TSuperDataStore::getNumberOfChanges(void)
977 {
978   sInt32 noc,totalNoc = 0;
979   TSubDSLinkList::iterator pos;
980   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
981     noc = pos->fDatastoreLinkP->getNumberOfChanges();
982     if (noc<0) return -1; // if one of the subdatastores does not know NOC, we can't return a NOC
983     // subdatastore knows its NOC, sum up
984     totalNoc+=noc;
985   }
986   // return sum of all NOCs
987   return totalNoc;
988 }; // TSuperDataStore::getNumberOfChanges
989
990
991 // show statistics or error of current sync
992 void TSuperDataStore::showStatistics(void)
993 {
994   // show something in debug log
995   PDEBUGPRINTFX(DBG_HOT,("Superdatastore Sync for '%s' (%s), %s sync status:",
996     getName(),
997     fRemoteViewOfLocalURI.c_str(),
998     fSlowSync ? "slow" : "normal"
999   ));
1000   // and on user console
1001   CONSOLEPRINTF((""));
1002   CONSOLEPRINTF(("- Superdatastore Sync for '%s' (%s), %s sync status:",
1003     getName(),
1004     fRemoteViewOfLocalURI.c_str(),
1005     fSlowSync ? "slow" : "normal"
1006   ));
1007   // now show results
1008   if (isAborted()) {
1009     // failed
1010     PDEBUGPRINTFX(DBG_ERROR,("Warning: Failed with status code=%hd",fAbortStatusCode));
1011     CONSOLEPRINTF(("  ************ Failed with status code=%hd",fAbortStatusCode));
1012   }
1013   else {
1014     // successful: show statistics on console
1015     PDEBUGPRINTFX(DBG_HOT,("Completed successfully - details see subdatastores"));
1016     CONSOLEPRINTF(("  Completed successfully  - details see subdatastores"));
1017   }
1018   CONSOLEPRINTF((""));
1019 } // TSuperDataStore::showStatistics
1020
1021
1022 // - returns true if DB implementation of all subdatastores support resume
1023 bool TSuperDataStore::dsResumeSupportedInDB(void)
1024 {
1025   // yes if all subdatastores support it
1026   bool yes=true;
1027   TSubDSLinkList::iterator pos;
1028   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1029     yes = yes && pos->fDatastoreLinkP->dsResumeSupportedInDB();
1030   }
1031   return yes;
1032 } // TSuperDataStore::dsResumeSupportedInDB
1033
1034
1035 // helper to save resume state either at end of request or explicitly at reception of a "suspend"
1036 localstatus TSuperDataStore::engSaveSuspendState(bool aAnyway)
1037 {
1038   // only save here if not aborted already (aborting saves the state immediately)
1039   // or explicitly requested
1040   if (aAnyway || !isAborted()) {
1041     // only save if DS 1.2 and supported by DB
1042     if ((fSessionP->getSyncMLVersion()>=syncml_vers_1_2) && dsResumeSupportedInDB()) {
1043       PDEBUGBLOCKFMT(("SuperSaveSuspendState","Saving superdatastore suspend/resume state","superdatastore=%s",getName()));
1044       // save alert state
1045       fResumeAlertCode=fAlertCode;
1046       TSubDSLinkList::iterator pos;
1047       if (fResumeAlertCode) {
1048         // let all subdatastores update partial item and markOnlyUngeneratedForResume()
1049         for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1050           // save partial state if any
1051           if (pos->fDatastoreLinkP->fPartialItemState!=pi_state_save_outgoing) {
1052             // ONLY if we have no request for saving an outgoing item state already,
1053             // we possibly need to save a pending incoming item
1054             // if there is an incompletely received item, let it update Partial Item (fPIxxx) state
1055             // (if it is an item of this datastore, that is).
1056             if (fSessionP->fIncompleteDataCommandP) {
1057               fSessionP->fIncompleteDataCommandP->updatePartialItemState(pos->fDatastoreLinkP);
1058             }
1059           }
1060           // mark ungenerated
1061           pos->fDatastoreLinkP->logicMarkOnlyUngeneratedForResume();
1062         }
1063         /// @note that already generated items are related to the originating
1064         /// localEngineDS, so markPendingForResume() on existing commands will
1065         /// directly reach the correct datastore
1066         /// @note markItemForResume() will get the localID as presented to
1067         /// remote, that is in case of superdatastores with prefixes that need to be removed
1068         fSessionP->markPendingForResume(this);
1069       }
1070       // let all subdatastores logicSaveResumeMarks() to make all this persistent
1071       localstatus globErr=LOCERR_OK;
1072       for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1073         localstatus err=pos->fDatastoreLinkP->logicSaveResumeMarks();
1074         if (err!=LOCERR_OK) globErr=err;
1075       }
1076       PDEBUGENDBLOCK("SuperSaveSuspendState");
1077       return globErr;
1078     }
1079   }
1080   return LOCERR_OK;
1081 } // TSuperDataStore::engSaveSuspendState
1082
1083
1084 #ifdef SYSYNC_SERVER
1085
1086 /// @brief called at end of request processing, should be used to save suspend state
1087 /// @note subdatastores don't do anything themselves, to make sure superds can make things happen in correct order
1088 void TSuperDataStore::engRequestEnded(void)
1089 {
1090   // variant for superdatastore - also handles its subdatastores
1091   // For DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
1092   // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
1093   // from the client probably arrives much earlier.
1094   if (testState(dssta_syncmodestable)) {
1095     // make sure all unsent items are marked for resume
1096     localstatus sta=engSaveSuspendState(false); // only if not already aborted
1097     if (sta!=LOCERR_OK) {
1098       DEBUGPRINTFX(DBG_ERROR,("Could not save suspend state at end of Request: err=%hd",sta));
1099     }
1100   }
1101   // let datastore prepare for end of request (other than thread change)
1102   TSubDSLinkList::iterator pos;
1103   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1104     pos->fDatastoreLinkP->dsRequestEnded();
1105   }
1106   // then let them know that thread may change
1107   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1108     pos->fDatastoreLinkP->dsThreadMayChangeNow();
1109   }
1110 } // TSuperDataStore::engRequestEnded
1111
1112 #endif
1113
1114
1115 // - called to let server generate sync commands for client
1116 //   Returns true if now finished for this datastore
1117 //   also sets fState to dss_syncdone when finished
1118 bool TSuperDataStore::engGenerateSyncCommands(
1119   TSmlCommandPContainer &aNextMessageCommands,
1120   TSmlCommand * &aInterruptedCommandP,
1121   const char *aLocalIDPrefix
1122 )
1123 {
1124   PDEBUGBLOCKFMT(("SuperSyncGen","Now generating sync commands from superdatastore","datastore=%s",getName()));
1125   bool finished=false;
1126   string prefix;
1127
1128   while (!isAborted()) {
1129     // check for end
1130     if (fCurrentGenDSPos==fSubDSLinks.end()) {
1131       // done, update status
1132       changeState(dssta_syncgendone,true);
1133       break;
1134     }
1135     // create current prefix
1136     AssignString(prefix,aLocalIDPrefix);
1137     prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
1138     // call subdatastore to generate commands
1139     finished=fCurrentGenDSPos->fDatastoreLinkP->engGenerateSyncCommands(
1140       aNextMessageCommands,
1141       aInterruptedCommandP,
1142       prefix.c_str()
1143     );
1144     // exit if not yet finished with generating commands for this datastore
1145     if (!finished) break;
1146     // done with this datastore, switch to next if any
1147     fCurrentGenDSPos++;
1148   } // while not aborted
1149   // finished when state is dss_syncdone
1150   PDEBUGPRINTFX(DBG_DATA,(
1151     "superdatastore's engGenerateSyncCommands ended, state='%s', sync generation %sdone",
1152     getDSStateName(),
1153     dbgTestState(dssta_syncgendone,true) ? "" : "NOT "
1154   ));
1155   PDEBUGENDBLOCK("SuperSyncGen");
1156   // also finished with this datastore when aborted
1157   return (isAborted() || testState(dssta_syncgendone,true));
1158 } // TSuperDataStore::generateSyncCommands
1159
1160
1161 #ifdef SYSYNC_CLIENT
1162
1163 // Client only: returns number of unsent map items
1164 sInt32 TSuperDataStore::numUnsentMaps(void)
1165 {
1166   // add maps from all subdatastores
1167   uInt32 num=0;
1168   TSubDSLinkList::iterator pos;
1169   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
1170     num+=pos->fDatastoreLinkP->numUnsentMaps();
1171   }
1172   return num;
1173 } // TSuperDataStore::numUnsentMaps
1174
1175
1176 // called to mark maps confirmed, that is, we have received ok status for them
1177 void TSuperDataStore::engMarkMapConfirmed(cAppCharP aLocalID, cAppCharP aRemoteID)
1178 {
1179   // we must detect the subdatastore by prefix
1180   TSubDatastoreLink *linkP = findSubLinkByLocalID(aLocalID);
1181   if (linkP) {
1182     // pass to subdatastore with prefix removed
1183     linkP->fDatastoreLinkP->engMarkMapConfirmed(aLocalID+linkP->fDSLinkConfigP->fGUIDPrefix.size(),aRemoteID);
1184   }
1185 } // TSuperDataStore::engMarkMapConfirmed
1186
1187
1188 // - client only: called to generate Map items
1189 //   Returns true if now finished for this datastore
1190 //   also sets fState to dss_done when finished
1191 bool TSuperDataStore::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP aLocalIDPrefix)
1192 {
1193   TSubDSLinkList::iterator pos=fSubDSLinks.begin();
1194   bool ok;
1195   string prefix;
1196
1197   PDEBUGBLOCKDESC("SuperMapGenerate","TSuperDataStore: Generating Map items...");
1198   do {
1199     // check if already done
1200     if (pos==fSubDSLinks.end()) break; // done
1201     // create current prefix
1202     AssignString(prefix,aLocalIDPrefix);
1203     prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
1204     // generate Map items
1205     ok=pos->fDatastoreLinkP->engGenerateMapItems(aMapCommandP,prefix.c_str());
1206     // exit if not yet finished with generating map items for this datastore
1207     if (!ok) {
1208       PDEBUGENDBLOCK("MapGenerate");
1209       return false; // not all map items generated
1210     }
1211     // next datastore
1212     pos++;
1213   } while(true);
1214   // done
1215   // we are done if state is syncdone (no more sync commands will occur)
1216   if (testState(dssta_dataaccessdone)) {
1217     changeState(dssta_clientmapssent,true);
1218     PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items, server has finished <sync>, we are done now"))
1219   }
1220   #ifdef SYDEBUG
1221   // else if we are not yet dssta_syncgendone -> this is the end of a early pending map send
1222   else if (!dbgTestState(dssta_syncgendone)) {
1223     PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished sending cached Map items from last session"))
1224   }
1225   // otherwise, we are not really finished with the maps yet (but with the current map command)
1226   else {
1227     PDEBUGPRINTFX(DBG_PROTO,("TSuperDataStore: Finished generating Map items for now, but server still sending <Sync>"))
1228   }
1229   #endif
1230   PDEBUGENDBLOCK("MapGenerate");
1231   return true;
1232 } // TSuperDataStore::engGenerateMapItems
1233
1234
1235 #endif // SYSYNC_CLIENT
1236
1237
1238 /* end of TSuperDataStore implementation */
1239
1240 #endif // SUPERDATASTORES
1241
1242 // eof