Imported Upstream version 1.0beta3
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / syncsession.cpp
1 /*
2  *  TSyncSession
3  *    Represents an entire Synchronisation Session, possibly consisting
4  *    of multiple SyncML-Toolkit "Sessions" (Message composition/de-
5  *    composition) as well as multiple database synchronisations.
6  *
7  *  Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
8  *
9  *  2001-05-07 : luz : Created
10  *
11  */
12
13
14 #include "prefix_file.h"
15
16 #include "sysync.h"
17 #include "syncsession.h"
18 #include "syncagent.h"
19 #ifdef SUPERDATASTORES
20   #include "superdatastore.h"
21 #endif
22 #ifdef SCRIPT_SUPPORT
23   #include "scriptcontext.h"
24 #endif
25 #ifdef MULTI_THREAD_SUPPORT
26   #include "platform_thread.h"
27 #endif
28
29
30 #ifndef SYNCSESSION_PART1_EXCLUDE
31
32 namespace sysync {
33
34 // enum names
35 // ----------
36
37 // SyncML version info
38 const char * const SyncMLVerProtoNames[numSyncMLVersions] = {
39   "undefined",
40   "SyncML/1.0",
41   "SyncML/1.1",
42   "SyncML/1.2"
43 };
44 const SmlVersion_t SmlVersionCodes[numSyncMLVersions] = {
45   SML_VERS_UNDEF,
46   SML_VERS_1_0,
47   SML_VERS_1_1,
48   SML_VERS_1_2
49 };
50 const char * const SyncMLVerDTDNames[numSyncMLVersions] = {
51   "???",
52   "1.0",
53   "1.1",
54   "1.2"
55 };
56 const char * const SyncMLDevInfNames[numSyncMLVersions] = {
57   NULL,
58   "./devinf10",
59   "./devinf11",
60   "./devinf12"
61 };
62 #ifndef HARDCODED_CONFIG
63 // version for use in config files
64 const char * const SyncMLVersionNames[numSyncMLVersions] = {
65   "unknown",
66   "1.0",
67   "1.1",
68   "1.2"
69 };
70 #endif
71
72
73 // auth type names for config
74 const char * const authTypeNames[numAuthTypes] = {
75   "none",       // no authorisation
76   "basic",      // basic (B64 encoded user pw string)
77   "md5",        // Md5 encoded user:pw:nonce
78 };
79
80
81 // sync mode names
82 const char * const SyncModeNames[numSyncModes] = {
83   "twoway",
84   "fromserver",
85   "fromclient"
86 };
87
88
89
90 #ifdef SYDEBUG
91 // package state names
92 const char * const PackageStateNames[numPackageStates] = {
93   "idle",
94   "init",
95   "sync",
96   "initsync",
97   "map",
98   "supplement"
99 };
100
101 // sync operations
102 const char * const SyncOpNames[numSyncOperations] = {
103   "wants-add",
104   "add",
105   "wants-replace",
106   "replace",
107   "reference-only",
108   "archive+delete",
109   "soft-delete",
110   "delete",
111   "copy",
112   "move",
113   "[none]" // should be last
114 };
115
116 #endif
117
118 // sync mode descriptions
119 const char * const SyncModeDescriptions[numSyncModes] = {
120   "two-way",
121   "from server only",
122   "from client only"
123 };
124
125
126 #ifdef SCRIPT_SUPPORT
127
128 // builtin functions for status-handling scripts
129
130 // integer STATUS()
131 static void func_Status(TItemField *&aTermP, TScriptContext *aFuncContextP)
132 {
133   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
134   aTermP->setAsInteger(
135     errctxP->statuscode
136   );
137 } // func_Status
138
139
140 // void SETSTATUS(integer statuscode)
141 static void func_SetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
142 {
143   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
144   errctxP->newstatuscode=
145     aFuncContextP->getLocalVar(0)->getAsInteger();
146 } // func_SetStatus
147
148
149 // void SETRESEND(boolean doresend)
150 static void func_SetResend(TItemField *&aTermP, TScriptContext *aFuncContextP)
151 {
152   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
153   errctxP->resend=
154     aFuncContextP->getLocalVar(0)->getAsBoolean();
155 } // func_SetResend
156
157
158 // void ABORTDATASTORE(integer statuscode)
159 static void func_AbortDatastore(TItemField *&aTermP, TScriptContext *aFuncContextP)
160 {
161   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
162   if (errctxP->datastoreP) {
163     errctxP->datastoreP->engAbortDataStoreSync(aFuncContextP->getLocalVar(0)->getAsInteger(),true); // we cause the abort locally
164   }
165 } // func_AbortDatastore
166
167
168 // void STOPADDING()
169 static void func_StopAdding(TItemField *&aTermP, TScriptContext *aFuncContextP)
170 {
171   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
172   if (errctxP->datastoreP) {
173     errctxP->datastoreP->engStopAddingToRemote();
174   }
175 } // func_StopAdding
176
177
178 // string SYNCOP()
179 // returns sync-operation as text
180 static void func_SyncOp(TItemField *&aTermP, TScriptContext *aFuncContextP)
181 {
182   TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
183   aTermP->setAsString(
184     SyncOpNames[errctxP->syncop]
185   );
186 } // func_SyncOp
187
188
189 const uInt8 param_OneInteger[] = { VAL(fty_integer) };
190 const uInt8 param_TwoIntegers[] = { VAL(fty_integer), VAL(fty_integer) };
191 const uInt8 param_OneString[] = { VAL(fty_string) };
192
193 const TBuiltInFuncDef ErrorFuncDefs[] = {
194   { "STATUS", func_Status, fty_integer, 0, NULL },
195   { "SETSTATUS", func_SetStatus, fty_none, 1, param_OneInteger },
196   { "SETRESEND", func_SetResend, fty_none, 1, param_OneInteger },
197   { "ABORTDATASTORE", func_AbortDatastore, fty_none, 1, param_OneInteger },
198   { "STOPADDING", func_StopAdding, fty_none, 0, NULL },
199   { "SYNCOP", func_SyncOp, fty_string, 0, NULL },
200 };
201
202 const TFuncTable ErrorFuncTable = {
203   sizeof(ErrorFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
204   ErrorFuncDefs, // table pointer
205   NULL // no chain func
206 };
207
208
209 // void SETSTATUS(integer statuscode)
210 static void func_GetPutResSetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
211 {
212   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
213   gprctxP->statuscode=
214     aFuncContextP->getLocalVar(0)->getAsInteger();
215 } // func_GetPutResSetStatus
216
217
218 // integer ISPUT()
219 static void func_IsPut(TItemField *&aTermP, TScriptContext *aFuncContextP)
220 {
221   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
222   aTermP->setAsBoolean(gprctxP->isPut);
223 } // func_IsPut
224
225
226 // string ITEMURI()
227 static void func_ItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
228 {
229   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
230   aTermP->setAsString(gprctxP->itemURI);
231 } // func_ItemURI
232
233
234 // void SETITEMURI(string data)
235 static void func_SetItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
236 {
237   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
238   aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemURI);
239 } // func_SetItemURI
240
241
242 // string ITEMDATA()
243 static void func_ItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
244 {
245   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
246   aTermP->setAsString(gprctxP->itemData);
247 } // func_ItemData
248
249
250 // void SETITEMDATA(string data)
251 static void func_SetItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
252 {
253   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
254   aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemData);
255 } // func_SetItemData
256
257
258 // string METATYPE()
259 static void func_MetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
260 {
261   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
262   aTermP->setAsString(gprctxP->metaType);
263 } // func_MetaType
264
265
266 // void SETMETATYPE(string data)
267 static void func_SetMetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
268 {
269   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
270   aFuncContextP->getLocalVar(0)->getAsString(gprctxP->metaType);
271 } // func_SetMetaType
272
273
274 // void ISSUEPUT(boolean allowFailure, boolean noResp)
275 // use ITEMURI, ITEMDATA and METATYPE to issue a PUT command
276 static void func_IssuePut(TItemField *&aTermP, TScriptContext *aFuncContextP)
277 {
278   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
279   if (gprctxP->canIssue) {
280     TPutCommand *putcommandP = new TPutCommand(aFuncContextP->getSession());
281     putcommandP->setMeta(newMetaType(gprctxP->metaType.c_str()));
282     SmlItemPtr_t putItemP = putcommandP->addSourceLocItem(gprctxP->itemURI.c_str());
283     // - add data to item
284     putItemP->data = newPCDataString(gprctxP->itemData);
285     // issue it
286     if (aFuncContextP->getLocalVar(0)->getAsBoolean()) putcommandP->allowFailure(); // allow failure (4xx or 5xx status)
287     aFuncContextP->getSession()->issueRootPtr(putcommandP,aFuncContextP->getLocalVar(1)->getAsBoolean());
288   }
289 } // func_IssuePut
290
291
292 // void ISSUEGET(boolean allowFailure)
293 // use ITEMURI and METATYPE to issue a GET command
294 static void func_IssueGet(TItemField *&aTermP, TScriptContext *aFuncContextP)
295 {
296   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
297   if (gprctxP->canIssue) {
298     TGetCommand *getcommandP = new TGetCommand(aFuncContextP->getSession());
299     getcommandP->addTargetLocItem(gprctxP->itemURI.c_str());
300     getcommandP->setMeta(newMetaType(gprctxP->metaType.c_str()));
301     // issue it
302     if (aFuncContextP->getLocalVar(0)->getAsBoolean()) getcommandP->allowFailure(); // allow failure (4xx or 5xx status)
303     aFuncContextP->getSession()->issueRootPtr(getcommandP,false); // get with noResp does not make sense
304   }
305 } // func_IssueGet
306
307
308 // void ISSUEALERT(boolean allowFailure, integer alertcode)
309 // use ITEMDATA to add an Alert item
310 static void func_IssueAlert(TItemField *&aTermP, TScriptContext *aFuncContextP)
311 {
312   TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
313   if (gprctxP->canIssue) {
314     uInt16 alertcode = aFuncContextP->getLocalVar(1)->getAsInteger();
315     TAlertCommand *alertCommandP = new TAlertCommand(aFuncContextP->getSession(),NULL,alertcode);
316     // - add string data item
317     alertCommandP->addItem(newStringDataItem(gprctxP->itemData.c_str()));
318     // issue it
319     if (aFuncContextP->getLocalVar(0)->getAsBoolean()) alertCommandP->allowFailure(); // allow failure (4xx or 5xx status)
320     aFuncContextP->getSession()->issueRootPtr(alertCommandP,false); // Alert with noResp not supported
321   }
322 } // func_IssueAlert
323
324
325
326
327 const TBuiltInFuncDef GetPutResultFuncDefs[] = {
328   { "SETSTATUS", func_GetPutResSetStatus, fty_none, 1, param_OneInteger },
329   { "ISPUT", func_IsPut, fty_integer, 0, NULL },
330   { "ITEMURI", func_ItemURI, fty_string, 0, NULL },
331   { "SETITEMURI", func_SetItemURI, fty_none, 1, param_OneString },
332   { "ITEMDATA", func_ItemData, fty_string, 0, NULL },
333   { "SETITEMDATA", func_SetItemData, fty_none, 1, param_OneString },
334   { "METATYPE", func_MetaType, fty_string, 0, NULL },
335   { "SETMETATYPE", func_SetMetaType, fty_none, 1, param_OneString },
336   { "ISSUEPUT", func_IssuePut, fty_none, 2, param_TwoIntegers },
337   { "ISSUEGET", func_IssueGet, fty_none, 1, param_OneInteger },
338   { "ISSUEALERT", func_IssueAlert, fty_none, 2, param_TwoIntegers }
339 };
340
341 const TFuncTable GetPutResultFuncTable = {
342   sizeof(GetPutResultFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
343   GetPutResultFuncDefs, // table pointer
344   NULL // no chain func
345 };
346
347
348
349 #endif
350
351
352 #ifndef NO_REMOTE_RULES
353
354 // Remote Rule Config
355 // ==================
356
357 #define DONT_REJECT 0xFFFF
358
359 // config constructor
360 TRemoteRuleConfig::TRemoteRuleConfig(const char *aElementName, TConfigElement *aParentElementP) :
361   TConfigElement(aElementName,aParentElementP)
362 {
363   clear();
364 } // TRemoteRuleConfig::TRemoteRuleConfig
365
366
367 // config destructor
368 TRemoteRuleConfig::~TRemoteRuleConfig()
369 {
370   clear();
371 } // TRemoteRuleConfig::~TRemoteRuleConfig
372
373
374 // init defaults
375 void TRemoteRuleConfig::clear(void)
376 {
377   // init defaults
378   // - id
379   fManufacturer.erase();
380   fModel.erase();
381   fOem.erase();
382   fFirmwareVers.erase();
383   fSoftwareVers.erase();
384   fHardwareVers.erase();
385   fDevId.erase();
386   fDevTyp.erase();
387   // - options
388   fRejectStatusCode=DONT_REJECT; // not rejected
389   fLegacyMode=-1; // set if remote is known legacy, so don't use new types
390   fLenientMode=-1; // set if remote's SyncML should be handled leniently, i.e. not too strict checking where not absolutely needed
391   fLimitedFieldLengths=-1; // set if remote has limited field lengths
392   fDontSendEmptyProperties=-1; // set if remote does not want empty properties
393   fDoQuote8BitContent=-1; // normally, only use QP for contents with EOLNs in vCard 2.1
394   fDoNotFoldContent=-1; // normally, content must be folded in MIME-DIR
395   fNoReplaceInSlowsync=-1; // normally, we are allowed to use Replace (as server) in slow sync
396   fTreatRemoteTimeAsLocal=-1; // do not ignore time zone
397   fTreatRemoteTimeAsUTC=-1; // do not ignore time zone
398   fVCal10EnddatesSameDay=-1; // use default end date rendering
399   fIgnoreDevInfMaxSize=-1; // do not ignore max field size in remote's devInf
400   fIgnoreCTCap=-1; // do not ignore CTCap
401   fDSPathInDevInf=-1; // use actual DS path as used in Alert for creating datastore devInf (needed for newer Nokia clients)
402   fDSCgiInDevInf=-1; // also show CGI as used in Alert for creating datastore devInf (needed for newer Nokia clients)
403   fForceUTC=-1; // automatic decision based on DevInf (SyncML 1.1) or just UTC for SyncML 1.0
404   fForceLocaltime=-1;
405   fTreatCopyAsAdd=-1;
406   fCompleteFromClientOnly=-1;
407   fRequestMaxTime=-1; // not defined
408   fDefaultOutCharset=chs_unknown; // do not set the default output charset
409   fDefaultInCharset=chs_unknown; // do not set the default input interpretation charset
410   // - options that also have a configurable session default
411   fUpdateClientDuringSlowsync=-1;
412   fUpdateServerDuringSlowsync=-1;
413   fAllowMessageRetries=-1;
414   fStrictExecOrdering=-1;
415   #ifndef MINIMAL_CODE
416   fRemoteDescName.erase();
417   #endif
418   fSubRulesList.clear(); // no included subrules
419         fSubRule = false; // normal rule by default
420   // - rules are final by default
421   fFinalRule = true;
422   // clear inherited
423   inherited::clear();
424 } // TRemoteRuleConfig::clear
425
426
427 #ifndef HARDCODED_CONFIG
428
429 // remote rule config element parsing
430 bool TRemoteRuleConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
431 {
432   // checking the elements
433   // - identification of remote (irrelevant for subrules)
434   if (!fSubRule && strucmp(aElementName,"manufacturer")==0)
435     expectString(fManufacturer);
436   else if (!fSubRule && strucmp(aElementName,"model")==0)
437     expectString(fModel);
438   else if (!fSubRule && strucmp(aElementName,"oem")==0)
439     expectString(fOem);
440   else if (!fSubRule && strucmp(aElementName,"firmware")==0)
441     expectString(fFirmwareVers);
442   else if (!fSubRule && strucmp(aElementName,"software")==0)
443     expectString(fSoftwareVers);
444   else if (!fSubRule && strucmp(aElementName,"hardware")==0)
445     expectString(fHardwareVers);
446   else if (!fSubRule && strucmp(aElementName,"deviceid")==0)
447     expectString(fDevId);
448   else if (!fSubRule && strucmp(aElementName,"devicetype")==0)
449     expectString(fDevTyp);
450   // - options
451   else if (strucmp(aElementName,"legacymode")==0)
452     expectTristate(fLegacyMode);
453   else if (strucmp(aElementName,"lenientmode")==0)
454     expectTristate(fLenientMode);
455   else if (strucmp(aElementName,"limitedfieldlengths")==0)
456     expectTristate(fLimitedFieldLengths);
457   else if (strucmp(aElementName,"noemptyproperties")==0)
458     expectTristate(fDontSendEmptyProperties);
459   else if (strucmp(aElementName,"quote8bitcontent")==0)
460     expectTristate(fDoQuote8BitContent);
461   else if (strucmp(aElementName,"nocontentfolding")==0)
462     expectTristate(fDoNotFoldContent);
463   else if (strucmp(aElementName,"noreplaceinslowsync")==0)
464     expectTristate(fNoReplaceInSlowsync);
465   else if (strucmp(aElementName,"treataslocaltime")==0)
466     expectTristate(fTreatRemoteTimeAsLocal);
467   else if (strucmp(aElementName,"treatasutc")==0)
468     expectTristate(fTreatRemoteTimeAsUTC);
469   else if (strucmp(aElementName,"autoenddateinclusive")==0)
470     expectTristate(fVCal10EnddatesSameDay);
471   else if (strucmp(aElementName,"ignoredevinfmaxsize")==0)
472     expectTristate(fIgnoreDevInfMaxSize);
473   else if (strucmp(aElementName,"ignorectcap")==0)
474     expectTristate(fIgnoreCTCap);
475   else if (strucmp(aElementName,"dspathindevinf")==0)
476     expectTristate(fDSPathInDevInf);
477   else if (strucmp(aElementName,"dscgiindevinf")==0)
478     expectTristate(fDSCgiInDevInf);
479   else if (strucmp(aElementName,"updateclientinslowsync")==0)
480     expectTristate(fUpdateClientDuringSlowsync);
481   else if (strucmp(aElementName,"updateserverinslowsync")==0)
482     expectTristate(fUpdateServerDuringSlowsync);
483   else if (strucmp(aElementName,"allowmessageretries")==0)
484     expectTristate(fAllowMessageRetries);
485   else if (strucmp(aElementName,"strictexecordering")==0)
486     expectTristate(fStrictExecOrdering);
487   else if (strucmp(aElementName,"treatcopyasadd")==0)
488     expectTristate(fTreatCopyAsAdd);
489   else if (strucmp(aElementName,"completefromclientonly")==0)
490     expectTristate(fCompleteFromClientOnly);
491   else if (strucmp(aElementName,"requestmaxtime")==0)
492     expectInt32(fRequestMaxTime);
493   else if (strucmp(aElementName,"outputcharset")==0)
494     expectEnum(sizeof(fDefaultOutCharset),&fDefaultOutCharset,MIMECharSetNames,numCharSets);
495   else if (strucmp(aElementName,"inputcharset")==0)
496     expectEnum(sizeof(fDefaultInCharset),&fDefaultInCharset,MIMECharSetNames,numCharSets);
497   else if (strucmp(aElementName,"rejectstatus")==0)
498     expectUInt16(fRejectStatusCode);
499   else if (strucmp(aElementName,"forceutc")==0)
500     expectTristate(fForceUTC);
501   else if (strucmp(aElementName,"forcelocaltime")==0)
502     expectTristate(fForceLocaltime);
503   // inclusion of subrules
504   else if (strucmp(aElementName,"include")==0) {
505                 // <include rule=""/>  
506     expectEmpty();
507     const char* nam = getAttr(aAttributes,"rule");
508     if (!nam)
509         return fail("<include> must specify \"rule\"");
510     else {
511         // find rule
512       TRemoteRulesList::iterator pos;
513       TSessionConfig *scfgP = static_cast<TSessionConfig *>(getParentElement());
514       for(pos=scfgP->fRemoteRulesList.begin();pos!=scfgP->fRemoteRulesList.end();pos++) {
515         if (strucmp(nam,(*pos)->getName())==0) {
516                             fSubRulesList.push_back(*pos);
517           return true; // done
518         }
519       }
520         return fail("rule '%s' for <include> not found (must be defined before included)",nam);
521     }
522   }
523   // rule script. Note that this is special, as it is NOT resolved in the config, but
524   // copied to the session first, as it might differ between sessions.
525   #ifdef SCRIPT_SUPPORT
526   else if (strucmp(aElementName,"rulescript")==0)
527     expectScript(fRuleScriptTemplate,aLine,NULL,true); // late binding, no declarations allowed
528   #endif
529   #ifndef MINIMAL_CODE
530   else if (strucmp(aElementName,"descriptivename")==0)
531     expectString(fRemoteDescName);
532   #endif
533   // - final rule?
534   else if (strucmp(aElementName,"finalrule")==0)
535     expectBool(fFinalRule);
536   // - not known here
537   else
538     return inherited::localStartElement(aElementName,aAttributes,aLine);
539   // ok
540   return true;
541 } // TRemoteRuleConfig::localStartElement
542
543 #endif // HARDCODED_CONFIG
544
545 #endif // NO_REMOTE_RULES
546
547
548 #endif // not SYNCSESSION_PART1_EXCLUDE
549 #ifndef SYNCSESSION_PART2_EXCLUDE
550
551
552 // Session Config
553 // ==============
554
555
556 // config constructor
557 TSessionConfig::TSessionConfig(const char *aElementName, TConfigElement *aParentElementP) :
558   inherited(aElementName,aParentElementP)
559 {
560   clear();
561 } // TSessionConfig::TSessionConfig
562
563
564 // config destructor
565 TSessionConfig::~TSessionConfig()
566 {
567   clear();
568 } // TSessionConfig::~TSessionConfig
569
570
571 // init defaults
572 void TSessionConfig::clear(void)
573 {
574   // init defaults
575   #ifndef NO_REMOTE_RULES
576   // - no remote rules
577   TRemoteRulesList::iterator pos;
578   for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
579     delete *pos;
580   fRemoteRulesList.clear();
581   #endif
582   // remove datastores
583   TLocalDSList::iterator pos2;
584   for(pos2=fDatastores.begin();pos2!=fDatastores.end();pos2++)
585     delete *pos2;
586   fDatastores.clear();
587   // - no simple auth
588   fSimpleAuthUser.erase();
589   fSimpleAuthPassword.erase();
590   // - medium timeout
591   fSessionTimeout=60; // one minute, will be overridden by derived classes
592   // - set default maximum SyncML version enabled
593   fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION;
594   // - minimum is 1.0
595   fMinSyncMLVersionSupported=syncml_vers_1_0;
596   // - accept server-alerted codes by default
597   fAcceptServerAlerted=true;
598   // - defaults for remote-rule configurable behaviour
599   fUpdateClientDuringSlowsync=false; // do not update client records during slowsync (but do it for first sync!)
600   fUpdateServerDuringSlowsync=false; // do not update server records during NON-FIRST-TIME slowsync (but do it for first sync!)
601   fAllowMessageRetries=true; // generally allow retries
602   fCompleteFromClientOnly=false; // default to standard-compliant behaviour.
603   fRequestMaxTime=0; // no limit by default
604   fRequestMinTime=0; // no minimal request processing delay
605   // - default value for flag to send property lists in CTCap
606   fShowCTCapProps=true;
607   // - default value for flag to send type/size in CTCap for SyncML 1.0 (disable as old clients like S55 crash on this)
608   fShowTypeSzInCTCap10=false;
609   if (IS_CLIENT) {
610     // - Synthesis clients always behaved like that (sending 23:59:59), so we'll keep it as a default
611     fVCal10EnddatesSameDay = true;
612   }
613   else {
614     // - Many modern clients need the exclusive format (start of next day) to detect all-day events properly.
615     //   Synthesis clients detect these fine as well, so not using 23:59:59 style by default is more
616     //   compatible in general for a server.
617     fVCal10EnddatesSameDay = false;
618   }
619   // traditionally Synthesis has folded content
620   fDoNotFoldContent = false;
621   // - default value for flag is "default" (depends on SyncML version)
622   fEnumDefaultPropParams=-1;
623   // - decide, whether multi-threading for the datastores will be used:
624   //   As there are some problems with older Linux versions (e.g. Debian 3.0r2 stable) the
625   //   default values are set for downwards compatibility Linux=false / all others=true
626   // Multithreading can be switched of either by #define or by <fMultiThread> flag
627   #if defined LINUX || !defined MULTI_THREAD_DATASTORE
628     fMultiThread= false;
629   #else
630     fMultiThread= true;
631   #endif
632   // - do not wait for status of interrupted command by default (note: before 2.1.0.2, this was always true)
633   fWaitForStatusOfInterrupted=false;
634   // - accept delete commands for already deleted items with 200 (rather that 404 or 211)
635   #ifdef SCTS_COMPATIBILITY_HACKS
636   fDeletingGoneOK=false; // SCTS needs that
637   #else
638   fDeletingGoneOK=true; // makes more sense as it avoids unnecessary session aborts
639   #endif
640   // - abort if all items sent to remote fail
641   fAbortOnAllItemsFailed=true; // note: does only apply in slow syncs now!
642   // - default to system time
643   fUserTimeContext=TCTX_SYSTEM;
644   #ifdef SCRIPT_SUPPORT
645   // - session init script
646   fSessionInitScript.erase();
647   // - status handling scripts
648   fSentItemStatusScript.erase();
649   fReceivedItemStatusScript.erase();
650   // - session termination script
651   fSessionFinishScript.erase();
652   // - custom get handler
653   fCustomGetHandlerScript.erase();
654   // - custom get and put generators
655   fCustomGetPutScript.erase();
656   fCustomEndPutScript.erase();
657   // - custom PUT and RESULT handler
658   fCustomPutResultHandlerScript.erase();
659   #endif
660   #ifndef MINIMAL_CODE
661   // - logfile
662   fLogFileName.erase();
663   if (IS_SERVER) {
664     fLogFileFormat.assign(DEFAULT_LOG_FORMAT_SERVER);
665     fLogFileLabels.assign(DEFAULT_LOG_LABELS_SERVER);
666   }
667   else {
668     fLogFileFormat.assign(DEFAULT_LOG_FORMAT_CLIENT);
669     fLogFileLabels.assign(DEFAULT_LOG_LABELS_CLIENT);  
670   }
671   fLogEnabled=true;
672   fDebugChunkMaxSize=0; // disabled
673   #endif
674   fRelyOnEarlyMaps=true; // we rely on early maps sent by clients for adds from the previous session
675   // clear inherited
676   inherited::clear();
677 } // TSessionConfig::clear
678
679
680 // get local DS config pointer by database name or dbTypeID
681 TLocalDSConfig *TSessionConfig::getLocalDS(const char *aName, uInt32 aDBTypeID)
682 {
683   TLocalDSList::iterator pos;
684   for(pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
685         if (aName==NULL) {
686         if ((*pos)->fLocalDBTypeID==aDBTypeID) return *pos; // found by DBTypeID
687     }
688     else {
689         if (strucmp((*pos)->getName(),aName)==0) return *pos; // found by name
690     }
691   }
692   return NULL; // not found
693 } // TSessionConfig::getLocalDS
694
695
696 #ifndef HARDCODED_CONFIG
697
698 // server config element parsing
699 bool TSessionConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
700 {
701   // checking the elements
702   #ifndef NO_REMOTE_RULES
703   bool isSubRule = strucmp(aElementName,"subrule")==0;
704   if (strucmp(aElementName,"remoterule")==0 || isSubRule) {
705     // check for optional name attribute
706     const char* nam = getAttr(aAttributes,"name");
707     if (!nam) nam="unnamed";
708     // create rule
709     TRemoteRuleConfig *ruleP = new TRemoteRuleConfig(nam,this);
710     ruleP->fSubRule = isSubRule;
711     fRemoteRulesList.push_back(ruleP);
712     expectChildParsing(*ruleP);
713   }
714   else
715   #endif
716   if (strucmp(aElementName,"sessiontimeout")==0)
717     expectInt32(fSessionTimeout);
718   else if (strucmp(aElementName,"requestmaxtime")==0)
719     expectUInt32(fRequestMaxTime);
720   else if (strucmp(aElementName,"requestmintime")==0)
721     expectInt32(fRequestMinTime);
722   else if (strucmp(aElementName,"simpleauthuser")==0)
723     expectString(fSimpleAuthUser);
724   else if (strucmp(aElementName,"simpleauthpw")==0)
725     expectString(fSimpleAuthPassword);
726   else if (strucmp(aElementName,"maxsyncmlversion")==0)
727     expectEnum(sizeof(fMaxSyncMLVersionSupported),&fMaxSyncMLVersionSupported,SyncMLVersionNames,numSyncMLVersions);
728   else if (strucmp(aElementName,"minsyncmlversion")==0)
729     expectEnum(sizeof(fMinSyncMLVersionSupported),&fMinSyncMLVersionSupported,SyncMLVersionNames,numSyncMLVersions);
730   else if (strucmp(aElementName,"acceptserveralerted")==0)
731     expectBool(fAcceptServerAlerted);
732   else if (strucmp(aElementName,"updateclientinslowsync")==0)
733     expectBool(fUpdateClientDuringSlowsync);
734   else if (strucmp(aElementName,"updateserverinslowsync")==0)
735     expectBool(fUpdateServerDuringSlowsync);
736   else if (strucmp(aElementName,"completefromclientonly")==0)
737     expectBool(fCompleteFromClientOnly);
738   else if (strucmp(aElementName,"allowmessageretries")==0)
739     expectBool(fAllowMessageRetries);
740   else if (strucmp(aElementName,"multithread")==0)
741     expectBool(fMultiThread);
742   else if (strucmp(aElementName,"waitforstatusofinterrupted")==0)
743     expectBool(fWaitForStatusOfInterrupted);
744   else if (strucmp(aElementName,"deletinggoneok")==0)
745     expectBool(fDeletingGoneOK);
746   else if (strucmp(aElementName,"abortonallitemsfailed")==0)
747     expectBool(fAbortOnAllItemsFailed);
748   else if (strucmp(aElementName,"showctcapproperties")==0)
749     expectBool(fShowCTCapProps);
750   else if (strucmp(aElementName,"showtypesizeinctcap10")==0)
751     expectBool(fShowTypeSzInCTCap10);
752   else if (strucmp(aElementName,"autoenddateinclusive")==0)
753     expectBool(fVCal10EnddatesSameDay);
754   else if (strucmp(aElementName,"donotfoldcontent")==0)
755     expectBool(fDoNotFoldContent);
756   else if (strucmp(aElementName,"enumdefaultpropparams")==0)
757     expectTristate(fEnumDefaultPropParams); // Tristate!!!
758   else if (strucmp(aElementName,"usertimezone")==0)
759     expectTimezone(fUserTimeContext);
760   #ifdef SCRIPT_SUPPORT
761   else if (strucmp(aElementName,"sessioninitscript")==0)
762     expectScript(fSessionInitScript,aLine,NULL);
763   else if (strucmp(aElementName,"sentitemstatusscript")==0)
764     expectScript(fSentItemStatusScript,aLine,&ErrorFuncTable);
765   else if (strucmp(aElementName,"receiveditemstatusscript")==0)
766     expectScript(fReceivedItemStatusScript,aLine,&ErrorFuncTable);
767   else if (strucmp(aElementName,"sessionfinishscript")==0)
768     expectScript(fSessionFinishScript,aLine,NULL);
769   else if (strucmp(aElementName,"customgethandlerscript")==0)
770     expectScript(fCustomGetHandlerScript,aLine,&GetPutResultFuncTable);
771   else if (strucmp(aElementName,"customgetputscript")==0)
772     expectScript(fCustomGetPutScript,aLine,&GetPutResultFuncTable);
773   else if (strucmp(aElementName,"customendputscript")==0)
774     expectScript(fCustomEndPutScript,aLine,&GetPutResultFuncTable);
775   else if (strucmp(aElementName,"customputresulthandlerscript")==0)
776     expectScript(fCustomPutResultHandlerScript,aLine,&GetPutResultFuncTable);
777   #endif
778   #ifndef MINIMAL_CODE
779   // logfile
780   else if (strucmp(aElementName,"logfile")==0)
781     expectMacroString(fLogFileName);
782   else if (strucmp(aElementName,"logformat")==0)
783     expectCString(fLogFileFormat);
784   else if (strucmp(aElementName,"loglabels")==0)
785     expectCString(fLogFileLabels);
786   else if (strucmp(aElementName,"logenabled")==0)
787     expectBool(fLogEnabled);
788   else if (strucmp(aElementName,"debugchunkmaxsize")==0)
789     expectUInt32(fDebugChunkMaxSize);
790   #endif
791   else if (strucmp(aElementName,"relyonearlymaps")==0)
792     expectBool(fRelyOnEarlyMaps);
793   // - local datastores
794   else if (strucmp(aElementName,"datastore")==0) {
795     // definition of a new datastore
796     const char* nam = getAttr(aAttributes,"name");
797     if (!nam) {
798       ReportError(true,"datastore missing 'name' attribute");
799     }
800     else {
801       // get subtype attribute (some versions can have
802       // different datastore types in same agent)
803       const char* subtype = getAttr(aAttributes,"type");
804       // create new named datastore
805       TLocalDSConfig *datastorecfgP = newDatastoreConfig(nam,subtype,this);
806       if (!datastorecfgP)
807         ReportError(true,"datastore has unknown 'type' attribute");
808       else {
809         // - save in list
810         fDatastores.push_back(datastorecfgP);
811         // - let element handle parsing
812         expectChildParsing(*datastorecfgP);
813       }
814     }
815   }
816   #ifdef SUPERDATASTORES
817   // - superdatastore
818   else if (strucmp(aElementName,"superdatastore")==0) {
819     // definition of a new datastore
820     const char* nam = getAttr(aAttributes,"name");
821     if (!nam) {
822       ReportError(true,"datastore missing 'name' attribute");
823     }
824     else {
825       // create new named datastore
826       TLocalDSConfig *datastorecfgP = new TSuperDSConfig(nam,this);
827       // - save in list
828       fDatastores.push_back(datastorecfgP);
829       // - let element handle parsing
830       expectChildParsing(*datastorecfgP);
831     }
832   }
833   #endif
834   // - none known here
835   else
836     return inherited::localStartElement(aElementName,aAttributes,aLine);
837   // ok
838   return true;
839 } // TSessionConfig::localStartElement
840
841 #endif
842
843
844 // resolve
845 void TSessionConfig::localResolve(bool aLastPass)
846 {
847   // resolve
848   if (aLastPass) {
849     #ifndef NO_REMOTE_RULES
850     // - resolve rules
851     TRemoteRulesList::iterator pos;
852     for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
853       (*pos)->Resolve(aLastPass);
854     #endif
855     TLocalDSList::iterator pos2;
856     for(pos2=fDatastores.begin();pos2!=fDatastores.end();pos2++)
857       (*pos2)->Resolve(aLastPass);
858     #ifdef SCRIPT_SUPPORT
859     TScriptContext *sccP = NULL;
860     SYSYNC_TRY {
861       // resolve all scripts in same context
862       // - init scripts
863       TScriptContext::resolveScript(getSyncAppBase(),fSessionInitScript,sccP,NULL);
864       TScriptContext::resolveScript(getSyncAppBase(),fSentItemStatusScript,sccP,NULL);
865       TScriptContext::resolveScript(getSyncAppBase(),fReceivedItemStatusScript,sccP,NULL);
866       TScriptContext::resolveScript(getSyncAppBase(),fSessionFinishScript,sccP,NULL);
867       TScriptContext::resolveScript(getSyncAppBase(),fCustomGetHandlerScript,sccP,NULL);
868       TScriptContext::resolveScript(getSyncAppBase(),fCustomGetPutScript,sccP,NULL);
869       TScriptContext::resolveScript(getSyncAppBase(),fCustomEndPutScript,sccP,NULL);
870       TScriptContext::resolveScript(getSyncAppBase(),fCustomPutResultHandlerScript,sccP,NULL);
871       // - forget this context
872       if (sccP) delete sccP;
873     }
874     SYSYNC_CATCH (...)
875       if (sccP) delete sccP;
876       SYSYNC_RETHROW;
877     SYSYNC_ENDCATCH
878     #endif
879   }
880   // resolve inherited
881   inherited::localResolve(aLastPass);
882 } // TSessionConfig::localResolve
883
884
885
886
887 // TSyncSession
888 // ============
889
890 // constructor
891 TSyncSession::TSyncSession(
892   TSyncAppBase *aSyncAppBaseP, // the owning application base (dispatcher/client base)
893   const char *aSessionID // a session ID
894 ) :
895   #ifdef SYDEBUG
896   fSessionDebugLogs(0),
897   #endif
898   fTerminated(false),
899   #ifdef SYDEBUG
900   fSessionLogger(&fSessionZones),
901   #endif
902   fSyncAppBaseP(aSyncAppBaseP) // link to owning base (dispatcher/clienbase)
903 {
904   // Inherit globally defined time zones
905   // Note: this must be done as very first step as all time output routines will use the
906   //       session zones
907   fSessionZones = *(fSyncAppBaseP->getAppZones());
908   // now mark used to avoid early timeout (will be marked again at InternalResetSession())
909   SessionUsed();
910   fLastRequestStarted=getSessionLastUsed(); // set this in case we terminate before StartMessage()
911   fSessionStarted=fLastRequestStarted; // this is also the start of the session
912   // show creation
913   DEBUGPRINTFX(DBG_OBJINST,("++++++++ TSyncSession created"));
914   // assign session ID to have debug ID on correct channel
915   fLocalSessionID.assign(aSessionID);
916   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: Session ID assigned"));
917   // init and start profiling
918   MP_SHOWCURRENT(DBG_PROFILE,"TSyncSession::TSyncSession: TSyncSession created");
919   TP_INIT(fTPInfo);
920   TP_START(fTPInfo,TP_general);
921   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: Profiling initialized"));
922   // set fields
923   fEncoding = SML_UNDEF;
924   fLocalAbortReason = true; // unless set otherwise 
925   fAbortReasonStatus = 0;
926   fSessionIsBusy = false; // not busy by default
927   fSmlWorkspaceID = 0; // no SyncML toolkit workspace ID yet
928   fMaxRoomForData = getRootConfig()->fLocalMaxMsgSize; // rough init
929   // other pointers
930   #ifdef SCRIPT_SUPPORT
931   fSessionScriptContextP = NULL;
932   #endif
933   fInterruptedCommandP = NULL;
934   fIncompleteDataCommandP = NULL;
935   #ifdef SYNCSTATUS_AT_SYNC_CLOSE
936   fSyncCloseStatusCommandP=NULL;
937   #endif
938   // we do not know anything about remote datastores yet
939   fRemoteDevInfKnown=false;
940   fRemoteDataStoresKnown=false;
941   fRemoteDataTypesKnown=false;
942   fRemoteDevInfLock=false;
943   // we have not sent any devinf to the remote yet
944   fRemoteGotDevinf=false;
945   fRemoteMustSeeDevinf=false;
946   fCustomGetPutSent=false;
947   // assume normal, full-featured session. Profile config or session progress might set this flag later 
948   fLegacyMode = false;
949   fLenientMode = false;
950
951   //initialize the conditonal variables to keep valgrind happy
952   fNeedAuth = true;
953   fRemoteRequestedAuth = auth_none;
954
955   #ifdef SYDEBUG
956   // initialize session debug logging
957   fSessionDebugLogs=getRootConfig()->fDebugConfig.fSessionDebugLogs; /// init from config @todo: get rid of this special session level flag, handle it all via session logger's fDebugEnabled / getDbgMask()
958   fSessionLogger.setEnabled(fSessionDebugLogs); // init from session-level flag @todo: get rid of this special session level flag, handle it all via session logger's fDebugEnabled / getDbgMask()
959   fSessionLogger.setMask(getRootConfig()->fDebugConfig.fDebug); // init from config
960   fSessionLogger.setOptions(&(getRootConfig()->fDebugConfig.fSessionDbgLoggerOptions));
961   if (getRootConfig()->fDebugConfig.fLogSessionsToGlobal) {
962                 // pass session output to app global logger
963     fSessionLogger.outputVia(getSyncAppBase()->getDbgLogger());
964     // show start of log
965     PDEBUGPRINTFX(DBG_HOT,("--------- START of embedded log for session ID '%s' ---------", fLocalSessionID.c_str()));
966         }
967   else {
968         // use separate output for session logs  
969     fSessionLogger.installOutput(getSyncAppBase()->newDbgOutputter(false)); // install the output object (and pass ownership!)
970     fSessionLogger.setDebugPath(getRootConfig()->fDebugConfig.fDebugInfoPath.c_str()); // base path
971     const string &name = getRootConfig()->fDebugConfig.fSessionDbgLoggerOptions.fBasename;
972     fSessionLogger.appendToDebugPath(name.empty() ?
973                                      TARGETID :
974                                      name.c_str());
975     if (getRootConfig()->fDebugConfig.fSingleSessionLog) {
976       getRootConfig()->fDebugConfig.fSessionDbgLoggerOptions.fAppend=true; // One single log - in this case, we MUST append to current log
977       fSessionLogger.appendToDebugPath("_session"); // only single session log, always with the same name
978     }
979     else {
980       if (getRootConfig()->fDebugConfig.fTimedSessionLogNames) {
981         fSessionLogger.appendToDebugPath("_");
982         string t;
983         TimestampToISO8601Str(t, getSystemNowAs(TCTX_UTC), TCTX_UTC, false, false);
984         fSessionLogger.appendToDebugPath(t.c_str());
985       }
986       fSessionLogger.appendToDebugPath("_s");
987       fSessionLogger.appendToDebugPath(fLocalSessionID.c_str());
988     }
989   }
990   fSessionLogger.DebugDefineMainThread();
991   // initialize session level dump flags
992   fDumpCount=0;
993   fIgnoreIncomingCommands=false;
994   fOutgoingXMLInstance=NULL;
995   fIncomingXMLInstance=NULL;
996   fXMLtranslate=getRootConfig()->fDebugConfig.fXMLtranslate; // initialize from config
997   fMsgDump=getRootConfig()->fDebugConfig.fMsgDump; // initialize from config
998   #endif
999
1000   // reset session at creation
1001   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: calling InternalResetSession"));
1002   InternalResetSessionEx(false);
1003   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: InternalResetSession called"));
1004   // show starting
1005   OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionstart,NULL,0,0,0);
1006   #ifdef SYDEBUG
1007   #if defined(SYSYNC_SERVER) && defined(SYSYNC_CLIENT)
1008     #define CAN_BE_TEXT "Server+Client"
1009   #elif defined(SYSYNC_SERVER)
1010     #define CAN_BE_TEXT "Server"
1011   #else
1012     #define CAN_BE_TEXT "Client"
1013   #endif
1014   if (PDEBUGTEST(DBG_HOT)) {
1015     // Show Session Start
1016     PDEBUGPRINTFX(DBG_HOT,(
1017       "==== %s Session started with SyncML (%s) Engine Version %d.%d.%d.%d",
1018       IS_SERVER ? "Server" : "Client",
1019       CAN_BE_TEXT,
1020       SYSYNC_VERSION_MAJOR,
1021       SYSYNC_VERSION_MINOR,
1022       SYSYNC_SUBVERSION,
1023       SYSYNC_BUILDNUMBER
1024     ));
1025     // show Product ID string
1026     PDEBUGPRINTFX(DBG_HOT,(
1027       "---- Hardcoded Product name: " CUST_SYNC_MODEL
1028     ));
1029     PDEBUGPRINTFX(DBG_HOT,(
1030       "---- Configured Model/Manufacturer: %s / %s",
1031       getSyncAppBase()->getModel().c_str(), getSyncAppBase()->getManufacturer().c_str()
1032     ));
1033     // show platform we're on
1034     string devid;
1035     getSyncAppBase()->getMyDeviceID(devid);
1036     PDEBUGPRINTFX(DBG_HOT,(
1037       "---- Running on " SYSYNC_PLATFORM_NAME ", URI/deviceID='%s'",
1038       devid.c_str()
1039     ));
1040     // show process and thread ID of the main session thread
1041     #ifdef MULTI_THREAD_SUPPORT
1042     PDEBUGPRINTFX(DBG_HOT,(
1043       "---- Process ID = %lu, Thread ID = %lu",
1044       myProcessID(),
1045       myThreadID()
1046     ));
1047     #endif
1048     // show platform details
1049     string dname,vers;
1050     // - as determined by engine itself
1051     getPlatformString(pfs_device_name,dname);
1052     getPlatformString(pfs_platformvers,vers);
1053     PDEBUGPRINTFX(DBG_HOT,(
1054       "---- Platform Hardware Name/Version = '%s', Firmware/OS Version = '%s'",
1055       dname.c_str(),
1056       vers.c_str()
1057     ));
1058     // - as configured
1059     PDEBUGPRINTFX(DBG_HOT,(
1060       "---- Configured Hardware Version = '%s', Firmware Version = '%s'",
1061       getSyncAppBase()->getHardwareVersion().c_str(),
1062       getSyncAppBase()->getFirmwareVersion().c_str()
1063     ));
1064     // show time zone infos
1065     lineartime_t tim;
1066     string z,ts;
1067     timecontext_t tctx;
1068     sInt16 offs;
1069     // - System local time and zone
1070     tctx = getRootConfig()->fSystemTimeContext;
1071     TzResolveMetaContext(tctx, getSessionZones()); // make non-meta
1072     TimeZoneContextToName(tctx, z, getSessionZones());
1073     tim = getSystemNowAs(tctx);
1074     StringObjTimestamp(ts,tim);
1075     TzResolveToOffset(tctx, offs, tim, false, getSessionZones());
1076     PDEBUGPRINTFX(DBG_HOT,(
1077       "---- System local time  : %s  (time zone '%s', offset %hd:%02hd hours east of UTC)",
1078       ts.c_str(),z.c_str(),
1079       (sInt16)(offs / MinsPerHour),
1080       (sInt16)abs(offs % MinsPerHour)
1081     ));
1082     PDEBUGPRINTFX(DBG_EXOTIC,("     Offset in Minutes east of UTC: %hd",offs));
1083     // - System time in UTC
1084     tim = getSystemNowAs(TCTX_UTC);
1085     StringObjTimestamp(ts,tim);
1086     PDEBUGPRINTFX(DBG_HOT,("---- System time in UTC : %s",ts.c_str()));
1087     // - make a winter and a summer test
1088     if (PDEBUGTEST(DBG_EXOTIC)) {
1089       sInt16 y,m,d;
1090       lineartime2date(tim,&y,&m,&d);
1091       d=1;m=2; // February 1st
1092       tim=date2lineartime(y,m,d);
1093       TzResolveToOffset(TCTX_SYSTEM, offs, tim, true, getSessionZones());
1094       PDEBUGPRINTFX(DBG_EXOTIC,(
1095         "---- System time zone offset per %04hd-02-01 = %hd:%02hd (=%hd mins)",
1096         y, (sInt16)(offs / MinsPerHour), (sInt16)abs(offs % MinsPerHour), offs
1097       ));
1098       d=1;m=8; // August 1st
1099       tim=date2lineartime(y,m,d);
1100       TzResolveToOffset(TCTX_SYSTEM, offs, tim, true, getSessionZones());
1101       PDEBUGPRINTFX(DBG_EXOTIC,(
1102         "---- System time zone offset per %04hd-08-01 = %hd:%02hd (=%hd mins)",
1103         y, (sInt16)(offs / MinsPerHour), (sInt16)abs(offs % MinsPerHour), offs
1104       ));
1105     }
1106   }
1107   #endif
1108   DebugShowCfgInfo();
1109 } // TSyncSession::TSyncSession
1110
1111
1112 // destructor
1113 TSyncSession::~TSyncSession()
1114 {
1115   // remove user data pointer because session does not exist any longer
1116   getSyncAppBase()->setSmlInstanceUserData(fSmlWorkspaceID,NULL);
1117   // make sure it is terminated (but normally it is already terminated here)
1118   TerminateSession();
1119   // debug
1120   DEBUGPRINTFX(DBG_OBJINST,("-------- TSyncSession almost destroyed (except implicit member destruction)"));
1121   #ifdef SYDEBUG
1122   if (getRootConfig()->fDebugConfig.fLogSessionsToGlobal) {
1123     // show end of embedded log
1124     PDEBUGPRINTFX(DBG_HOT,("--------- END of embedded log for session ID '%s' ---------", fLocalSessionID.c_str()));
1125         }
1126   fSessionLogger.DebugThreadOutputDone();
1127   #endif
1128 } // TSyncSession::~TSyncSession
1129
1130
1131 /// @brief terminate a session.
1132 /// @Note: Termination is final - session cannot be restarted by RestartSession() after
1133 //         calling this routine
1134 void TSyncSession::TerminateSession(void)
1135 {
1136   if (!fTerminated) {
1137     // save type of ending (before fAborted gets reset in InternalResetSession())
1138     bool normalend = !fAborted;
1139     bool allsuccess = isAllSuccess();
1140     // do this class' reset stuff
1141     DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TerminateSession: calling InternalResetSession"));
1142     InternalResetSessionEx(true);
1143     DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TerminateSession: InternalResetSession called"));
1144           #ifdef SCRIPT_SUPPORT
1145     // remove the session script context
1146     if (fSessionScriptContextP) {
1147         delete fSessionScriptContextP;
1148       fSessionScriptContextP = NULL;
1149     }
1150     #endif
1151     // remove all local datastores
1152     TLocalDataStorePContainer::iterator pos1;
1153     int n=fLocalDataStores.size();
1154     PDEBUGPRINTFX(DBG_EXOTIC,("Deleting %d datastores",n));
1155     for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
1156       delete *pos1;
1157     }
1158     fLocalDataStores.clear(); // clear list
1159     // remove all local itemtypes
1160     TSyncItemTypePContainer::iterator pos2;
1161     for (pos2=fLocalItemTypes.begin(); pos2!=fLocalItemTypes.end(); ++pos2) {
1162       delete *pos2;
1163     }
1164     fLocalItemTypes.clear(); // clear list
1165     #ifdef SYDEBUG
1166     // save half-begun XML translations
1167     XMLTranslationOutgoingEnd();
1168     XMLTranslationIncomingEnd();
1169     #endif
1170     // stop and show profiling info
1171     TP_STOP(fTPInfo);
1172     #ifdef TIME_PROFILING
1173     if (getDbgMask() & DBG_PROFILE) {
1174       sInt16 i;
1175       uInt32 sy,us;
1176       PDEBUGPRINTFX(DBG_PROFILE,("Session CPU usage statistics: (system/user/total)"));
1177       // sections
1178       for (i=0; i<numTPTypes; i++) {
1179         sy = TP_GETSYSTEMMS(fTPInfo,(TTP_Types)i);
1180         us = TP_GETUSERMS(fTPInfo,(TTP_Types)i);
1181         PDEBUGPRINTFX(DBG_PROFILE,(
1182           "- %-20s : %10ld /%10ld /%10ld ms",
1183           TP_TypeNames[i],
1184           sy,
1185           us,
1186           sy+us
1187         ));
1188       }
1189       // total
1190       sy = TP_GETTOTALSYSTEMMS(fTPInfo);
1191       us = TP_GETTOTALUSERMS(fTPInfo);
1192       PDEBUGPRINTFX(DBG_PROFILE,(
1193         "- TOTAL                : %10ld /%10ld /%10ld ms",
1194         sy,
1195         us,
1196         sy+us
1197       ));
1198       // Real time
1199       uInt32 rt = TP_GETREALTIME(fTPInfo);
1200       PDEBUGPRINTFX(DBG_PROFILE,(
1201         "- Real Time            : %10ld ms",
1202         rt
1203       ));
1204       // % CPU
1205       PDEBUGPRINTFX(DBG_PROFILE,(
1206         "- CPU load             : %10ld promille",
1207         rt ? (sy+us)*1000/rt : 0
1208       ));
1209     }
1210     #endif
1211     MP_SHOWCURRENT(DBG_PROFILE,"TSyncSession deleting");
1212     // show ending (if not normal, then ending was already shown in AbortSession())
1213     if (normalend) {
1214       OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionend,NULL, allsuccess ? 0 : LOCERR_INCOMPLETE,0,0);
1215     }
1216     // is NOW terminated
1217     fTerminated = true;
1218     PDEBUGPRINTFX(DBG_EXOTIC,("Session is now terminated (but not yet deleted)"));
1219   } // if not already terminated
1220 } // TSyncSession::TerminateSession
1221
1222
1223 // Virtual version
1224 void TSyncSession::ResetSession(void)
1225 {
1226   // terminate sync of datastores
1227   TerminateDatastores();
1228   // reset internals
1229   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::ResetSession: calling InternalResetSession"));
1230   InternalResetSessionEx(false);
1231   DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::ResetSession: InternalResetSession called"));
1232   // no ancestor to call
1233 } // TSyncSession::ResetSession
1234
1235
1236 /// @brief Announce destruction of descendant to all datastores which might have direct links to these descendants and must cancel those
1237 /// @note must be called by derived class' destructors to allow datastores to detach from agent BEFORE descendant destructor has run
1238 void TSyncSession::announceDestruction()
1239 {
1240   // terminate sync with all datastores
1241   TLocalDataStorePContainer::iterator pos;
1242   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1243     // now let datastores cancel possible direct links to derived TSession
1244     (*pos)->announceAgentDestruction();
1245   }
1246 } // TSyncSession::announceDestruction
1247
1248
1249 // - terminate all datastores
1250 void TSyncSession::TerminateDatastores(localstatus aAbortStatusCode)
1251 {
1252   // terminate sync with all datastores
1253   TLocalDataStorePContainer::iterator pos;
1254   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1255     // terminate
1256     (*pos)->engTerminateDatastore(aAbortStatusCode);
1257   }
1258 } // TSyncSession::TerminateDatastores
1259
1260
1261 // - resets session and removes all datastores (local and remote)
1262 void TSyncSession::ResetAndRemoveDatastores(void)
1263 {
1264   // Must reset session before
1265   ResetSession();
1266   // remove all local datastores
1267   TLocalDataStorePContainer::iterator pos;
1268   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1269     // now actually delete them
1270     TLocalEngineDS *dsP = (*pos);
1271     if (dsP) {
1272       (*pos) = NULL; // to avoid double deletes
1273       delete dsP;
1274     }
1275   }
1276   fLocalDataStores.clear(); // remove pointers
1277 } // TSyncSession::ResetAndRemoveDatastores
1278
1279
1280
1281 // reset session to inital state (new)
1282 // - this is called at creation and destruction but can be called
1283 //   also when an existing session needs to be restarted.
1284 // - InternalResetSession() must be proof for being called more than
1285 //   once in a row.
1286 // - InternalResetSession() must also be callable from destructor
1287 //   (care not to call other objects which will refer to the already
1288 //   half-destructed session!)
1289 void TSyncSession::InternalResetSessionEx(bool terminationCall)
1290 {
1291   #ifdef SCRIPT_SUPPORT
1292   // call session termination script (ONCE!)
1293   if (terminationCall && !fTerminated) {
1294     TScriptContext::execute(
1295       fSessionScriptContextP,
1296       getSessionConfig()->fSessionFinishScript,
1297       NULL, // context's function table
1298       NULL // datastore pointer needed for context
1299     );
1300   }
1301   #endif
1302
1303   // reset sync and datastores
1304   // - version not known in advance
1305   fSyncMLVersion=syncml_vers_unknown;
1306   // - immediately abort SYNC command in progress
1307   fLocalSyncDatastoreP = NULL;
1308   #ifndef NO_REMOTE_RULES
1309   // - no remote rules applied
1310   fActiveRemoteRules.clear();
1311   #endif
1312   // - set defaults for >=SyncML 1.1 features
1313   fRemoteWantsNOC = false; // no, unless requested
1314   fRemoteCanHandleUTC = false; // assume remote can not handle UTC time (note that for SyncML 1.0 this will be set to true later)
1315   fRemoteSupportsLargeObjects = false; // no large object support by default
1316   // - default options
1317   fTreatRemoteTimeAsLocal = false; // do not ignore time zone information from remote
1318   fTreatRemoteTimeAsUTC = false; // do not ignore time zone information from remote
1319   fIgnoreDevInfMaxSize = false; // do not ignore <maxsize> specification in CTCap
1320   fIgnoreCTCap = false; // do not ignore CTCap
1321   fDSPathInDevInf = true; // newer Nokias need this, as they expect the same path in devInf as they sent in alert
1322   fDSCgiInDevInf = true; // newer Nokias need this, as they expect the same path AND CGI in devInf as they sent in alert
1323   fReadOnly = false; // always disabled unless set by SessionLogin()
1324   // - init user time zone from setting. May be modified later using SETUSERTIMEZONE()
1325   fUserTimeContext = getSessionConfig()->fUserTimeContext;
1326   #ifdef SYDEBUG
1327   fSessionLogger.setEnabled(getRootConfig()->fDebugConfig.fSessionDebugLogs); // get default value
1328   #endif
1329   #ifndef MINIMAL_CODE
1330   fLogEnabled = getSessionConfig()->fLogEnabled;
1331   #endif
1332   #ifdef SCRIPT_SUPPORT
1333   // retain session variables if InternalResetSessionEx() is called more than once in the same session
1334   // (which is normal procedure in clients, where SelectProfile calls ResetSession)
1335   // Note: fSessionScriptContextP will be deleted in the destructor
1336   if (!fSessionScriptContextP) {
1337     if (!terminationCall && !fTerminated) {
1338       // prepare session-level scripts
1339       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSessionInitScript,fSessionScriptContextP,this);
1340       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSentItemStatusScript,fSessionScriptContextP,this);
1341       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fReceivedItemStatusScript,fSessionScriptContextP,this);
1342       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fSessionFinishScript,fSessionScriptContextP,this);
1343       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomGetHandlerScript,fSessionScriptContextP,this);
1344       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomGetPutScript,fSessionScriptContextP,this);
1345       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomEndPutScript,fSessionScriptContextP,this);
1346       TScriptContext::rebuildContext(getSyncAppBase(),getSessionConfig()->fCustomPutResultHandlerScript,fSessionScriptContextP,this,true); // now instantiate vars
1347     }
1348   }
1349   #endif
1350   /* %%% moved to ResetSession() because this might not be called from
1351          destructor as datastores might refer to derived session which is
1352          already destructed then
1353   // - Note: datastores may not be cleared here, only reset
1354   TerminateDatastores();
1355   */
1356   // - remove all remote datastores
1357   TRemoteDataStorePContainer::iterator pos1;
1358   for (pos1=fRemoteDataStores.begin(); pos1!=fRemoteDataStores.end(); ++pos1) {
1359     delete *pos1;
1360   }
1361   fRemoteDataStores.clear(); // clear list
1362   // - remove all remote itemtypes
1363   TSyncItemTypePContainer::iterator pos2;
1364   for (pos2=fRemoteItemTypes.begin(); pos2!=fRemoteItemTypes.end(); ++pos2) {
1365     delete *pos2;
1366   }
1367   fRemoteItemTypes.clear(); // clear list
1368   // reset basics
1369   SYSYNC_TRY {
1370     // empty command queues
1371     TSmlCommandPContainer::iterator pos;
1372     // - commands waiting for status
1373     for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
1374       #ifdef SYDEBUG
1375       TSmlCommand *cmdP = *pos;
1376       // show that command was not answered
1377       PDEBUGPRINTFX(DBG_PROTO,("Never received status for &html;<a name=\"SO_%ld_%ld\" href=\"#IO_%ld_%ld\">&html;command '%s'&html;</a>&html;, (outgoing MsgID=%ld, CmdID=%ld)",
1378         (long)cmdP->getMsgID(),
1379         (long)cmdP->getCmdID(),
1380         (long)cmdP->getMsgID(),
1381         (long)cmdP->getCmdID(),
1382         cmdP->getName(),
1383         (long)cmdP->getMsgID(),
1384         (long)cmdP->getCmdID()
1385       ));
1386       #endif
1387       // delete, if this is not the interrupted command.
1388       // Note: only the interrupted command can also be in the status queue.
1389       //       Other queues are exclusive owners of their commands.
1390       if (*pos != fInterruptedCommandP)
1391         delete *pos;
1392       else
1393         DEBUGPRINTF(("- prevented deleting because command is interrupted"));
1394     }
1395     fStatusWaitCommands.clear(); // clear list
1396     // - commands waiting for outgoing message to begin
1397     forgetHeaderWaitCommands();
1398     // - commands to be issued only after all commands in this message have
1399     //   been processed and answered by a status
1400     for (pos=fEndOfMessageCommands.begin(); pos!=fEndOfMessageCommands.end(); ++pos) {
1401       // show that command was not sent
1402       DEBUGPRINTF(("Never sent end-of-message command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
1403         (*pos)->getName(),
1404         (long)(*pos)->getMsgID(),
1405         (long)(*pos)->getCmdID()
1406       ));
1407       // delete
1408       delete *pos;
1409     }
1410     fEndOfMessageCommands.clear(); // clear list
1411     // - commands to be sent in next message
1412     for (pos=fNextMessageCommands.begin(); pos!=fNextMessageCommands.end(); ++pos) {
1413       // show that command was not sent
1414       DEBUGPRINTF(("Never sent next-message command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
1415         (*pos)->getName(),
1416         (long)(*pos)->getMsgID(),
1417         (long)(*pos)->getCmdID()
1418       ));
1419       // delete
1420       delete *pos;
1421     }
1422     fNextMessageCommands.clear(); // clear list
1423     // - commands to be sent in next package
1424     for (pos=fNextPackageCommands.begin(); pos!=fNextPackageCommands.end(); ++pos) {
1425       // show that command was not sent
1426       DEBUGPRINTF(("Never sent next-package command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
1427         (*pos)->getName(),
1428         (long)(*pos)->getMsgID(),
1429         (long)(*pos)->getCmdID()
1430       ));
1431       // delete
1432       delete *pos;
1433     }
1434     fNextPackageCommands.clear(); // clear list
1435     // - interrupted command
1436     if (fInterruptedCommandP) {
1437       // show that command was not sent
1438       DEBUGPRINTF(("Never finished interrupted command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
1439         fInterruptedCommandP->getName(),
1440         (long)fInterruptedCommandP->getMsgID(),
1441         (long)fInterruptedCommandP->getCmdID()
1442       ));
1443       delete fInterruptedCommandP;
1444       fInterruptedCommandP=NULL;
1445     }
1446     // - commands to be executed again at beginning of next message
1447     fDelayedExecSyncEnds=0;
1448     for (pos=fDelayedExecutionCommands.begin(); pos!=fDelayedExecutionCommands.end(); ++pos) {
1449       // show that command was not sent
1450       DEBUGPRINTF(("Never finished executing command '%s', (incoming MsgID=%ld, CmdID=%ld)",
1451         (*pos)->getName(),
1452         (long)(*pos)->getMsgID(),
1453         (long)(*pos)->getCmdID()
1454       ));
1455       // delete
1456       delete *pos;
1457     }
1458     fDelayedExecutionCommands.clear(); // clear list
1459     #ifdef SYNCSTATUS_AT_SYNC_CLOSE
1460     // make sure sync status is disposed
1461     if (fSyncCloseStatusCommandP) delete fSyncCloseStatusCommandP;
1462     fSyncCloseStatusCommandP=NULL;
1463     #endif
1464     // remove incomplete data command
1465     if (fIncompleteDataCommandP) delete fIncompleteDataCommandP;
1466     fIncompleteDataCommandP=NULL;
1467   }
1468   SYSYNC_CATCH (exception &e)
1469     #ifdef SYDEBUG
1470     DEBUGPRINTFX(DBG_ERROR,(
1471       "WARNING: Exception during InternalResetSession(): %s",
1472       e.what()
1473     ));
1474     #endif
1475   SYSYNC_ENDCATCH
1476   SYSYNC_CATCH (...)
1477     #ifdef SYDEBUG
1478     DEBUGPRINTFX(DBG_ERROR,(
1479       "WARNING: Unknown Exception during InternalResetSession()"
1480     ));
1481     #endif
1482   SYSYNC_ENDCATCH
1483   // remember time of creation or last reset
1484   SessionUsed();
1485   // reset session status
1486   fIncomingState=psta_idle; // no incoming package status yet
1487   fCmdIncomingState=psta_idle;
1488   fOutgoingState=psta_idle; // no outgoing package status yet
1489   fNextMessageRequests=0; // no pending next message requests
1490   fFakeFinalFlag=false; // special flag to work around broken resume implementations
1491   fNewOutgoingPackage=true; // first message will be first in outgoing package
1492   fSessionAuthorized=false; // session not permanently authorized
1493   fMessageAuthorized=false; // message not authorized either
1494   fAuthFailures=0; // no failed attempts by remote so far
1495   fAuthRetries=0; // no failed attempts by myself so far
1496   fIncomingMsgID=0; // expected session to start with MsgID=1, so must be 0 now as it will be incremented at StartMessage()
1497   fOutgoingMsgID=0; // starting answers with MsgID=1, so must be 0 as it will be incremented before sending a new message
1498   fAborted=false; // not yet aborted
1499   fSuspended=false; // not being suspended yet
1500   fSuspendAlertSent=false; // no suspend alert sent so far
1501   fFailedDatastores=0; // none failed
1502   fErrorItemDatastores=0; // none generated or detected error items
1503   fInProgress=false; // not yet in progress
1504   fOutgoingStarted=false; // no outgoing message started yet
1505   fSequenceNesting=0; // no sequence command open
1506   fMaxOutgoingMsgSize=0; // no limit for outgoing messages so far
1507   fMaxOutgoingObjSize=0; // SyncML 1.1: no limit for outgoing objects so far
1508   fOutgoingMessageFull=false; // limit not yet reached
1509   // init special remote-dependent behaviour
1510   fLimitedRemoteFieldLengths=false; // assume remote has not generally limited field lenghts
1511   fDontSendEmptyProperties=false; // normally, empty properties will be sent
1512   fDoQuote8BitContent=false;
1513   fNoReplaceInSlowsync=false;
1514   fTreatRemoteTimeAsLocal=false;
1515   fTreatRemoteTimeAsUTC=false;
1516   fIgnoreDevInfMaxSize=false;
1517   fTreatCopyAsAdd=false;
1518   fStrictExecOrdering=true; // SyncML standard requires strict ordering (of statuses, but this implies execution of commands, too)
1519   fDefaultOutCharset=chs_utf8; // SyncML content is usually UTF-8
1520   fDefaultInCharset=chs_utf8; // SyncML content is usually UTF-8
1521   // defaults for possibly remote-dependent behaviour
1522   fCompleteFromClientOnly=getSessionConfig()->fCompleteFromClientOnly; // conform to standard by default
1523   fRequestMaxTime=getSessionConfig()->fRequestMaxTime;
1524   fRequestMinTime=getSessionConfig()->fRequestMinTime;
1525   fUpdateClientDuringSlowsync=getSessionConfig()->fUpdateClientDuringSlowsync;
1526   fUpdateServerDuringSlowsync=getSessionConfig()->fUpdateServerDuringSlowsync;
1527   fAllowMessageRetries=getSessionConfig()->fAllowMessageRetries;
1528   fShowCTCapProps=getSessionConfig()->fShowCTCapProps;
1529   fShowTypeSzInCTCap10=getSessionConfig()->fShowTypeSzInCTCap10;
1530   fVCal10EnddatesSameDay=getSessionConfig()->fVCal10EnddatesSameDay;
1531   fDoNotFoldContent=getSessionConfig()->fDoNotFoldContent;
1532   // tristates!!
1533   fEnumDefaultPropParams=getSessionConfig()->fEnumDefaultPropParams;
1534   #ifdef SCRIPT_SUPPORT
1535   // call session init script
1536   if (!terminationCall && !fTerminated) {
1537     TScriptContext::execute(
1538       fSessionScriptContextP,
1539       getSessionConfig()->fSessionInitScript,
1540       NULL, // context's function table
1541       NULL // datastore pointer needed for context
1542     );
1543   }
1544   #endif
1545 } // TSyncSession::InternalResetSessionEx
1546
1547
1548
1549 // get root config pointer
1550 // NOTE: we have moved this here because Palm linker
1551 //       would have problems accessing it as syncsession.cpp is
1552 //       large enough to have EndMessage >32k away from the
1553 //       original position of this routine.
1554 TRootConfig *TSyncSession::getRootConfig(void)
1555 {
1556   return fSyncAppBaseP->getRootConfig();
1557 } // TSyncSession::getRootConfig
1558
1559
1560 // forget commands waiting to be sent when header is generated
1561 void TSyncSession::forgetHeaderWaitCommands(void)
1562 {
1563   // empty command queues
1564   TSmlCommandPContainer::iterator pos;
1565   // - commands waiting for outgoing message to begin
1566   for (pos=fHeaderWaitCommands.begin(); pos!=fHeaderWaitCommands.end(); ++pos) {
1567     #if SYDEBUG>1
1568     TSmlCommand *cmdP = *pos;
1569     // show that command was not answered
1570     DEBUGPRINTF(("Never sent command '%s', (outgoing MsgID=%ld, CmdID=%ld) because outgoing message never started",
1571       cmdP->getName(),
1572       (long)cmdP->getMsgID(),
1573       (long)cmdP->getCmdID()
1574     ));
1575     #endif
1576     // delete
1577     delete *pos;
1578   }
1579   fHeaderWaitCommands.clear(); // clear list
1580 } // TSyncSession::forgetHeaderWaitCommands
1581
1582
1583 // abort processing commands in this message
1584 void TSyncSession::AbortCommandProcessing(TSyError aStatusCode)
1585 {
1586   if (!fIgnoreIncomingCommands) {
1587     fIgnoreIncomingCommands=true; // do not process further commands
1588     fStatusCodeForIgnored=aStatusCode; // save status code
1589     PDEBUGPRINTFX(DBG_HOT,(
1590       "--------------- Ignoring all commands in this message (after %ld sec. request processing, %ld sec. total) with Status %hd (0=none) from here on",
1591       (long)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor),
1592       (long)((getSystemNowAs(TCTX_UTC)-getSessionStarted()) / (lineartime_t)secondToLinearTimeFactor),
1593       fStatusCodeForIgnored
1594     ));
1595   }
1596 } // TSyncSession::AbortCommandProcessing
1597
1598
1599 // suspend session (that is: flag abortion)
1600 void TSyncSession::SuspendSession(TSyError aReason)
1601 {
1602   // first check for suspend
1603   if (fSyncMLVersion>=syncml_vers_1_2) {
1604     // try suspend, only possible if not already suspended or aborted
1605     if (!fSuspended && !fAborted) {
1606       fSuspended=true; // trigger suspend
1607       fAbortReasonStatus = aReason;
1608       fLocalAbortReason = true;
1609       AbortCommandProcessing(514); // abort command processing, all subsequent commands will be ignored with "cancelled" status
1610       PDEBUGPRINTFX(DBG_ERROR,(
1611         "WARNING: Session locally flagged for suspend, reason=%hd",
1612         aReason
1613       ));
1614       CONSOLEPRINTF((
1615         "Session will be suspended due to local error code %hd\n",
1616         aReason
1617       ));
1618     }
1619   }
1620   else {
1621     // We can't suspend, we need to abort
1622     AbortSession(500,true,aReason);
1623   }
1624 } // TSyncSession::SuspendSession
1625
1626
1627 void TSyncSession::MarkSuspendAlertSent(bool aSent)
1628 {
1629   fSuspendAlertSent = aSent;
1630 }
1631
1632
1633 // abort session (that is: flag abortion)
1634 void TSyncSession::AbortSession(TSyError aStatusCode, bool aLocalProblem, TSyError aReason)
1635 {
1636         // Catch the case that some inner routine, e.g. a plugin, detects a user suspend. It can
1637   // return LOCERR_USERSUSPEND then and will cause the engine instead of aborting.
1638   if (aStatusCode==LOCERR_USERSUSPEND) {
1639         SuspendSession(LOCERR_USERSUSPEND);
1640     return;
1641   }
1642   // make sure session gets aborted
1643   // BUT: do NOT reset yet. Reset would incorrectly abort message answering
1644   if (!fAborted && !fTerminated) {
1645     fAborted=true; // session aborted
1646     fAbortReasonStatus = aReason ? aReason : aStatusCode;
1647     fLocalAbortReason = aLocalProblem;
1648     fInProgress=false; // not in progress any more (will be deleted after end of request)
1649     PDEBUGBLOCKFMT(("SessionAbort","Aborting Session",
1650       "Status=%hd|ProblemSource=%s",
1651       fAbortReasonStatus,
1652       fLocalAbortReason ? "LOCAL" : "REMOTE"
1653     ));
1654     PDEBUGPRINTFX(DBG_ERROR,(
1655       "WARNING: Aborting Session with Reason Status %hd (%s problem) ***",
1656       fAbortReasonStatus,
1657       fLocalAbortReason ? "LOCAL" : "REMOTE"
1658     ));
1659     // In SyncML 1.1, we have a special status code to show that commands are not
1660     // executed any more due to cancellation of the session
1661     if (fSyncMLVersion>=syncml_vers_1_1)
1662       aStatusCode=514; // cancelled, command not completed
1663     AbortCommandProcessing(aStatusCode);
1664     // let all local datastores know
1665     TLocalDataStorePContainer::iterator pos;
1666     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1667       (*pos)->engAbortDataStoreSync(fAbortReasonStatus, fLocalAbortReason);
1668     }
1669     CONSOLEPRINTF((
1670       "Session aborted because of %s SyncML error code %hd\n",
1671       fLocalAbortReason ? "LOCAL" : "REMOTE",
1672       fAbortReasonStatus
1673     ));
1674     OBJ_PROGRESS_EVENT(
1675       getSyncAppBase(),
1676       pev_sessionend,
1677       NULL,
1678       getAbortReasonStatus(),
1679       0,0
1680     );
1681     PDEBUGENDBLOCK("SessionAbort");
1682   }
1683 } // TSyncSession::AbortSession
1684
1685
1686 // returns true if session was a complete success
1687 bool TSyncSession::isAllSuccess(void)
1688 {
1689   return fFailedDatastores==0 && fErrorItemDatastores==0;
1690 } // TSyncSession::isAllSuccess
1691
1692
1693 // let session know that datastore has failed
1694 void TSyncSession::DatastoreFailed(TSyError aStatusCode, bool aLocalProblem)
1695 {
1696   fFailedDatastores++;
1697   // %%% note that this is not perfect for server, as inactive datastores are possible
1698   if (fFailedDatastores>=fLocalDataStores.size()) {
1699     // all have failed by now, abort the session
1700     AbortSession(aStatusCode, aLocalProblem, aStatusCode);
1701   }
1702 } // TSyncSession::DatastoreFailed
1703
1704
1705 // set SyncML toolkit workspace ID
1706 void TSyncSession::setSmlWorkspaceID(InstanceID_t aSmlWorkspaceID)
1707 {
1708   fSmlWorkspaceID=aSmlWorkspaceID;
1709 } // TSyncSession::setSmlWorkspaceID
1710
1711
1712 // show some information about the config
1713 void TSyncSession::DebugShowCfgInfo(void)
1714 {
1715   #ifdef SYDEBUG
1716   string t;
1717   StringObjTimestamp(t,getRootConfig()->fConfigDate);
1718   // now write settings to log
1719   PDEBUGPRINTFX(DBG_HOT,(
1720     "==== Config file='%s', Last Change=%s",
1721     getSyncAppBase()->fConfigFilePath.c_str(),
1722     t.c_str()
1723   ));
1724   #ifndef HARDCODED_CONFIG
1725   PDEBUGPRINTFX(DBG_HOT,(
1726     "==== Config ID string='%s'",
1727     getRootConfig()->fConfigIDString.c_str()
1728   ));
1729   #endif
1730   #endif
1731 } // TSyncSession::DebugShowCfgInfo
1732
1733
1734 #ifndef MINIMAL_CODE
1735
1736 #include <errno.h>
1737
1738 // write to sync log file
1739 void TSyncSession::WriteLogLine(const char *aLogline)
1740 {
1741   if (getSessionConfig()->fLogFileName.empty()) return; // do not write without a path
1742   // open for append
1743   FILE * logfile=fopen(getSessionConfig()->fLogFileName.c_str(),"a");
1744   if (!logfile) {
1745     PDEBUGPRINTFX(DBG_ERROR,("**** Cannot write to logfile '%s' (errno=%ld)",getSessionConfig()->fLogFileName.c_str(),(long)errno));
1746     return; // cannot write
1747   }
1748   // check if we need to write labels first
1749   if (!getSessionConfig()->fLogFileLabels.empty()) {
1750     // check file size
1751     if (ftell(logfile)==0) {
1752       // we are at the beginning, print labels first
1753       fputs(getSessionConfig()->fLogFileLabels.c_str(),logfile);
1754     }
1755   }
1756   // now write log line
1757   fputs(aLogline,logfile);
1758   // close file
1759   fclose(logfile);
1760 } // TSyncSession::WriteLogLine
1761
1762 #endif
1763
1764
1765
1766 // queue a SyncBody context command for issuing after incoming message has ended
1767 void TSyncSession::queueForIssueRoot(
1768   TSmlCommand * &aSyncCommandP                  // the command
1769 )
1770 {
1771   fEndOfMessageCommands.push_back(aSyncCommandP);
1772   aSyncCommandP=NULL;
1773 } // TSyncSession::queueForIssueRoot
1774
1775
1776 // queue a SyncBody context command for issuing after incoming message has ended
1777 void TSyncSession::issueNotBeforePackage(
1778   TPackageStates aPackageState,
1779   TSmlCommand *aSyncCommandP                  // the command
1780 )
1781 {
1782   if (fOutgoingState>=aPackageState) {
1783     issueRootPtr(aSyncCommandP);
1784   }
1785   else {
1786     PDEBUGPRINTFX(DBG_SESSION,("%s queued for next package",aSyncCommandP->getName()));
1787     fNextPackageCommands.push_back(aSyncCommandP);
1788   }
1789 } // TSyncSession::issueNotBeforePackage
1790
1791
1792 // issue a command in SyncBody context (uses session's interruptedCommand/NextMessageCommands)
1793 bool TSyncSession::issueRootPtr(
1794   TSmlCommand *aSyncCommandP,                   // the command
1795   bool aNoResp,                                 // set if no response is wanted
1796   bool aIsOKSyncHdrStatus                       // set if this is sync hdr status
1797 )
1798 {
1799   // now issue
1800   return issuePtr(aSyncCommandP,fNextMessageCommands,fInterruptedCommandP,aNoResp,aIsOKSyncHdrStatus);
1801 } // TSyncSession::issueRootPtr
1802
1803
1804 // issue object passed as pointer (rather than pointer reference)
1805 // normally used internally only
1806 bool TSyncSession::issuePtr(
1807   TSmlCommand *aSyncCommandP,                   // the command
1808   TSmlCommandPContainer &aNextMessageCommands,  // the list to add the command if cannot be issued in this message
1809   TSmlCommand * &aInterruptedCommandP,          // where to store command ptr if it was interrupted
1810   bool aNoResp,                                 // set if no response is wanted
1811   bool aIsOKSyncHdrStatus                       // set if this is sync hdr status
1812 )
1813 {
1814   bool issued=true; // NULL issue is successful issue
1815
1816   if (aSyncCommandP) {
1817     PDEBUGBLOCKFMT(("issue","issuing command",
1818       "Cmd=%s",
1819       aSyncCommandP->getName()
1820     ));
1821     SYSYNC_TRY {
1822       // check for not-to-be-sent commands
1823       if (aSyncCommandP->getDontSend()) {
1824         // command must not be sent, just silently discarded
1825         DEBUGPRINTFX(DBG_PROTO,("%s: not sent because fDontSend is set -> just delete",
1826           aSyncCommandP->getName()
1827         ));
1828         delete aSyncCommandP;
1829         issued=true; // counts as issued
1830         goto endissue;
1831       }
1832       // check if this commmand now triggers need to answer
1833       if (!aIsOKSyncHdrStatus) {
1834         fNeedToAnswer=true;
1835       }
1836       // check if outgoing message has already started. If not, queue command
1837       // for sending when message has started (client case, when status for
1838       // header must be evaluated before next header can be generated)
1839       if (!fOutgoingStarted) {
1840         fHeaderWaitCommands.push_back(aSyncCommandP);
1841         PDEBUGPRINTFX(DBG_SESSION+DBG_EXOTIC,(
1842           "Outgoing message header not yet generated, command '%s' queued",
1843           aSyncCommandP->getName()
1844         ));
1845         issued=true; // act as if issued
1846         goto endissue;
1847       }
1848       // check if this is a continuation of a suspended chunked item transfer
1849       // if yes, substitute the full data we currently have in the item (from
1850       // the database) with the buffered left-overs from the previous
1851       // session.
1852       aSyncCommandP->checkChunkContinuation();
1853       // check if message size restrictions or local buffer size
1854       // will prevent command from being sent now
1855       TSmlCommand *splitCmdP = NULL;
1856       if (!fOutgoingMessageFull) {
1857         #ifndef USE_SML_EVALUATION
1858           #error "This Implementation does not work any more without USE_SML_EVALUATION"
1859         #endif
1860         // check if enough room to send data
1861         sInt32 freeaftersend=aSyncCommandP->evalIssue(
1862           peekNextOutgoingCmdID(), // this will be the ID
1863           getOutgoingMsgID(),
1864           aNoResp
1865         );
1866         // - if room for new commands is smaller than expected message size
1867         sInt32 maxfree=getSmlWorkspaceFreeBytes();
1868         sInt32 sizetoend=maxfree-freeaftersend;
1869         #ifdef SYDEBUG
1870         PDEBUGPRINTFX(DBG_SESSION+DBG_EXOTIC,(
1871           "Command '%s': is %hd-th counted cmd, cmdsize(+tags needed to end msg)=%ld, available=%ld (maxfree=%ld, freeaftersend=%ld, notUsableBufferBytes()=%ld)",
1872           aSyncCommandP->getName(),
1873           fOutgoingCmds,
1874           (long)sizetoend, // size of command (+tags needed to end msg)
1875           (long)(maxfree-getNotUsableBufferBytes()),
1876           (long)maxfree,
1877           (long)freeaftersend,
1878           (long)getNotUsableBufferBytes()
1879         ));
1880         #endif
1881         #ifndef MINIMAL_CODE
1882         // - check for artifical debug chunking
1883         if (getSessionConfig()->fDebugChunkMaxSize && aSyncCommandP->isSyncOp()) {
1884           // always chunk commands over the configured size
1885           uInt32 cmdSize=getSmlWorkspaceFreeBytes()-freeaftersend;
1886           if (cmdSize>getSessionConfig()->fDebugChunkMaxSize) {
1887             // simulate a different free after send to force chunking
1888             sInt32 nfas=getNotUsableBufferBytes()-(cmdSize-getSessionConfig()->fDebugChunkMaxSize)+100;
1889             if (nfas<freeaftersend) {
1890               freeaftersend=nfas;
1891               PDEBUGPRINTFX(DBG_ERROR,("Attention: Debug Chunking enabled, freeaftersend adjusted to %ld",(long)freeaftersend));
1892             }
1893           }
1894         }
1895         #endif
1896         // - check if we can send this
1897         if (freeaftersend<=getNotUsableBufferBytes()) {
1898           // not enough space in this message
1899           PDEBUGPRINTFX(DBG_PROTO,(
1900             "command is %ld bytes too big to be sent as-is in this message",
1901             (long)(getNotUsableBufferBytes()-freeaftersend)
1902           ));
1903           bool sendable=false;
1904           // - first choice: try to split (available in SyncML 1.1 and later)
1905           if (getSyncMLVersion()>=syncml_vers_1_1) {
1906             // we can use moredata mechanism, try if it works for this command
1907             if (fOutgoingCmds>0 && sizetoend < getMaxOutgoingSize()/4) {
1908               // this is a small command, and not in best position
1909               // - command will most likely fit into next message, so end this message now
1910               fOutgoingMessageFull=true;
1911               sendable=true; // kind of "sendable" - as are confident it will be sendable in next message
1912               PDEBUGPRINTFX(DBG_PROTO,("command is less than 1/4 of maxmsgsize -> will likely fit into next message, so queue it"));
1913             }
1914             else {
1915               // - try to split
1916               //   reserve about 200 bytes for Meta Size
1917               splitCmdP = aSyncCommandP->splitCommand(getNotUsableBufferBytes()-freeaftersend+200);
1918               if (splitCmdP) {
1919                 sendable=true;
1920               }
1921               else if (aSyncCommandP->canSplit()) {
1922                 // command is basically splittable, but not right now - end message now and try in next
1923                 fOutgoingMessageFull=true;
1924                 sendable=true; // kind of "sendable" - just not now, but certainly later
1925               }
1926             }
1927           }
1928           // - second choice: try to shrink (mainly devInf)
1929           if (!sendable) {
1930             // gets sendable if we can shrink it to given size
1931             sendable = aSyncCommandP->shrinkCommand(getNotUsableBufferBytes()-freeaftersend);
1932             PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,(
1933               "shrinkCommand could %sshrink command by %ld bytes or more (which is needed to send it now)",
1934               sendable ? "" : "NOT ",
1935               (long)(getNotUsableBufferBytes()-freeaftersend)
1936             ));
1937           }
1938           // - third choice: send it later as first command
1939           if (!sendable) {
1940             if (fOutgoingCmds>0 && sizetoend<getMaxOutgoingSize()+300) {
1941               // not best position now, and not plain impossible (i.e. cmd<maxmsgsize+300)
1942               PDEBUGPRINTFX(DBG_PROTO,("command can fit into one message -> queue and hope we'll be able to send it as only command in subsequent message"));
1943               // - command will possibly fit into next message, so end this message now
1944               fOutgoingMessageFull=true;
1945               sendable=true; // kind of "sendable" - as we are hoping it will be sendable later
1946             }
1947             else {
1948               // this WAS already the best possible position to send or really too big
1949               // - we are in trouble, this command is unsendable
1950               PDEBUGPRINTFX(DBG_ERROR,("%s: Warning: command is too big to be sent at all -> discarded/mark for resend",
1951                 aSyncCommandP->getName()
1952               ));
1953               // - try again in next session (data size might be smaller then, or remote might allow larger data at some time)
1954               aSyncCommandP->markForResend();
1955               // - just discard it for now
1956               delete aSyncCommandP;
1957               issued=false; // not issued (but also not queued again).
1958               goto endissue;
1959             }
1960           }
1961           // check if command was made sendable by splitting off a part
1962           if (splitCmdP) {
1963             // - queue command containing remaining data for issuing in next message
1964             aNextMessageCommands.push_back(splitCmdP);
1965             PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Split command, sending a chunk, queued rest of data for sending in next message"));
1966             // - Note: fOutgoingMessageFull is not set here (as first part of cmd must be sent first)
1967             //         but will be set below after issuing first part (due to splitCmdP!=NULL)
1968           }
1969         } // command too large to be sent now
1970       }
1971       // Queue if message is full, or if we have a interrupted command and
1972       // command to be sent is something other than status. This will make sure
1973       // incoming commands get their statuses before message is filled with other
1974       // explicitly generated commands
1975       if (fOutgoingMessageFull || (aInterruptedCommandP && aSyncCommandP->getCmdType()!=scmd_status)) {
1976         // - queue for issuing in next message
1977         aNextMessageCommands.push_back(aSyncCommandP);
1978         PDEBUGPRINTFX(DBG_HOT,(
1979           "No room for issueing in this message, command '%s' queued for next message",
1980           aSyncCommandP->getName()
1981         ));
1982         // - could not be issued, was queued
1983         issued=false;
1984       }
1985       else {
1986         // issue the command
1987         if (splitCmdP) fOutgoingMessageFull=true; // if we are sending a split command, message IS full after that!
1988         bool dodelete=true;
1989         if (aSyncCommandP->issue(getNextOutgoingCmdID(),fOutgoingMsgID,aNoResp)) {
1990           // command expects status and must be kept in list
1991           PDEBUGPRINTFX(DBG_HOT,("%s: issued as (outgoing MsgID=%ld, CmdID=%ld), now queueing for &html;<a name=\"IO_%ld_%ld\" href=\"#SO_%ld_%ld\">&html;status&html;</a>&html;",
1992             aSyncCommandP->getName(),
1993             (long)aSyncCommandP->getMsgID(),
1994             (long)aSyncCommandP->getCmdID(),
1995             (long)aSyncCommandP->getMsgID(),
1996             (long)aSyncCommandP->getCmdID(),
1997             (long)aSyncCommandP->getMsgID(),
1998             (long)aSyncCommandP->getCmdID()
1999           ));
2000           // - queue for status
2001           aSyncCommandP->setWaitingForStatus(true); // increment waiting for status count of this command
2002           fStatusWaitCommands.push_back(aSyncCommandP);
2003           dodelete=false;
2004         }
2005         else {
2006           // command does not expect status and can be deleted if it is finished
2007           PDEBUGPRINTFX(DBG_HOT,("%s: issued as (outgoing MsgID=%ld, CmdID=%ld), not waiting for status",
2008             aSyncCommandP->getName(),
2009             (long)aSyncCommandP->getMsgID(),
2010             (long)aSyncCommandP->getCmdID()
2011           ));
2012         }
2013         // Now as it is issued, count all "real" commands
2014         if (!aIsOKSyncHdrStatus) {
2015           // count outgoing "real" command, that is NOT SyncHdr OK status nor Alert 222 OK statu
2016           // Note that issuing a container command such as <sync> will have pre-decremented fOutgoingCmds
2017           // to compensate (container should not be counted)
2018           fOutgoingCmds++;
2019         }
2020         // test if finished issuing
2021         if (!aSyncCommandP->finished()) {
2022           // issuing was interrupted, continue at start of next message
2023           aInterruptedCommandP=aSyncCommandP; // remember
2024           dodelete=false;
2025           issued=false;
2026           PDEBUGPRINTFX(DBG_SESSION,("%s: issue not finished -> queued interrupted command",
2027             aSyncCommandP->getName()
2028           ));
2029         }
2030         // - delete if not queued
2031         if (dodelete) {
2032           PDEBUGPRINTFX(DBG_SESSION,("%s: issue finished and not waiting for status -> deleting command",
2033             aSyncCommandP->getName()
2034           ));
2035           delete aSyncCommandP;
2036         }
2037         // message size
2038         PDEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
2039       }
2040     }
2041     SYSYNC_CATCH (...)
2042       // exception during command issuing
2043       // - make sure command is deleted (as issue owns it now)
2044       delete aSyncCommandP;
2045       // make sure session gets aborted
2046       AbortSession(500,true); // local problem
2047       // close block
2048       PDEBUGENDBLOCK("issue");
2049       // re-throw
2050       SYSYNC_RETHROW;
2051     SYSYNC_ENDCATCH
2052   endissue:
2053     PDEBUGENDBLOCK("issue");
2054   } // if something to issue at all
2055   else {
2056     DEBUGPRINTFX(DBG_SESSION,("issuePtr called with NULL command"));
2057   }
2058   // return true if completely issued, false if interrupted or not issued at all
2059   return issued;
2060 } // TSyncSession::issuePtr
2061
2062
2063 // returns true if given number of bytes are transferable
2064 // (not exceeding MaxMsgSize (in SyncML 1.0) or MaxObjSize (SyncML 1.1 and later)
2065 bool TSyncSession::dataSizeTransferable(uInt32 aDataBytes)
2066 {
2067   if (fSyncMLVersion<syncml_vers_1_1 || !fRemoteSupportsLargeObjects) {
2068     // SyncML 1.0: Data is transferable only if it is not more
2069     // than the room we had when the first syncop command in the message
2070     // was being sent
2071     return aDataBytes<=uInt32(fMaxRoomForData);
2072   }
2073   else {
2074     // SyncML 1.1 and later: Data is transferable if it is not more than
2075     // the MaxObjSize (if defined at all)
2076     return aDataBytes<=uInt32(fMaxOutgoingObjSize) || fMaxOutgoingObjSize==0;
2077   }
2078 } // TSyncSession::dataSizeTransferable
2079
2080
2081
2082 #ifndef USE_SML_EVALUATION
2083   #error "This implementation requires USE_SML_EVALUATION"
2084 #endif
2085
2086 // get how many bytes may not be used in the outgoing message buffer
2087 // because of maxMsgSize restrictions
2088 sInt32 TSyncSession::getNotUsableBufferBytes(void)
2089 {
2090   if (fMaxOutgoingMsgSize) {
2091     // there is a limit
2092     sInt32 leavefree =
2093       getSmlWorkspaceFreeBytes() // what is free now
2094       + fOutgoingMsgSize // + what is already used = totally available
2095       - fMaxOutgoingMsgSize; // - max number to send = what we must leave free
2096     // if available space is less than what we may send, there's no need
2097     // to leave anything free.
2098     return leavefree > 0 ? leavefree : 0;
2099   }
2100   else {
2101     // limited only by workspace itself: no bytes need to be left free
2102     return 0;
2103   }
2104 } // TSyncSession::getNotUsableBufferBytes
2105
2106
2107 // get max size outgoing message may have (either defined by remote's maxmsgsize or local buffer space)
2108 sInt32 TSyncSession::getMaxOutgoingSize(void)
2109 {
2110   return fMaxOutgoingMsgSize>0 ? fMaxOutgoingMsgSize : getSmlWorkspaceFreeBytes();
2111 } // TSyncSession::getMaxOutgoingSize
2112
2113
2114 /// @brief mark all pending items for a datastore for resume
2115 ///   (those items that are in a session queue for being issued or getting status)
2116 void TSyncSession::markPendingForResume(TLocalEngineDS *aForDatastoreP)
2117 {
2118   TSmlCommandPContainer::iterator pos;
2119   // - commands not issued yet because header not yet generated
2120   for (pos=fHeaderWaitCommands.begin(); pos!=fHeaderWaitCommands.end(); ++pos) {
2121     (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
2122   }
2123   // - commands not issued yet because they belong at the end of the message
2124   for (pos=fEndOfMessageCommands.begin(); pos!=fEndOfMessageCommands.end(); ++pos) {
2125     (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
2126   }
2127   // - interrupted and next-message commands
2128   markPendingForResume(fNextMessageCommands,fInterruptedCommandP,aForDatastoreP);
2129   // - next package commands
2130   for (pos=fNextPackageCommands.begin(); pos!=fNextPackageCommands.end(); ++pos) {
2131     (*pos)->markPendingForResume(aForDatastoreP,true); // unsent
2132   }
2133   // - commands waiting for status
2134   for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
2135     (*pos)->markPendingForResume(aForDatastoreP,false); // these are already sent (except if they are waiting for chunk ok 213 status)!
2136   }
2137 } // TSyncSession::markPendingForResume
2138
2139
2140 /// @brief mark all pending items for a datastore for resume
2141 ///   (those items that are in a session queue for being issued or getting status)
2142 void TSyncSession::markPendingForResume(
2143   TSmlCommandPContainer &aNextMessageCommands,
2144   TSmlCommand *aInterruptedCommandP,
2145   TLocalEngineDS *aForDatastoreP
2146 )
2147 {
2148   // - all those that are in an interrupted command
2149   if (aInterruptedCommandP) {
2150     aInterruptedCommandP->markPendingForResume(aForDatastoreP,true); // these are unsent
2151   }
2152   // - all those pending for next message
2153   TSmlCommandPContainer::iterator pos;
2154   for (pos=aNextMessageCommands.begin(); pos!=aNextMessageCommands.end(); ++pos) {
2155     (*pos)->markPendingForResume(aForDatastoreP,true); // these are unsent
2156   }
2157 } // TSyncSession::markPendingForResume
2158
2159
2160 // continue interrupted or prevented issue of root level commands
2161 void TSyncSession::ContinuePackageRoot(void)
2162 {
2163   // check if we have anything to send, and if so, reset the count
2164   if (fNextMessageCommands.size()>0 || fInterruptedCommandP) {
2165     #ifdef SYDEBUG
2166     if (fNextMessageRequests) {
2167       PDEBUGPRINTFX(DBG_PROTO,("Fulfilling %ld Next-Message-Request-Alerts 222 by sending commands now",(long)fNextMessageRequests));
2168     }
2169     #endif
2170     fNextMessageRequests=0; // sent something, request fulfilled
2171   }
2172   ContinuePackage(fNextMessageCommands,fInterruptedCommandP);
2173 } // TSyncSession::ContinuePackageRoot
2174
2175
2176 // continue interrupted or prevented issue in next package
2177 void TSyncSession::ContinuePackage(
2178   TSmlCommandPContainer &aNextMessageCommands,
2179   TSmlCommand * &aInterruptedCommandP
2180 )
2181 {
2182   TSmlCommand *cmdP = aInterruptedCommandP;
2183   // - first restart interrupted command
2184   if (cmdP && !isAborted() && !isSuspending()) {
2185     // first check if interrupted command has received status
2186     if (cmdP->isWaitingForStatus() && getSessionConfig()->fWaitForStatusOfInterrupted) {
2187       // Command to be continued has not yet received status, so wait with continuing it
2188       PDEBUGPRINTFX(DBG_SESSION,(
2189         "Interrupted command '%s' (outgoing MsgID=%ld, CmdID=%ld) is still waiting for status -> do not continue nor send queued commands",
2190         cmdP->getName(),
2191         (long)cmdP->getMsgID(),
2192         (long)cmdP->getCmdID()
2193       ));
2194       // aInterruptedCommandP is still set, prevents sending of queued commands
2195     }
2196     else {
2197       PDEBUGPRINTFX(DBG_SESSION,("Sending command that was interrupted at end of last message"));
2198       // there is an interrupted command, continue it
2199       bool dodelete=true;
2200       // if an interrupted command must be continued, this can't be an OK for SyncHdr
2201       fNeedToAnswer=true;
2202       bool newIssue=false;
2203       // now continue issuing
2204       bool queueForStatus=cmdP->continueIssue(newIssue);
2205       // check if complete re-issuing is needed
2206       if (newIssue) {
2207         // command must be re-issued as if it was a new command
2208         PDEBUGPRINTFX(DBG_SESSION,("%s: continuing command by issuing anew",cmdP->getName()));
2209         // interruption is over for now, IssuePtr will set it again if interrupted again
2210         aInterruptedCommandP=NULL;
2211         // IssuePtr will take care of all needed status queueing and deleting command if finished etc.
2212         issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP);
2213         // never delete, as issuePtr will have done it if needed
2214         dodelete=false;
2215       }
2216       else {
2217         // no complete re-issuing needed, just check if we need to queue for status (again)
2218         if (queueForStatus) {
2219           // command expects status (again?) and must be kept in list
2220           PDEBUGPRINTFX(DBG_SESSION,("%s: continued, now queueing for status (again) as (outgoing MsgID=%ld, CmdID=%ld)",
2221             cmdP->getName(),
2222             (long)cmdP->getMsgID(),
2223             (long)cmdP->getCmdID()
2224           ));
2225           // - queue for status (note that every SyncML 1.1 chunk wants to see a status 213 in any case!)
2226           if (!cmdP->isWaitingForStatus()) {
2227             // only put to the queue again if not already waiting there
2228             fStatusWaitCommands.push_back(cmdP);
2229             cmdP->setWaitingForStatus(true);
2230           }
2231           else {
2232             // already waiting, so it's already in the queue - do not push it again
2233             PDEBUGPRINTFX(DBG_SESSION,("%s: continued command was already waiting for status, do not push again", cmdP->getName()));
2234           }
2235           dodelete=false;
2236         }
2237       }
2238       // test if completely issued now
2239       if (!cmdP->finished()) {
2240         // issuing was again interrupted, continue at start of next message
2241         // - keep aInterruptedCommandP pointer unchanged
2242         dodelete=false;
2243         PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue not finished -> queued interrupted command again",
2244           cmdP->getName()
2245         ));
2246       }
2247       else {
2248         // no interrupted command any more
2249         aInterruptedCommandP=NULL;
2250       }
2251       // delete if not queued
2252       if (dodelete) {
2253         PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue finished and not waiting for status -> deleting command",
2254           cmdP->getName()
2255         ));
2256         delete cmdP;
2257       }
2258     } // if status was received for interrupted command
2259   } // if interrupted command
2260   // send commands from queue (until interrupted again)
2261   #ifdef SYDEBUG
2262   if (!isAborted() && !isSuspending()  && !aInterruptedCommandP && aNextMessageCommands.size()>0) {
2263     PDEBUGPRINTFX(DBG_SESSION,("Sending %ld commands that didn't make it into last message",(long)aNextMessageCommands.size()));
2264   }
2265   #endif
2266   TSmlCommandPContainer::iterator pos;
2267   while (!isAborted() && !isSuspending() && !aInterruptedCommandP && !outgoingMessageFull()) {
2268     // first in list
2269     pos=aNextMessageCommands.begin();
2270     if (pos==aNextMessageCommands.end()) break; // done
2271     // take command out of the list
2272     cmdP=(*pos);
2273     aNextMessageCommands.erase(pos);
2274     // issue it (without luck, might land in the queue again --> %%% endless retry??)
2275     if (!issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) break;
2276   }
2277 } // TSyncSession::ContinuePackage
2278
2279
2280
2281 // create, send and delete SyncHeader "command"
2282 void TSyncSession::issueHeader(bool aNoResp)
2283 {
2284   #ifdef SYDEBUG
2285   // Start output translation before issuing outgoing header
2286   XMLTranslationOutgoingStart();
2287   #endif
2288   if (IS_CLIENT) {
2289           #ifdef SYDEBUG
2290     // for client, document exchange starts with outgoing message
2291     // but for server, SyncML_Outgoing is started before SyncML_Incoming, as SyncML_Incoming ends first
2292     PDEBUGBLOCKDESC("SyncML_Outgoing","start of new outgoing message");
2293     PDEBUGPRINTFX(DBG_HOT,("=================> Started new outgoing message"));
2294     #endif
2295     #ifdef EXPIRES_AFTER_DATE
2296     // set 1/4 of the date here
2297     fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
2298     #endif
2299   }
2300   // create and send response header
2301   TSyncHeader *syncheaderP;
2302   MP_NEW(syncheaderP,DBG_OBJINST,"TSyncHeader",TSyncHeader(this,aNoResp));
2303   PDEBUGBLOCKFMT(("SyncHdr","SyncHdr generation","SyncMLVers=%s|OutgoingMsgID=%ld",SyncMLVerDTDNames[fSyncMLVersion],(long)fOutgoingMsgID));
2304   SYSYNC_TRY {
2305     // Note: do not use session's issue(), as this is designed for real commands, not headers
2306     if (syncheaderP->issue(0,fOutgoingMsgID)) {
2307       // - queue for status
2308       PDEBUGPRINTFX(DBG_PROTO,("SyncHdr: issued in MsgID=%ld, now queueing for status",(long)syncheaderP->getMsgID()));
2309       syncheaderP->setWaitingForStatus(true);
2310       fStatusWaitCommands.push_back(syncheaderP);
2311     }
2312     else {
2313       PDEBUGPRINTFX(DBG_PROTO,("SyncHdr: issued in MsgID=%ld, now deleting",(long)syncheaderP->getMsgID()));
2314       delete syncheaderP; // not used any more
2315     }
2316     DEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
2317     // - init number of commands in message
2318     fOutgoingCmds=0;
2319     // - now issue all commands that could not yet be sent because outgoing
2320     //   message was not started
2321     #ifdef SYDEBUG
2322     if (fHeaderWaitCommands.size()>0) {
2323       PDEBUGPRINTFX(DBG_SESSION,("Sending %ld commands that were queued because header was not yet generated",(long)fHeaderWaitCommands.size()));
2324     }
2325     #endif
2326     TSmlCommandPContainer::iterator pos;
2327     while (!fAborted) {
2328       // first in list
2329       pos=fHeaderWaitCommands.begin();
2330       if (pos==fHeaderWaitCommands.end()) break; // done
2331       // take command out of the list
2332       TSmlCommand *cmdP=(*pos);
2333       fHeaderWaitCommands.erase(pos);
2334       if (!cmdP) {
2335         DEBUGPRINTFX(DBG_ERROR,("There was a NULL command in the fHeaderWaitCommands queue?!?"));
2336         continue;
2337       }
2338       // issue now (as if all were OK for synchdr, because fNeedToAnswer may not be affected here!)
2339       if (!issuePtr(cmdP,fNextMessageCommands,fInterruptedCommandP,false,true)) {
2340         PDEBUGPRINTFX(DBG_SESSION,("Could not issue queued command"));
2341         break;
2342       }
2343     }
2344     PDEBUGENDBLOCK("SyncHdr");
2345   }
2346   SYSYNC_CATCH (...)
2347     PDEBUGENDBLOCK("SyncHdr");
2348     delete syncheaderP; // not used any more
2349     SYSYNC_RETHROW; // rethrow
2350   SYSYNC_ENDCATCH
2351 } // TSyncSession::IssueHeader
2352
2353
2354
2355
2356 // process a command (analyze and execute it).
2357 // Ownership of command is passed to process() in all cases.
2358 // Note: This method does not throw exceptions (catches all) and
2359 //       is suitable for being called without further precautions from
2360 //       SyncML toolkit callbacks. Exceptions are translated to
2361 //       smlXXX error codes.
2362 Ret_t TSyncSession::process(TSmlCommand *aSyncCommandP)
2363 {
2364   if (aSyncCommandP) {
2365     SYSYNC_TRY {
2366       // first analyze it (before we can open the block, as we need to find cmdID fisrt)
2367       if (!aSyncCommandP->analyze(fIncomingState)) {
2368         // bad command
2369         PDEBUGPRINTFX(DBG_ERROR,("%s: command failed analyze() -> aborting session",aSyncCommandP->getName()));
2370         AbortSession(400,true); // local problem
2371         delete aSyncCommandP;
2372       }
2373       else {
2374         // command ok so far (has cmdid, so we can refer to it)
2375         PDEBUGBLOCKFMT(("processCmd","Processing incoming command",
2376           "Cmd=%s|IncomingMsgID=%ld|CmdID=%ld",
2377           aSyncCommandP->getName(),
2378           (long)aSyncCommandP->getMsgID(),
2379           (long)aSyncCommandP->getCmdID()
2380         ));
2381         SYSYNC_TRY {
2382           // - test if processing is enabled
2383           if (fIgnoreIncomingCommands && !aSyncCommandP->neverIgnore()) {
2384             // commands must be ignored (fatal error in previous command/header)
2385             PDEBUGPRINTFX(DBG_ERROR,("%s: IGNORED ",aSyncCommandP->getName()));
2386             // - still generate status (except if aborted status is 0, which means silent abort)
2387             //   but DO NOT generate status for status!
2388             if (fStatusCodeForIgnored!=0 && aSyncCommandP->getCmdType()!=scmd_status) {
2389               PDEBUGPRINTFX(DBG_ERROR,("    Sending status %hd for ignored command",fStatusCodeForIgnored));
2390               TStatusCommand *aStatusCmdP = aSyncCommandP->newStatusCommand(fStatusCodeForIgnored);
2391               issueRootPtr(aStatusCmdP);
2392             }
2393             // - delete unexecuted
2394             delete aSyncCommandP;
2395           }
2396           else if (
2397             fStrictExecOrdering &&
2398             fDelayedExecutionCommands.size()>0 &&
2399             aSyncCommandP->getCmdType()!=scmd_status &&
2400             aSyncCommandP->getCmdType()!=scmd_alert
2401           ) {
2402             // some commands have been delayed already -> delay all non-statuses and alerts as well
2403             PDEBUGPRINTFX(DBG_SESSION,("%s: command received after other commands needed to be delayed -> must be delayed, too",aSyncCommandP->getName()));
2404             // - put into delayed execution queue
2405             delayExecUntilNextRequest(aSyncCommandP);
2406           }
2407           else {
2408             // command is ok, execute it
2409             fCmdIncomingState=aSyncCommandP->getPackageState();
2410             if (aSyncCommandP->execute()) {
2411               // execution finished, can be deleted
2412               if (aSyncCommandP->finished()) {
2413                 PDEBUGPRINTFX(DBG_SESSION,("%s: command finished execution -> deleting",aSyncCommandP->getName()));
2414                 delete aSyncCommandP;
2415               }
2416               else {
2417                 PDEBUGPRINTFX(DBG_SESSION,("%s: command NOT finished execution, NOT deleting now",aSyncCommandP->getName()));
2418               }
2419             }
2420             else {
2421               // command has not finished execution, must be retried after next incoming message
2422               PDEBUGPRINTFX(DBG_SESSION,("%s: command wants re-execution later -> queueing",aSyncCommandP->getName()));
2423               // - put into delayed execution queue
2424               delayExecUntilNextRequest(aSyncCommandP);
2425             }
2426           } // if not ignored
2427           // successfully processed
2428           PDEBUGENDBLOCK("processCmd");
2429         } // try
2430         SYSYNC_CATCH (...)
2431           PDEBUGENDBLOCK("processCmd");
2432           SYSYNC_RETHROW;
2433         SYSYNC_ENDCATCH
2434       } // if analyzed successfully
2435     }
2436     SYSYNC_CATCH (TSmlException &e)
2437       // Sml error exception somewhere in command processing
2438       // - make sure command is deleted (as issue owns it now)
2439       delete aSyncCommandP;
2440       PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
2441       // - return SML error that caused this exception
2442       return e.getSmlError();
2443     SYSYNC_ENDCATCH
2444     SYSYNC_CATCH (TSyncException &e)
2445       // sync exception during command processing
2446       // - make sure command is deleted (as issue owns it now)
2447       delete aSyncCommandP;
2448       PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) SyncException: %s, status=%hd",e.what(),e.status()));
2449       // - unspecific SyncML toolkit error, causes session to abort
2450       return SML_ERR_UNSPECIFIC;
2451     SYSYNC_ENDCATCH
2452     SYSYNC_CATCH (exception &e)
2453       // C++ exception during command processing
2454       // - make sure command is deleted (as issue owns it now)
2455       delete aSyncCommandP;
2456       PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) Exception: %s",e.what()));
2457       // - unspecific SyncML toolkit error, causes session to abort
2458       return SML_ERR_UNSPECIFIC;
2459     SYSYNC_ENDCATCH
2460     SYSYNC_CATCH (...)
2461       // other exception during command processing
2462       // - make sure command is deleted (as issue owns it now)
2463       delete aSyncCommandP;
2464       PDEBUGPRINTFX(DBG_ERROR,("WARNING: process(cmd) unknown exception: -> cmd deleted"));
2465       // - unspecific SyncML toolkit error, causes session to abort
2466       return SML_ERR_UNSPECIFIC;
2467     SYSYNC_ENDCATCH
2468   }
2469   // successful
2470   return SML_ERR_OK;
2471 } // TSyncSession::process
2472
2473
2474 // process synchdr (analyze and execute it).
2475 // Ownership of command is passed to process() in all cases.
2476 // Note: May cause session reset if message sequence numbers are not ok.
2477 Ret_t TSyncSession::processHeader(TSyncHeader *aSyncHdrP)
2478 {
2479   if (aSyncHdrP) {
2480     PDEBUGBLOCKDESC("processHdr","Processing incoming SyncHdr");
2481     SYSYNC_TRY {
2482       // init some session vars
2483       // - do not ignore commands by default
2484       fIgnoreIncomingCommands=false;
2485       // - need to answer raises out of first non-synchdr-status command issue()d
2486       fNeedToAnswer=false; // no need yet
2487       // - initialize message status
2488       fMsgNoResp=false; // default to response for messages
2489       fIncomingMsgID++; // count this incoming message
2490       // init flag
2491       bool tryagain=false;
2492       do {
2493         // first analyze header
2494         if (!aSyncHdrP->analyze(fIncomingState)) {
2495           // bad command
2496           PDEBUGPRINTFX(DBG_ERROR,("%s: failed analyze() -> deleting",aSyncHdrP->getName()));
2497           delete aSyncHdrP;
2498         }
2499         else {
2500           // command is ok, execute it
2501           fCmdIncomingState=aSyncHdrP->getPackageState();
2502           PDEBUGBLOCKFMT(("SyncHdr","Processing incoming SyncHdr",
2503             "IncomingMsgID=%ld",
2504             (long)fIncomingMsgID
2505           ));
2506           SYSYNC_TRY {
2507             if (aSyncHdrP->execute()) {
2508               // show session info after processing header
2509               #ifdef SYDEBUG
2510               PDEBUGPRINTFX(DBG_HOT,("Incoming SyncHdr processed, incomingMsgID=%ld, SyncMLVers=%s",(long)fIncomingMsgID,SyncMLVerDTDNames[fSyncMLVersion]));
2511               PDEBUGPRINTFX(DBG_HOT,("- Session ID='%s'",fSynchdrSessionID.c_str()));
2512               PDEBUGPRINTFX(DBG_HOT,("- Source (Remote party): URI='%s' DisplayName='%s'",fRemoteURI.c_str(),fRemoteName.c_str()));
2513               PDEBUGPRINTFX(DBG_HOT,("- Response to be sent to URI='%s'",fRespondURI.empty() ? "[none specified, back to source]" : fRespondURI.c_str()));
2514               PDEBUGPRINTFX(DBG_HOT,("- Target (Local party) : URI='%s' DisplayName='%s'",fLocalURI.c_str(),fLocalName.c_str()));
2515               #endif
2516               CONSOLEPRINTF(("> SyncML message #%ld received from '%s'",fIncomingMsgID,fRemoteURI.c_str()));
2517               // - UTC support is implied for SyncML 1.0 (as most devices support it, and
2518               //   there is was no way to signal it in 1.0).
2519               if (!fRemoteDevInfKnown && fSyncMLVersion==syncml_vers_1_0) fRemoteCanHandleUTC=true;
2520               // execution finished, can be deleted
2521               PDEBUGPRINTFX(DBG_SESSION,("%s: finished execution -> deleting",aSyncHdrP->getName()));
2522               delete aSyncHdrP;
2523               // now execute delayed commands (before executing new ones)
2524               PDEBUGPRINTFX(DBG_SESSION,("New message: Executing %ld delayed commands",(long)fDelayedExecutionCommands.size()));
2525               TSmlCommandPContainer::iterator pos=fDelayedExecutionCommands.begin();
2526               bool syncEndAfterSyncPackageEnd=false;
2527               while (pos!=fDelayedExecutionCommands.end()) {
2528                 // execute again
2529                 TSmlCommand *cmdP = (*pos);
2530                 // command ok so far (has cmdid, so we can refer to it)
2531                 PDEBUGBLOCKFMT(("executeDelayedCmd","Re-executing command from delayed queue",
2532                   "Cmd=%s|IncomingMsgID=%ld|CmdID=%ld",
2533                   cmdP->getName(),
2534                   (long)cmdP->getMsgID(),
2535                   (long)cmdP->getCmdID()
2536                 ));
2537                 SYSYNC_TRY {
2538                   fCmdIncomingState=cmdP->getPackageState();
2539                   if (cmdP->execute()) {
2540                     // check if this was a syncend which was now executed AFTER the end of the incoming sync package
2541                     if (cmdP->getCmdType()==scmd_syncend) {
2542                       fDelayedExecSyncEnds--; // count executed syncend
2543                       if (cmdP->getPackageState()!=fIncomingState)
2544                         syncEndAfterSyncPackageEnd=true; // remember that we had at least one
2545                     }
2546                     // execution finished, can be deleted
2547                     PDEBUGPRINTFX(DBG_SESSION,("%s: command finished execution -> deleting",cmdP->getName()));
2548                     delete cmdP;
2549                     // delete from queue and get next
2550                     pos=fDelayedExecutionCommands.erase(pos);
2551                   }
2552                   else {
2553                     // command has not finished execution, must be retried after next incoming message
2554                     PDEBUGPRINTFX(DBG_SESSION,("%s: command STILL NOT finished execution -> keep it (and all follwoing) in queue ",cmdP->getName()));
2555                     // keep this and all subsequent commands in the queue
2556                     PDEBUGENDBLOCK("executeDelayedCmd");
2557                     break;
2558                   }
2559                   PDEBUGENDBLOCK("executeDelayedCmd");
2560                 } // try
2561                 SYSYNC_CATCH (...)
2562                   PDEBUGENDBLOCK("executeDelayedCmd");
2563                   SYSYNC_RETHROW;
2564                 SYSYNC_ENDCATCH
2565               }
2566               // check if all delayed commands are executed now
2567               if (fDelayedExecSyncEnds<=0 && syncEndAfterSyncPackageEnd) {
2568                 // there was at least one queued syncend executed AFTER end of incoming sync package
2569                 // This means that we must finalize the sync-from-remote phase for the datastores here
2570                 // (as it was suppressed when the incoming sync package had ended)
2571                 TLocalDataStorePContainer::iterator dspos;
2572                 for (dspos=fLocalDataStores.begin(); dspos!=fLocalDataStores.end(); ++dspos) {
2573                   (*dspos)->engEndOfSyncFromRemote(true);
2574                 }
2575               }
2576               else {
2577                 PDEBUGPRINTFX(DBG_SESSION,("%ld delayed commands could not yet be executed and are left in the queue for next message",(long)fDelayedExecutionCommands.size()));
2578               }
2579               // now issue next package commands if any
2580               if (fNewOutgoingPackage) {
2581                 PDEBUGPRINTFX(DBG_SESSION,("New package: Sending %ld commands that were generated earlier for this package",(long)fNextPackageCommands.size()));
2582                 TSmlCommandPContainer::iterator nppos;
2583                 for (nppos=fNextPackageCommands.begin(); nppos!=fNextPackageCommands.end(); nppos++) {
2584                   // issue it (might land in NextMessageCommands)
2585                   issueRootPtr((*nppos));
2586                 }
2587                 // done sending next package commands
2588                 fNextPackageCommands.clear(); // clear list
2589               }
2590               // done, don't try again
2591               break;
2592             }
2593             else {
2594               // unexecutable SyncHdr
2595               // - could be resent message
2596               if (fMessageRetried) {
2597                 // simply abort processing, let transport handle this
2598                 PDEBUGENDBLOCK("processHdr");
2599                 return LOCERR_RETRYMSG; // signal retry has happened
2600               }
2601               // - let agent decide what to do (and whether to try again executing the command)
2602               DEBUGPRINTFX(DBG_SESSION,("%s: Cannot be executed properly, trying to recover",aSyncHdrP->getName()));
2603               tryagain = syncHdrFailure(tryagain);
2604             } // synchdr not ok
2605           } // try
2606           SYSYNC_CATCH (...)
2607             PDEBUGENDBLOCK("SyncHdr");
2608             SYSYNC_RETHROW;
2609           SYSYNC_ENDCATCH
2610         }
2611       } while(tryagain);
2612       PDEBUGENDBLOCK("SyncHdr");
2613     }
2614     SYSYNC_CATCH (TSmlException &e)
2615       // Sml error exception somewhere in command processing
2616       // - make sure command is deleted (as issue owns it now)
2617       delete aSyncHdrP;
2618       PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
2619       PDEBUGENDBLOCK("processHdr");
2620       // - return SML error that caused this exception
2621       return e.getSmlError();
2622     SYSYNC_ENDCATCH
2623     SYSYNC_CATCH (exception &e)
2624       // C++ exception somewhere in command processing
2625       // - make sure command is deleted (as issue owns it now)
2626       delete aSyncHdrP;
2627       PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr exception: %s",e.what()));
2628       PDEBUGENDBLOCK("processHdr");
2629       // - return SML error that caused this exception
2630       return SML_ERR_UNSPECIFIC;
2631     SYSYNC_ENDCATCH
2632     SYSYNC_CATCH (...)
2633       // other exception during command processing
2634       // - make sure command is deleted (as issue owns it now)
2635       delete aSyncHdrP;
2636       PDEBUGPRINTFX(DBG_ERROR,("WARNING: synchdr unknown-class exception: -> deleted"));
2637       PDEBUGENDBLOCK("processHdr");
2638       // - unspecific SyncML toolkit error, causes session to abort
2639       return SML_ERR_UNSPECIFIC;
2640     SYSYNC_ENDCATCH
2641   }
2642   // successful
2643   PDEBUGENDBLOCK("processHdr");
2644   return SML_ERR_OK;
2645 } // TSyncSession::processHeader
2646
2647 #ifdef __PALM_OS__
2648 #pragma segment session2
2649 #endif
2650
2651 // %%% integrate Results command here, too (that, is, make a common
2652 //     ancestor for both TStatusCommand and TResultsCommand which
2653 //     is then handled here in common).
2654 // handle a status "command"
2655 // Ownership of status is passed to handleStatus() in all cases.
2656 // Note: This method does not throw exceptions (catches all) and
2657 //       is suitable for being called without further precautions from
2658 //       SyncML toolkit callbacks. Exceptions are translated to
2659 //       smlXXX error codes.
2660 Ret_t TSyncSession::handleStatus(TStatusCommand *aStatusCommandP)
2661 {
2662   if (aStatusCommandP) {
2663     PDEBUGBLOCKDESC("processStatus","Processing incoming Status");
2664     SYSYNC_TRY {
2665       // analyze first
2666       if (!aStatusCommandP->analyze(fIncomingState)) {
2667         // bad command
2668         PDEBUGPRINTFX(DBG_SESSION,("%s: status failed analyze() -> deleting",aStatusCommandP->getName()));
2669         delete aStatusCommandP;
2670       }
2671       else {
2672         bool found=false;
2673         // status is ok, find matching command
2674         TSmlCommandPContainer::iterator pos;
2675         for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
2676           if ((*pos)->matchStatus(aStatusCommandP)) {
2677             PDEBUGPRINTFX(DBG_PROTO,("Found matching command '%s' for Status",(*pos)->getName()));
2678             (*pos)->setWaitingForStatus(false); // has received status
2679             found=true;
2680             if (fIgnoreIncomingCommands) {
2681               // ignore statuses, but remove waiting command from queue
2682               if ((*pos)->finished()) delete (*pos); // unfinished are owned otherwise and must not be deleted
2683               fStatusWaitCommands.erase(pos);
2684               PDEBUGPRINTFX(DBG_SESSION,("Status ignored, command considered done -> deleted"));
2685             }
2686             else {
2687               // let descendants know when we process a required status
2688               if ((*pos)->statusEssential()) {
2689                 essentialStatusReceived();
2690               }
2691               // normally process status
2692               if ((*pos)->handleStatus(aStatusCommandP)) {
2693                 PDEBUGPRINTFX(DBG_SESSION,("Status: processed, removed command '%s' from status wait queue",(*pos)->getName()));
2694                 // done with command, remove from queue
2695                 if ((*pos)->finished()) {
2696                   // - if this is an interrupted command, make sure to remove pointer
2697                   if ((*pos)==fInterruptedCommandP) fInterruptedCommandP=NULL;
2698                   // - delete command itself
2699                   //   NOTE; if not finished, command is owned otherwise and must
2700                   //   persist
2701                   PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status and allows to be deleted",(*pos)->getName()));
2702                   delete (*pos);
2703                 }
2704                 else {
2705                   PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status, but not finished() -> NOT deleted",(*pos)->getName()));
2706                 }
2707                 // - anyway, remove from list
2708                 fStatusWaitCommands.erase(pos);
2709               }
2710               else {
2711                 // command not yet acknowledged, keep in queue
2712                 (*pos)->setWaitingForStatus(true); // is again waiting for a status
2713                 PDEBUGPRINTFX(DBG_SESSION,("(intermediate) Status processed, command kept in queue, not deleted"));
2714               }
2715             } // else normal processing
2716             break; // exit for loop (iterator is not ok any more)
2717           }
2718         } // for
2719         if (!found) {
2720           // no matching command found
2721           PDEBUGPRINTFX(DBG_ERROR,("No command found for status -> ignoring"));
2722         }
2723         // now delete status
2724         delete aStatusCommandP;
2725       } // if analyzed successfully
2726     }
2727     SYSYNC_CATCH (TSmlException &e)
2728       // Sml error exception somewhere in command processing
2729       // - make sure command is deleted (as issue owns it now)
2730       delete aStatusCommandP;
2731       PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus SmlException: %s, smlerr=%hd",e.what(),e.getSmlError()));
2732       PDEBUGENDBLOCK("processStatus");
2733       // - return SML error that caused this exception
2734       return e.getSmlError();
2735     SYSYNC_ENDCATCH
2736     SYSYNC_CATCH (exception &e)
2737       // Sml error exception somewhere in command processing
2738       // - make sure command is deleted (as issue owns it now)
2739       delete aStatusCommandP;
2740       PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus exception: %s",e.what()));
2741       PDEBUGENDBLOCK("processStatus");
2742       // - unspecific SyncML toolkit error, causes session to abort
2743       return SML_ERR_UNSPECIFIC;
2744     SYSYNC_ENDCATCH
2745     SYSYNC_CATCH (...)
2746       // other exception during command processing
2747       // - make sure command is deleted (as issue owns it now)
2748       delete aStatusCommandP;
2749       PDEBUGPRINTFX(DBG_ERROR,("WARNING: handleStatus unknown-class exception: -> deleted"));
2750       PDEBUGENDBLOCK("processStatus");
2751       // - unspecific SyncML toolkit error, causes session to abort
2752       return SML_ERR_UNSPECIFIC;
2753     SYSYNC_ENDCATCH
2754   }
2755   // successful
2756   PDEBUGENDBLOCK("processStatus");
2757   return SML_ERR_OK;
2758 } // TSyncSession::handleStatus
2759
2760
2761 // Session level meta
2762 SmlPcdataPtr_t TSyncSession::newHeaderMeta(void)
2763 {
2764   SmlPcdataPtr_t metaP = NULL;
2765
2766   // create meta for initialisation message only
2767   #ifdef SEND_MAXMSGSIZE_ON_INIT_ONLY
2768   if (fOutgoingState<=psta_init)
2769   #endif
2770   {
2771     metaP=newMeta();
2772     SmlMetInfMetInfPtr_t metinfP = smlPCDataToMetInfP(metaP);
2773     // - add max message size
2774     metinfP->maxmsgsize=newPCDataLong(getRootConfig()->fLocalMaxMsgSize);
2775     if (
2776       (getRootConfig()->fLocalMaxObjSize>0) &&
2777       (fSyncMLVersion>=syncml_vers_1_1)
2778     ) {
2779       // SyncML 1.1 has object size
2780       metinfP->maxobjsize=newPCDataLong(getRootConfig()->fLocalMaxObjSize);
2781     }
2782   }
2783   return metaP;
2784 } // TSyncSession::newHeaderMeta
2785
2786
2787 // create new SyncHdr structure for TSyncHeader command
2788 // (here because all data for this is in session anyway)
2789 // Called exclusively from TSyncHeader command
2790 SmlSyncHdrPtr_t TSyncSession::NewOutgoingSyncHdr(bool aOutgoingNoResp)
2791 {
2792   SmlSyncHdrPtr_t headerP;
2793
2794   MP_SHOWCURRENT(DBG_PROFILE,"Start of outgoing message");
2795   // set response status for entire message
2796   fOutgoingNoResp=aOutgoingNoResp;
2797   // get new number for this message
2798   fOutgoingMsgID++;
2799   // reset message size counting
2800   fOutgoingMsgSize=0;
2801   // reset command ID
2802   fOutgoingCmdID=0;
2803   // now compose Sync Header from session vars
2804   // - create empty header
2805   headerP = SML_NEW(SmlSyncHdr_t);
2806   #ifdef EXPIRES_AFTER_DATE
2807   // prepare for a check, convert back to normal value * 4
2808   sInt32 scramblednow4 = (fCopyOfScrambledNow-503);
2809   #endif
2810   SYSYNC_TRY {
2811     // set proto element type to make it auto-disposable
2812     headerP->elementType=SML_PE_HEADER;
2813     // set version information
2814     headerP->version=newPCDataString(SyncMLVerDTDNames[fSyncMLVersion]);
2815     headerP->proto=newPCDataString(SyncMLVerProtoNames[fSyncMLVersion]);
2816     // set session ID
2817     headerP->sessionID=newPCDataString(fSynchdrSessionID);
2818     // set new message ID for this message
2819     #ifdef APP_CAN_EXPIRE
2820     // check for expiry again
2821     #ifdef EXPIRES_AFTER_DATE
2822     // - has hard expiry date
2823     if (
2824       (scramblednow4>SCRAMBLED_EXPIRY_VALUE*4)
2825       #ifdef SYSER_REGISTRATION
2826       && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
2827       #endif
2828     )
2829       getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
2830     #else
2831     // - no hard expiry date, just check if license is still valid
2832     if (
2833       !getSyncAppBase()->fRegOK || // no registered at all
2834       getSyncAppBase()->fDaysLeft==0 // or expired
2835     )
2836       getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
2837     #endif
2838     #endif
2839     headerP->msgID=newPCDataLong(fOutgoingMsgID);
2840     // flags
2841     headerP->flags=fOutgoingNoResp ? SmlNoResp_f : 0;
2842     // target (URI/Name of Remote party)
2843     // Note: Tsutomu Uenoyama (uenoyama@trl.mei.co.jp) sais in syncml feedback
2844     //       list that server RespURI behaviour should
2845     //       be reflecting received RespURI in target, so we do it:
2846     headerP->target=newLocation(
2847       fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str(),
2848       fRemoteName.c_str()
2849     );
2850     PDEBUGPRINTFX(DBG_PROTO,("Target (Remote URI) = '%s'",fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str()));
2851     // source (URI / User-Name of local party)
2852     // NOTE: The LocName must contain the name of the user and not
2853     //       the name of the device (this is a SyncML 1.0.1 correction
2854     //       to make MD5 auth implementable - we need a clear-text user name)
2855     headerP->source=newLocation(
2856       fLocalURI.c_str(),
2857       getUsernameForRemote() // user name for remote login, NULL if none available
2858     );
2859     // add respURI if local party cannot be responded to via normal URI
2860     // New added for T68i: do not send a RespURI for an aborted session
2861     if (!isAborted() && fMessageAuthorized)
2862       headerP->respURI=newResponseURIForRemote();
2863     else
2864       headerP->respURI=NULL;
2865     // add credentials if remote needs them
2866     headerP->cred=newCredentialsForRemote();
2867     // agent-specific meta
2868     headerP->meta=newHeaderMeta();
2869   }
2870   SYSYNC_CATCH (...)
2871     // make sure header is disposed
2872     smlFreeProtoElement(headerP);
2873     SYSYNC_RETHROW; // re-throw
2874   SYSYNC_ENDCATCH
2875   // return it
2876   return headerP;
2877 } // TSyncSession::NewOutgoingSyncHdr
2878
2879
2880 // delay command for execution at beginning of next received message
2881 void TSyncSession::delayExecUntilNextRequest(TSmlCommand *aCommand)
2882 {
2883   // push into delay queue
2884   fDelayedExecutionCommands.push_back(aCommand);
2885   // a delayed (=not processed) syncstart must clear the current fLocalSyncDatastoreP,
2886   // as it is not yet known for that <sync>. This causes syncops to receive a NULL datastore
2887   // at creation, so they must check for that and get it at execute().
2888   if (aCommand->getCmdType()==scmd_sync) {
2889     // delayed <sync> has no datastore (yet)
2890     fLocalSyncDatastoreP=NULL;
2891   }
2892   else if (aCommand->getCmdType()==scmd_syncend) {
2893     // count delayed syncends as they need special care later
2894     fDelayedExecSyncEnds++;
2895     // and forget current datastore - safety only, should be NULL here anyway
2896     fLocalSyncDatastoreP=NULL;
2897   }
2898 } // TSyncSession::delayExecUntilNextRequest
2899
2900
2901 // remote party requests next message by Alert 222
2902 void TSyncSession::nextMessageRequest(void)
2903 {
2904   // count the request
2905   fNextMessageRequests++;
2906   if (IS_SERVER) {
2907     // check if we have seen many requests but could not fulfil them
2908     if (fNextMessageRequests>3) {
2909       // check for resume that does not send us an empty Sync (Symbian client at TestFest 16)
2910       PDEBUGPRINTFX(DBG_ERROR,("Warning: More than 3 consecutive Alert 222 - looks like endless loop, check if we need to work around client implementation issues"));
2911       // - check datastores
2912       TLocalDataStorePContainer::iterator pos;
2913       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2914         // see if it is currently resuming
2915         TLocalEngineDS *ldsP = (*pos);
2916         if (ldsP->isResuming() && ldsP->getDSState()<dssta_serverseenclientmods) {
2917           // fake empty <sync> from client to get things going again
2918           // - create it
2919           SmlSyncPtr_t fakeSyncCmdP = (SmlSyncPtr_t)smlLibMalloc(sizeof(SmlSync_t));
2920           fakeSyncCmdP->elementType = SML_PE_SYNC_START;
2921           fakeSyncCmdP->cmdID=NULL; // none needed here
2922           fakeSyncCmdP->flags=0; // none
2923           fakeSyncCmdP->cred=NULL;
2924           fakeSyncCmdP->target=newLocation(ldsP->getName()); // client would target myself
2925           fakeSyncCmdP->source=newLocation(ldsP->getRemoteDBPath()); // client would target myself
2926           fakeSyncCmdP->meta=NULL; // no meta
2927           fakeSyncCmdP->noc=NULL; // no NOC
2928           // - have it processed like it was a real command
2929           PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,("Probably client expects resume to continue without sending an empty <Sync> -> simulate one"));
2930           PDEBUGBLOCKFMT((
2931             "Resume_Sim_Sync","Simulated empty sync to get resume going",
2932             "datastore=%s",
2933             ldsP->getName()
2934           ));
2935           TStatusCommand *fakeStatusCmdP = new TStatusCommand(this);
2936           bool queueforlater=false;
2937           /* bool ok= */
2938           processSyncStart(
2939             fakeSyncCmdP,
2940             *fakeStatusCmdP,
2941             queueforlater // will be set if command must be queued for later re-execution
2942           );
2943           if (!queueforlater) {
2944             fNextMessageRequests=0; // reset that counter
2945             // and make sure we advance the sync session state
2946             // - now the real ugly hacking starts - we have to fake receiving a <final/>
2947             fFakeFinalFlag=true;
2948           }
2949           else {
2950             PDEBUGPRINTFX(DBG_ERROR,("simulated <Sync> can't be processed now, we'll try again later"));
2951           }
2952           // now just simulate a </sync>
2953           processSyncEnd(queueforlater);
2954           // now let this particular datastore "know" that sync-from-client is over now
2955           (*pos)->engEndOfSyncFromRemote(true); // fake "final"
2956           PDEBUGENDBLOCK("Resume_Sim_Sync");
2957         }
2958       }
2959     }
2960   } // server
2961 } // TSyncSession::nextMessageRequest
2962
2963
2964
2965
2966 // check if session must continue (for session-level reasons, that
2967 // is without regarding sync state of server or client)
2968 bool TSyncSession::sessionMustContinue(void) {
2969   // if there are delayed commands not yet executed after this message: session must go on
2970   if (!fDelayedExecutionCommands.empty()) {
2971     PDEBUGPRINTFX(DBG_SESSION,(
2972       "%ld commands in delayed-execution-queue -> session must continue",
2973       (long)fDelayedExecutionCommands.size()
2974     ));
2975     return true; // must continue
2976   }
2977   // if no status to wait for: session may be deleted now
2978   if (fStatusWaitCommands.empty()) return false;
2979   TSmlCommandPContainer::iterator pos;
2980   // show them
2981   #ifdef SYDEBUG
2982   for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
2983     TSmlCommand *cmdP = *pos;
2984     // show that command was not answered
2985     PDEBUGPRINTFX(DBG_PROTO,("- Not yet received %sstatus for command '%s', (outgoing MsgID=%ld, CmdID=%ld)",
2986       cmdP->statusEssential() ? "REQUIRED " : "",
2987       cmdP->getName(),
2988       (long)cmdP->getMsgID(),
2989       (long)cmdP->getCmdID()
2990     ));
2991   }
2992   #endif
2993   // check type of commands we miss status for
2994   bool mustgoon=false;
2995   for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
2996     TSmlCommand *cmdP = *pos;
2997     if (cmdP->statusEssential()) {
2998       // we need a status for at least one of these
2999       mustgoon=true;
3000       break;
3001     }
3002   }
3003   // if only one single status to wait for, check if it is
3004   // SyncHdr status; if no, session MUST continue
3005   // (otherwise, remote will not send status for SyncHdr alone)
3006   if (mustgoon) {
3007     PDEBUGPRINTFX(DBG_HOT,("SESSION CANNOT END - Not yet received REQUIRED status for some of %ld commands",(long)fStatusWaitCommands.size()));
3008   }
3009   return mustgoon;
3010 } // TSyncSession::sessionMustContinue
3011
3012
3013 // returns true if session has pending commands
3014 bool TSyncSession::hasPendingCommands(void)
3015 {
3016   return (!(
3017     fNextMessageCommands.size()==0 &&       // ..no commands to send in next message AND
3018     fDelayedExecutionCommands.size()==0 &&  // ..no commands to process in next message AND
3019     fInterruptedCommandP==NULL              // ..no interrupted outgoing commands
3020   ));
3021 } // TSyncSession::hasPendingCommands
3022
3023
3024 // finish outgoing Message, returns true if final message of package
3025 bool TSyncSession::FinishMessage(bool aAllowFinal, bool aForceNonFinal)
3026 {
3027   Ret_t err;
3028   bool final=true;
3029
3030   // finish message if any
3031   if (fOutgoingStarted) {
3032     // there is an unfinished message, finish it
3033     // - final only if no commands waiting for next message
3034     //   and caller allows final
3035     final=
3036       !aForceNonFinal && (
3037         fAborted ||                             // if aborted, this is a final message, OR..
3038         (aAllowFinal && !hasPendingCommands())  // ..(if allowed final AND no pending commands)
3039       ); // ...THEN this is a final package
3040     PDEBUGPRINTFX(DBG_PROTO,(
3041       "Ending message with %s%ld next-message/%ld next-package commands: %sFINAL (%sfinal %sallowed by caller)",
3042       fInterruptedCommandP ? "interrupted command and " : "",
3043       (long)fNextMessageCommands.size(),
3044       (long)fNextPackageCommands.size(),
3045       final ? "" : "NOT ",
3046       fAborted ? "ABORTED, " : "",
3047       aAllowFinal ? "" : "not "
3048     ));
3049     // - close message now
3050     sInt32 bytesbeforeissue=getSmlWorkspaceFreeBytes();
3051     fOutgoingStarted=false; // done now
3052     fOutgoingMessageFull=false; // message finished, not full any more
3053     #ifdef SYDEBUG
3054     if (fXMLtranslate && fOutgoingXMLInstance)
3055       smlEndMessage(fOutgoingXMLInstance,final);
3056     // Now dump XML translation of outgoing message
3057     XMLTranslationOutgoingEnd();
3058     #endif
3059     if ((err=smlEndMessage(fSmlWorkspaceID, final))!=SML_ERR_OK) {
3060       SYSYNC_THROW(TSmlException("smlEndMessage",err));
3061     }
3062     incOutgoingMessageSize(bytesbeforeissue-getSmlWorkspaceFreeBytes());
3063     PDEBUGPRINTFX(DBG_PROTO,("Entire message size is now %ld Bytes",(long)getOutgoingMessageSize()));
3064     // if outgoing message was not final, prevent session end
3065     // except if session is aborted (and final flag is forced nonFinal e.g. when ending session because of serverBusy()
3066     if (!final && !fAborted) fInProgress=true;
3067     CONSOLEPRINTF(("< SyncML message #%ld sent to '%s'",(long)fOutgoingMsgID,fRespondURI.empty() ? fRemoteURI.c_str() : fRespondURI.c_str()));
3068     MP_SHOWCURRENT(DBG_PROFILE,"End of outgoing message");
3069     // dump it if configured
3070     #ifdef SYDEBUG
3071     DumpSyncMLMessage(true); // outgoing
3072     #endif
3073   }
3074   // if this message ends with <Final/>, next message will be in new package
3075   fNewOutgoingPackage=final;
3076   return final;
3077 } // TSyncSession::FinishMessage
3078
3079
3080 // get name of current encoding
3081 const char *TSyncSession::getEncodingName(void)
3082 {
3083   return SyncMLEncodingMIMENames[fEncoding];
3084 } // TSyncSession::getEncodingName
3085
3086
3087 // add current encoding spec to given (type-)string
3088 void TSyncSession::addEncoding(string &aString)
3089 {
3090   aString+=SYNCML_ENCODING_SEPARATOR;
3091   aString+=getEncodingName();
3092 } // TSyncSession::addEncoding
3093
3094
3095 // set encoding for session
3096 void TSyncSession::setEncoding(SmlEncoding_t aEncoding)
3097 {
3098   Ret_t err=smlSetEncoding(fSmlWorkspaceID,aEncoding);
3099   if (err==SML_ERR_OK) {
3100         fEncoding = aEncoding;
3101   }
3102 } // TSyncSession::setEncoding
3103
3104
3105 // find remote datastore by (remote party specified) URI
3106 TRemoteDataStore *TSyncSession::findRemoteDataStore(const char *aDatastoreURI)
3107 {
3108   TRemoteDataStorePContainer::iterator pos;
3109   TRemoteDataStore *bestMatchP=NULL;
3110   uInt16 bestNumMatched=0;
3111   // search for BEST match (most number of chars matched)
3112   for (pos=fRemoteDataStores.begin(); pos!=fRemoteDataStores.end(); ++pos) {
3113     // test for match
3114     if ((*pos)->isDatastore(aDatastoreURI) > bestNumMatched) {
3115       bestMatchP = *pos; // best so far, but check all
3116     }
3117   }
3118   return bestMatchP; // return NULL if no match found or best matching
3119 } // TSyncSession::findRemoteDataStore
3120
3121
3122 // - find local datastore by URI and separate identifying from optional part of URI
3123 TLocalEngineDS *TSyncSession::findLocalDataStoreByURI(const char *aURI,string *aOptions, string *aIdentifyingURI)
3124 {
3125   string dburi;
3126
3127   // - get relative URI of requested database
3128   const char *dblocuri = SessionRelativeURI(aURI);
3129   // In this base class implementation, identification is path, options are CGI
3130   // - separate target address and CGI (if any)
3131   const char *optionsCGI=(const char *)strchr(dblocuri,'?');
3132   if (optionsCGI) {
3133     dburi.assign(dblocuri,optionsCGI-dblocuri);
3134     optionsCGI++; // skip '?'
3135     dblocuri=dburi.c_str();
3136     PDEBUGPRINTFX(DBG_PROTO,("Target Address CGI Options: %s",optionsCGI));
3137     if (aOptions) aOptions->assign(optionsCGI);
3138   }
3139   else {
3140     // no CGI contained
3141     if (aOptions) aOptions->erase();
3142   }
3143   // assign identifying part of URL now
3144   if (aIdentifyingURI) {
3145     aIdentifyingURI->assign(dblocuri);
3146   }
3147   // find datastore now
3148   DEBUGPRINTF(("Determined relative, identifying URI (w/o CGI): %s",dblocuri));
3149   return findLocalDataStore(dblocuri);
3150 } // TSyncSession::findLocalDataStorebyURI
3151
3152
3153 // find local datastore by (relative) URI
3154 TLocalEngineDS *TSyncSession::findLocalDataStore(const char *aDatastoreURI)
3155 {
3156   TLocalDataStorePContainer::iterator pos;
3157   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
3158     // test for match (we do not do best-match search here because these are names
3159     // under our own control that do not contain slashes and hence no
3160     // mismatch possibilities like "/Calendar" and "/Calendar/Events" as
3161     // with Oracle server.
3162     if ((*pos)->isDatastore(aDatastoreURI))
3163       return (*pos); // found
3164   }
3165   return NULL; // none found
3166 } // TSyncSession::findLocalDataStore
3167
3168
3169 // - find local datastore by datastore handle (=config pointer)
3170 TLocalEngineDS *TSyncSession::findLocalDataStore(void *aDSHandle)
3171 {
3172   TLocalDataStorePContainer::iterator pos;
3173   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
3174     // test for match
3175     TLocalEngineDS *ldsP = (*pos);
3176     if ((void *)(ldsP->getDSConfig())==aDSHandle)
3177       return (ldsP); // found
3178   }
3179   return NULL; // none found
3180 } // TSyncSession::findLocalDataStore
3181
3182
3183 TLocalEngineDS *TSyncSession::addLocalDataStore(TLocalDSConfig *aLocalDSConfigP)
3184 {
3185   TLocalEngineDS *ldsP=aLocalDSConfigP->newLocalDataStore(this);
3186   fLocalDataStores.push_back(ldsP);
3187   return ldsP;
3188 } // TSyncSession::addLocalDataStore
3189
3190
3191
3192 // - find local datatype by config pointer (used to avoid duplicating types
3193 //   in session if used by more than a single datastore)
3194 TSyncItemType *TSyncSession::findLocalType(TDataTypeConfig *aDataTypeConfigP)
3195 {
3196   TSyncItemTypePContainer::iterator pos;
3197   for (pos=fLocalItemTypes.begin(); pos!=fLocalItemTypes.end(); ++pos) {
3198     // test for match
3199     if ((void *)((*pos)->getTypeConfig())==aDataTypeConfigP)
3200       return (*pos); // found
3201   }
3202   return NULL; // none found
3203 } // TSyncSession::findLocalType
3204
3205
3206 // - find implemented remote datatype by config pointer (and related datastore, if any)
3207 TSyncItemType *TSyncSession::findRemoteType(TDataTypeConfig *aDataTypeConfigP, TSyncDataStore *aRelatedRemoteDS)
3208 {
3209   TSyncItemTypePContainer::iterator pos;
3210   for (pos=fRemoteItemTypes.begin(); pos!=fRemoteItemTypes.end(); ++pos) {
3211     // test for match
3212     if ((void *)((*pos)->getTypeConfig())==aDataTypeConfigP) {
3213       // match only if related to same datastore or not related
3214       if (aRelatedRemoteDS == (*pos)->getRelatedDatastore())
3215         return (*pos); // found
3216     }
3217   }
3218   return NULL; // none found
3219 } // TSyncSession::findRemoteType
3220
3221
3222
3223
3224
3225 // get new list of all local datastores
3226 SmlDevInfDatastoreListPtr_t TSyncSession::newDevInfDataStoreList(bool aAlertedOnly, bool aWithoutCTCapProps)
3227 {
3228   SmlDevInfDatastoreListPtr_t rootP,*insertpos;
3229   SmlDevInfDatastorePtr_t datastoreP;
3230
3231   // no list at beginning
3232   rootP=NULL;
3233   insertpos = &rootP;
3234
3235   // go through local datastore list
3236   TLocalDataStorePContainer::iterator pos1;
3237   for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
3238     // check if we only want alerted datastore's info (client case)
3239     if (aAlertedOnly) {
3240       if (!(*pos1)->testState(dssta_clientsentalert))
3241         continue; // not alerted, do not show this one
3242     }
3243     // see if we have info at all
3244     datastoreP = (*pos1)->getDatastoreDevinf(IS_SERVER, aWithoutCTCapProps);
3245     if (datastoreP) {
3246       // create new list item
3247       (*insertpos) = SML_NEW(SmlDevInfDatastoreList_t);
3248       (*insertpos)->next = NULL;
3249       (*insertpos)->data = datastoreP;
3250       // set new insert position
3251       insertpos = &((*insertpos)->next);
3252     }
3253   }
3254   return rootP;
3255 } // TSyncSession::newDevInfDataStoreList
3256
3257
3258 // get common sync capabilities mask of this session (datastores might modify it)
3259 uInt32 TSyncSession::getSyncCapMask(void)
3260 {
3261   return
3262     SCAP_MASK_NORMAL |
3263     (getSessionConfig()->fAcceptServerAlerted ? SCAP_MASK_SERVER_ALERTED : 0);
3264 } // TSyncSession::getSyncCapMask
3265
3266
3267 // get new list of all local item types
3268 SmlDevInfCtcapListPtr_t TSyncSession::newLocalCTCapList(bool aAlertedOnly, TLocalEngineDS *aOnlyForDS, bool aWithoutCTCapProps)
3269 {
3270   SmlDevInfCtcapListPtr_t rootP,*insertpos;
3271   SmlDevInfCTCapPtr_t ctcapP;
3272
3273   // no list at beginning
3274   rootP=NULL;
3275   insertpos = &rootP;
3276   bool showTy;
3277   TTypeVariantDescriptor variantDesc = NULL;
3278
3279   // go through local datastore list
3280   // Note: rulematch types should normally not be shown, but the way to do that
3281   //       is not some testing here (which is almost impossible), but profiles
3282   //       defined for rulematch types should be made invisible.
3283   TSyncItemTypePContainer::iterator pos2;
3284   TLocalDataStorePContainer::iterator pos1;
3285   for (pos2=fLocalItemTypes.begin(); pos2!=fLocalItemTypes.end(); ++pos2) {
3286     showTy=true;
3287     // check for restriction to certain datastore
3288     if (aOnlyForDS) {
3289       // only show if specified datastore is using the type
3290       showTy = aOnlyForDS->doesUseType(*pos2, &variantDesc);
3291     }
3292     // see if datatype is used by any of the alerted datastores
3293     if (showTy && aAlertedOnly) {
3294       showTy=false;
3295       for (pos1=fLocalDataStores.begin(); pos1!=fLocalDataStores.end(); ++pos1) {
3296         // check if we only want alerted datastore's info (client case)
3297         if (!(*pos1)->testState(dssta_clientsentalert)) continue; // test next
3298         // see if datatype is used by this datastore
3299         if ((*pos1)->doesUseType(*pos2)) {
3300           showTy=true;
3301           break;
3302         }
3303       }
3304     }
3305     // now show if selected
3306     if (showTy) {
3307       // see if we have info at all
3308       ctcapP = (*pos2)->getCTCapDevInf(aOnlyForDS, variantDesc, aWithoutCTCapProps);
3309       if (ctcapP) {
3310         // create new list item
3311         (*insertpos) = SML_NEW(SmlDevInfCtcapList_t);
3312         (*insertpos)->next = NULL;
3313         (*insertpos)->data = ctcapP;
3314         // set new insert position
3315         insertpos = &((*insertpos)->next);
3316       }
3317     }
3318   }
3319   return rootP;
3320 } // TSyncSession::newLocalCTCapList
3321
3322
3323 // build DevInf of this session
3324 SmlDevInfDevInfPtr_t TSyncSession::newDevInf(bool aAlertedOnly, bool aWithoutCTCapProps)
3325 {
3326   SmlDevInfDevInfPtr_t devinfP;
3327
3328   // Create empty DevInf
3329   devinfP = SML_NEW(SmlDevInfDevInf_t);
3330   // Fill in information for current session
3331   devinfP->verdtd=newPCDataString(SyncMLVerDTDNames[fSyncMLVersion]);
3332   // - identification of this SyncML implementation
3333   devinfP->man=newPCDataOptString(getSyncAppBase()->getManufacturer().c_str());
3334   devinfP->mod=newPCDataOptString(getSyncAppBase()->getModel().c_str());
3335   devinfP->oem=newPCDataOptString(getSyncAppBase()->getOEM());
3336   devinfP->swv=newPCDataOptString(getSyncAppBase()->getSoftwareVersion());
3337   // - identification of the device ID and type (server/client etc.)
3338   devinfP->devid=newPCDataString(getDeviceID().c_str());
3339   devinfP->devtyp=newPCDataString(getDeviceType().c_str());
3340   // - identification of the platform the software runs on
3341   devinfP->hwv=newPCDataOptString(getSyncAppBase()->getHardwareVersion().c_str());
3342   devinfP->fwv=newPCDataOptString(getSyncAppBase()->getFirmwareVersion().c_str());
3343   // Now get info for content capabilities
3344   if (fSyncMLVersion<syncml_vers_1_2) {
3345     // CTCap is global, get it without limitation to a datastore
3346     devinfP->ctcap=newLocalCTCapList(aAlertedOnly, NULL, aWithoutCTCapProps);
3347   }
3348   else
3349     devinfP->ctcap=NULL; // no global CTCap any more at devInf level
3350   // Now get info for datastores
3351   devinfP->datastore=newDevInfDataStoreList(aAlertedOnly, aWithoutCTCapProps);
3352   // SyncML 1.1 related flags
3353   devinfP->flags=0; // no SyncML 1.1 flags by default
3354   if (fSyncMLVersion>=syncml_vers_1_1) {
3355     // - we can always parse number of changes (whether we can make use of it is irrelevant)
3356     devinfP->flags |= SmlDevInfNOfM_f;
3357     // - check if we support UTC based time (implementations with no means to obtain time zone might not)
3358     if (canHandleUTC())
3359       devinfP->flags |= SmlDevInfUTC_f; // we can handle UTC
3360     // - we support large object
3361     devinfP->flags |= SmlDevInfLargeObject_f;
3362   }
3363   // Now get extensions info
3364   devinfP->ext=NULL;
3365   //  %%% tdb, optional
3366   // return
3367   return devinfP;
3368 } // TSyncSession::newDevInf
3369
3370
3371 // get devInf for this session (caller is passed ownership)
3372 SmlItemPtr_t TSyncSession::getLocalDevInfItem(bool aAlertedOnly, bool aWithoutCTCapProps)
3373 {
3374   // - create item with correct source and Meta information
3375   SmlItemPtr_t devinf = newItem();
3376   // %%% if strictly following example in SyncML protocol specs,
3377   //     source should not have a Displayname
3378   //fLocalDevInfItemP->source=newLocation(SYNCML_DEVINF_LOCURI,SYNCML_DEVINF_LOCNAME);
3379   // %%% if strictly following example in SyncML protocol specs,
3380   //     source should not have "./" prefix
3381   // %%% this is disputable, DCM expects ./, so we send it again now
3382   devinf->source=newLocation(SyncMLDevInfNames[fSyncMLVersion]);
3383   // - create meta type
3384   /* %%% if strictly following example in SyncML protocol specs, meta
3385    *     must be defined in the result/put command, not the individual item
3386   string metatype=SYNCML_DEVINF_META_TYPE;
3387   addEncoding(metatype);
3388   fLocalDevInfItemP->meta=newMetaType(metatype.c_str());
3389   %%% */
3390   // - create DevInf PCData
3391   devinf->data = SML_NEW(SmlPcdata_t);
3392   devinf->data->contentType=SML_PCDATA_EXTENSION;
3393   devinf->data->extension=SML_EXT_DEVINF;
3394   // - %%% assume length is not relevant for structured content (looks like in mgrutil.c)
3395   devinf->data->length=0;
3396   // - create and insert DevInf
3397   devinf->data->content = newDevInf(aAlertedOnly, aWithoutCTCapProps);
3398   // - done
3399   return devinf;
3400 } // TSyncSession::getLocalDevInfItem
3401
3402
3403 // analyze remote devinf delivered by Put or Get/Result commands
3404 // or loaded from cache by loadRemoteDevInf()
3405 localstatus TSyncSession::analyzeRemoteDevInf(
3406   SmlDevInfDevInfPtr_t aDevInfP
3407 )
3408 {
3409   localstatus sta = LOCERR_OK;
3410   PDEBUGBLOCKDESC("DevInf_Analyze","Analyzing remote devInf");
3411   if (!aDevInfP) {
3412     PDEBUGPRINTFX(DBG_ERROR,("Warning: No DevInf found (possible cause: improperly encoded devInf from remote)"));
3413     sta=400; // no devInf
3414     goto done;
3415   }
3416   else {
3417     // we have seen the devinf now
3418     fRemoteDevInfKnown=true;
3419     // analyze what device we have here
3420     PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
3421       "Device ID='%" FMT_LENGTH(".50") "s', Type='%" FMT_LENGTH(".20") "s', Model='%" FMT_LENGTH(".50") "s'",
3422       FMT_LENGTH_LIMITED(50,smlPCDataToCharP(aDevInfP->devid)),
3423       FMT_LENGTH_LIMITED(20,smlPCDataToCharP(aDevInfP->devtyp)),
3424       FMT_LENGTH_LIMITED(50,smlPCDataToCharP(aDevInfP->mod))
3425     ));
3426     PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
3427       "Manufacturer='%" FMT_LENGTH(".30") "s', OEM='%" FMT_LENGTH(".30") "s'",
3428       FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->man)),
3429       FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->oem))
3430     ));
3431     PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
3432       "Softwarevers='%" FMT_LENGTH(".30") "s', Firmwarevers='%" FMT_LENGTH(".30") "s', Hardwarevers='%" FMT_LENGTH(".30") "s'",
3433       FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->swv)),
3434       FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->fwv)),
3435       FMT_LENGTH_LIMITED(30,smlPCDataToCharP(aDevInfP->hwv))
3436     ));
3437     #ifndef MINIMAL_CODE
3438     // get the devinf details 1:1
3439     fRemoteDevInf_devid=smlPCDataToCharP(aDevInfP->devid);
3440     fRemoteDevInf_devtyp=smlPCDataToCharP(aDevInfP->devtyp);
3441     fRemoteDevInf_mod=smlPCDataToCharP(aDevInfP->mod);
3442     fRemoteDevInf_man=smlPCDataToCharP(aDevInfP->man);
3443     fRemoteDevInf_oem=smlPCDataToCharP(aDevInfP->oem);
3444     fRemoteDevInf_swv=smlPCDataToCharP(aDevInfP->swv);
3445     fRemoteDevInf_fwv=smlPCDataToCharP(aDevInfP->fwv);
3446     fRemoteDevInf_hwv=smlPCDataToCharP(aDevInfP->hwv);
3447     // get the descriptive name of the device
3448     fRemoteDescName.assign(smlPCDataToCharP(aDevInfP->man));
3449     if (fRemoteDescName.size()>0) fRemoteDescName+=" ";
3450     fRemoteDescName.append(smlPCDataToCharP(aDevInfP->mod));
3451     // get extra info: "Type (HWV, FWV, SWV) Oem"
3452     fRemoteInfoString+=fRemoteDevInf_devtyp;
3453     fRemoteInfoString+=" (";
3454     fRemoteInfoString+=fRemoteDevInf_hwv;
3455     fRemoteInfoString+=", ";
3456     fRemoteInfoString+=fRemoteDevInf_fwv;
3457     fRemoteInfoString+=", ";
3458     fRemoteInfoString+=fRemoteDevInf_swv;
3459     fRemoteInfoString+=") ";
3460     fRemoteInfoString+=fRemoteDevInf_oem;
3461     #endif
3462     // Show SyncML version here (again)
3463     PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("SyncML Version: %s",SyncMLVerProtoNames[fSyncMLVersion]));
3464     // check SyncML 1.1 flags
3465     if (fSyncMLVersion>=syncml_vers_1_1) {
3466       // - check if remote can receive NOC (number of changes)
3467       fRemoteWantsNOC = (aDevInfP->flags & SmlDevInfNOfM_f);
3468       // - check if remote can handle UTC time
3469       fRemoteCanHandleUTC = (aDevInfP->flags & SmlDevInfUTC_f);
3470       // - check if remote supports large objects
3471       fRemoteSupportsLargeObjects = (aDevInfP->flags & SmlDevInfLargeObject_f);
3472       PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,(
3473         "SyncML capability flags: wantsNOC=%s, canHandleUTC=%s, supportsLargeObjs=%s",
3474         aDevInfP->flags & SmlDevInfNOfM_f ? "Yes" : "No",
3475         aDevInfP->flags & SmlDevInfUTC_f ? "Yes" : "No",
3476         aDevInfP->flags & SmlDevInfLargeObject_f ? "Yes" : "No"
3477       ));
3478     }
3479     // detect remote specific server behaviour if needed
3480     sta = checkRemoteSpecifics(aDevInfP);
3481     if (sta!=LOCERR_OK) {
3482       remoteAnalyzed(); // analyzed to reject
3483       goto done;
3484     }
3485     // Types and datastores may not be changed/added if sync has allready started
3486     if (fRemoteDevInfLock) {
3487       // Sync already started, in "blind" mode or previously received devInf,
3488       // do not confuse things with changing devInf in mid-sync
3489       PDEBUGPRINTFX(DBG_ERROR,(
3490         "WARNING: Type and Datastore info in DevInf ignored because it came to late"
3491       ));
3492     }
3493     else {
3494       if (getSyncMLVersion()<syncml_vers_1_2) {
3495         // analyze CTCaps (content type capabilities)
3496         SmlDevInfCtcapListPtr_t ctlP = aDevInfP->ctcap;
3497         // loop through list
3498         PDEBUGBLOCKDESC("RemoteTypes", "Analyzing remote types listed in devInf level CTCap");
3499         if (fIgnoreCTCap) {
3500           // ignore CTCap
3501           if (ctlP) {
3502             PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("Remote rule prevents looking at CTCap"));
3503           }
3504         }
3505         else {
3506           while (ctlP) {
3507             if (ctlP->data) {
3508               // create appropriate remote data itemtypes
3509               if (TSyncItemType::analyzeCTCapAndCreateItemTypes(
3510                 this,
3511                 NULL, // this is the pre-DS1.2 style where CTCap is on devInf level
3512                 ctlP->data, // CTCap
3513                 fLocalItemTypes, // look up in local types for specialized classes
3514                 fRemoteItemTypes // add new item types here
3515               )) {
3516                 // we have CTCap info of at least one remote type
3517                 fRemoteDataTypesKnown=true;
3518               }
3519               else {
3520                 PDEBUGPRINTFX(DBG_ERROR,("CTCap could not be used (missing version)"));
3521                 sta=500;
3522               }
3523             }
3524             // - go to next item
3525             ctlP=ctlP->next;
3526           } // while
3527         }
3528         PDEBUGENDBLOCK("RemoteTypes");
3529       } // if <DS1.2
3530       // now get datastores
3531       PDEBUGBLOCKDESC("RemoteDatastores", "Analyzing remote datastores");
3532       SmlDevInfDatastoreListPtr_t dslP = aDevInfP->datastore;
3533       while(dslP) {
3534         if (dslP->data) {
3535           // we have DataStore info of remote datastores
3536           fRemoteDataStoresKnown=true;
3537           // there is a DataStore entry, create RemoteDataStore for it
3538           TRemoteDataStore *datastoreP;
3539           MP_NEW(datastoreP,DBG_OBJINST,"TRemoteDataStore",TRemoteDataStore(this));
3540           PDEBUGBLOCKDESC("RemoteDSDevInf", "Registering remote Datastore from devInf");
3541           // let new datastore analyze the devinf data
3542           if (!datastoreP->setDatastoreDevInf(
3543             dslP->data,
3544             fLocalItemTypes,  // look up for datatypes here first
3545             fRemoteItemTypes  // but add types here
3546           )) {
3547             // invalid CTCap
3548             PDEBUGPRINTFX(DBG_ERROR,("Invalid DataStore devInf"));
3549             delete datastoreP; // forget invalid data store
3550             sta=500; // failed
3551           }
3552           else {
3553             // CTCap set successfully, new type created
3554             // - save it in the list of remote types
3555             fRemoteDataStores.push_back(datastoreP);
3556           }
3557           PDEBUGENDBLOCK("RemoteDSDevInf");
3558         }
3559         // - go to next item
3560         dslP=dslP->next;
3561       } // while
3562       PDEBUGENDBLOCK("RemoteDatastores");
3563     } // else sync not started yet
3564   }
3565   // give descendants possibility to do something with the analyzed data
3566   remoteAnalyzed();
3567   // ok
3568 done:
3569   PDEBUGENDBLOCK("DevInf_Analyze");
3570   return sta;
3571 } // TSyncSession::analyzeRemoteDevInf
3572
3573
3574 #ifdef SYSYNC_SERVER
3575
3576 // Initialize Sync: set up datastores and types for server sync session
3577 localstatus TSyncSession::initSync(
3578   const char *aLocalDatastoreURI,
3579   const char *aRemoteDatastoreURI
3580 )
3581 {
3582   localstatus sta = LOCERR_OK;
3583
3584   // search for local datastore first
3585   string cgiOptions;
3586   // - search for datastore and obtain possible CGI
3587   fLocalSyncDatastoreP = findLocalDataStoreByURI(SessionRelativeURI(aLocalDatastoreURI),&cgiOptions);
3588   if (!fLocalSyncDatastoreP) {
3589     // no such local datastore
3590     return 404;
3591   }
3592   // Local datastore is known here (fLocalSyncDatastoreP)
3593   // - now init for reception of syncops
3594   sta = fLocalSyncDatastoreP->engInitForSyncOps(aRemoteDatastoreURI);
3595   #ifdef SYNCML_TAF_SUPPORT
3596   if (sta==LOCERR_OK) {
3597     // - make sure that options are reparsed (TAF *might* change from Sync request to Sync request)
3598     sta = fLocalSyncDatastoreP->engParseOptions(
3599       cgiOptions.c_str(),
3600       true // we are parsing options from <sync> target URI
3601     );
3602   }
3603   #endif
3604   #ifdef OBJECT_FILTERING
3605   if (sta==LOCERR_OK) {
3606     // %%% check for DS 1.2 <filter> in <Sync> command as well (we do parse <filter> in <Alert> already)
3607     #if (!defined _MSC_VER || defined WINCE) && !defined(__GNUC__)
3608     #warning "tbd %%%: check for DS 1.2 <filter> in <Sync> command as well (we do parse <filter> in <Alert> already)"
3609     #endif
3610   }
3611   #endif
3612   #ifdef OBJECT_FILTERING
3613   // Show filter summary
3614   #ifdef SYDEBUG
3615   #ifdef SYNCML_TAF_SUPPORT
3616   PDEBUGPRINTFX(DBG_FILTER,("TAF   (temporary, INCLUSIVE) Filter : %s",fLocalSyncDatastoreP->fTargetAddressFilter.c_str()));
3617   #endif // SYNCML_TAF_SUPPORT
3618   PDEBUGPRINTFX(DBG_FILTER,("SyncSet (dynamic, EXCLUSIVE) Filter : %s",fLocalSyncDatastoreP->fSyncSetFilter.c_str()));
3619   #ifdef SYSYNC_TARGET_OPTIONS
3620   string ts;
3621   StringObjTimestamp(ts,fLocalSyncDatastoreP->fDateRangeStart);
3622   PDEBUGPRINTFX(DBG_FILTER,("Date Range Start                    : %s",fLocalSyncDatastoreP->fDateRangeStart ? ts.c_str() : "<none>"));
3623   StringObjTimestamp(ts,fLocalSyncDatastoreP->fDateRangeEnd);
3624   PDEBUGPRINTFX(DBG_FILTER,("Date Range End                      : %s",fLocalSyncDatastoreP->fDateRangeEnd ? ts.c_str() : "<none>"));
3625   #endif // SYSYNC_TARGET_OPTIONS
3626   #endif // SYDEBUG
3627   #endif // OBJECT_FILTERING
3628   // return status
3629   return sta;
3630 } // TSyncSession::initSync
3631
3632 #endif // SYSYNC_SERVER
3633
3634
3635
3636 // end sync group (of client sync commands)
3637 bool TSyncSession::processSyncEnd(bool &aQueueForLater)
3638 {
3639   bool ok=true;
3640
3641   // inform local
3642   if (fLocalSyncDatastoreP) {
3643     // let datastore process it
3644     ok=fLocalSyncDatastoreP->engProcessSyncCmdEnd(aQueueForLater);
3645   }
3646   // end Sync bracket
3647   #ifdef SYNCSTATUS_AT_SYNC_CLOSE
3648   // %%% status for sync command sent AFTER statuses for contained commands
3649   if (fSyncCloseStatusCommandP)
3650     issueRoot(fSyncCloseStatusCommandP);
3651   #endif
3652   // no local datastore active
3653   fLocalSyncDatastoreP=NULL;
3654   return ok;
3655 } // TSyncSession::processSyncEnd
3656
3657
3658
3659 // process generic sync command item within Sync group
3660 // - returns true (and unmodified or non-200-successful status) if
3661 //   operation could be processed regularily
3662 // - returns false (but probably still successful status) if
3663 //   operation was processed with internal irregularities, such as
3664 //   trying to delete non-existant item in datastore with
3665 //   incomplete Rollbacks (which returns status 200 in this case!).
3666 bool TSyncSession::processSyncOpItem(
3667   TSyncOperation aSyncOp,        // the operation
3668   SmlItemPtr_t aItemP,           // the item to be processed
3669   SmlMetInfMetInfPtr_t aMetaP,   // command-wide meta, if any
3670   TLocalEngineDS *aLocalSyncDatastore, // the local datastore for this syncop item
3671   TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
3672   bool &aQueueForLater // must be set if item cannot be processed now, but must be processed later
3673 )
3674 {
3675   // assign datastore context (%%% note: some day we will get rid of this
3676   // "global" pointer to the active datastore by moving it into the <sync> command
3677   // object and installing a hierarchical command processor.)
3678   fLocalSyncDatastoreP=aLocalSyncDatastore;
3679   // Server mode: commands affect datastores currently in sync
3680   if (!fLocalSyncDatastoreP) {
3681     // sync generic command outside sync bracket -> error
3682     aStatusCommand.setStatusCode(403); // forbidden
3683     ADDDEBUGITEM(aStatusCommand,"Add/Copy/Replace/Delete unrelated to datastores");
3684     PDEBUGPRINTFX(DBG_ERROR,("Add/Copy/Replace/Delete unrelated to datastores"));
3685     // no success
3686     return false;
3687   }
3688   // check for aborted datastore
3689   if (fLocalSyncDatastoreP->CheckAborted(aStatusCommand)) return false;
3690   // check if we can process it now
3691   // Note: request time limit is active in server only.
3692   if (!fLocalSyncDatastoreP->engIsStarted(false) || RemainingRequestTime()<0) {
3693     aQueueForLater=true; // re-execute later...
3694     return true; // ...but otherwise ok
3695   }
3696   // process Sync operation sent by remote
3697   // - show
3698   PDEBUGPRINTFX(DBG_DATA,(
3699     "Remote sent %s-operation:",
3700     SyncOpNames[aSyncOp]
3701   ));
3702   PDEBUGPRINTFX(DBG_DATA,(
3703     "- Source: remoteID ='%s', remoteName='%s'",
3704     smlSrcTargLocURIToCharP(aItemP->source),
3705     smlSrcTargLocNameToCharP(aItemP->source)
3706   ));
3707   PDEBUGPRINTFX(DBG_DATA,(
3708     "- Target: localID  ='%s', remoteName='%s'",
3709     smlSrcTargLocURIToCharP(aItemP->target),
3710     smlSrcTargLocNameToCharP(aItemP->target)
3711   ));
3712   // now let datastore handle it
3713   bool regular = fLocalSyncDatastoreP->engProcessSyncOpItem(aSyncOp, aItemP, aMetaP, aStatusCommand);
3714   #ifdef SCRIPT_SUPPORT
3715   // let script check status code
3716   TErrorFuncContext errctx;
3717   errctx.statuscode = aStatusCommand.getStatusCode();
3718   errctx.newstatuscode = errctx.statuscode;
3719   errctx.syncop = aSyncOp;
3720   errctx.datastoreP = fLocalSyncDatastoreP;
3721   // call script
3722   regular =
3723     TScriptContext::executeTest(
3724       regular, // pass through regular status
3725       fSessionScriptContextP,
3726       getSessionConfig()->fReceivedItemStatusScript,
3727       &ErrorFuncTable,
3728       &errctx // caller context
3729     );
3730   // not completely handled, use possibly modified status code
3731   #ifdef SYDEBUG
3732   if (aStatusCommand.getStatusCode() != errctx.newstatuscode) {
3733     PDEBUGPRINTFX(DBG_ERROR,("Status: Session Script changed original status=%hd to %hd (original op was %s)",aStatusCommand.getStatusCode(),errctx.newstatuscode,SyncOpNames[errctx.syncop]));
3734   }
3735   #endif
3736   aStatusCommand.setStatusCode(errctx.newstatuscode);
3737   #endif
3738   // check status
3739   if (!regular) {
3740     localstatus sta=aStatusCommand.getStatusCode();
3741     if (sta>=300 && sta!=419) { // conflict resolved with server data is not an error
3742       PDEBUGPRINTFX(DBG_ERROR,(
3743         "processSyncOpItem: Error while processing item, status=%hd",
3744         aStatusCommand.getStatusCode()
3745       ));
3746       fLocalSyncDatastoreP->fLocalItemsError++; // count this as an error, as remote will see it as such
3747     }
3748     else {
3749       PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
3750         "processSyncOpItem: Irregularity while processing item, status=%hd",
3751         aStatusCommand.getStatusCode()
3752       ));
3753     }
3754   }
3755   // done
3756   return regular;
3757 } // TSyncSession::processSyncOpItem
3758
3759
3760 #endif // not SYNCSESSION_PART2_EXCLUDE
3761 #ifndef SYNCSESSION_PART1_EXCLUDE
3762
3763
3764 // generate challenge for session
3765 SmlChalPtr_t TSyncSession::newSessionChallenge(void)
3766 {
3767   string nonce;
3768   getNextNonce(fRemoteURI.c_str(),nonce);
3769   PDEBUGPRINTFX(DBG_PROTO,(
3770     "Challenge for next auth: AuthType=%s, Nonce='%s', binary %sallowed",
3771     authTypeSyncMLNames[requestedAuthType()],
3772     nonce.c_str(),
3773     getEncoding()==SML_WBXML ? "" : "NOT "
3774   ));
3775   return newChallenge(requestedAuthType(),nonce,getEncoding()==SML_WBXML);
3776 } // TSyncSession::newSessionChallenge
3777
3778
3779 // generate credentials (based on fRemoteNonce, fRemoteRequestedAuth, fRemoteRequestedAuthEnc)
3780 SmlCredPtr_t TSyncSession::newCredentials(const char *aUser, const char *aPassword)
3781 {
3782   SmlCredPtr_t credP = NULL;
3783   SmlMetInfMetInfPtr_t metinfP = NULL;
3784   uInt8 *authdata = NULL;
3785   void *tobefreed = NULL;
3786   uInt32 authdatalen=0;
3787   bool isbinary=false;
3788   uInt8 digest[16]; // for MD5 digest
3789
3790   // create auth data
3791   // - build basic user/pw string
3792   string userpw;
3793   userpw.assign(aUser);
3794   userpw+=':';
3795   userpw.append(aPassword);
3796   // - code auth data
3797   switch (fRemoteRequestedAuth) {
3798     case auth_basic:
3799       // Note: this has been clarified in SyncML 1.1: even if B64 is inherent for
3800       // Basic auth, specifying B64 as format does NOT mean that user:pw is B64-ed twice,
3801       // but it is just an optional declaration of the inherent B64 format.
3802       #ifdef BASIC_AUTH_HAS_INHERENT_B64
3803       // %%% seems to be wrong according to SCTS...
3804       // Note that the b64 here is PART OF THE BASIC AUTH SCHEME
3805       // so format MUST NOT specify b64 again!
3806       fRemoteRequestedAuthEnc=fmt_chr;
3807       // make b64 of string
3808       authdata=(uInt8 *)b64::encode((const uInt8 *)userpw.c_str(), userpw.size(), &authdatalen);
3809       tobefreed=(void *)authdata; // remember to free at end of routine
3810       #else
3811       // basic auth is always b64 encoded
3812       fRemoteRequestedAuthEnc=fmt_b64;
3813       authdata=(uInt8 *)userpw.c_str();
3814       #endif
3815       break;
3816     case auth_md5:
3817       // Note that b64 encoding IS NOT part of the MD5 auth scheme.
3818       // Only if remote specifies b64 format in challenge, b64 encoding is applied
3819       if (fSyncMLVersion<syncml_vers_1_1) {
3820         // before 1.1, nonce was MD5-ed together with user/pw
3821         userpw+=':';
3822         userpw+=fRemoteNonce; // append nonce string, might contain NULs
3823       }
3824       // apply MD5
3825       md5::SYSYNC_MD5_CTX context;
3826       md5::Init (&context);
3827       md5::Update (&context, (unsigned const char *)userpw.c_str(), userpw.size());
3828       // get result
3829       md5::Final (digest, &context);
3830       // more if SyncML 1.1 or later
3831       if (fSyncMLVersion>=syncml_vers_1_1) {
3832         // starting with 1.1, nonce is added to b64ed-MD5 and the MD5ed again
3833         // - B64 it
3834         authdata=(uInt8*)b64::encode(digest, 16, &authdatalen);
3835         // - MD5 it while adding nonce
3836         md5::Init (&context);
3837         md5::Update (&context, authdata, authdatalen);
3838         b64::free((void *)authdata); // return buffer allocated by b64::encode
3839         // - important: add colon as nonce separator
3840         md5::Update (&context, (uInt8 *) ":", 1);
3841         // - also add nonce that will be used for checking later
3842         md5::Update (&context, (uInt8 *) fRemoteNonce.c_str(), fRemoteNonce.size());
3843         // - this is the MD5 auth value
3844         //   according to SyncML 1.1,
3845         //   "changes_for_syncml_represent_v11_20020215.pdf", Section 2.19
3846         md5::Final (digest, &context);
3847         // - according to the above mentioned section 2.19, MD5 auth is
3848         //   always b64 encoded, even in binary transports. This is a
3849         //   contradiction to discussion in syncml@yahoogroups, particularily
3850         //   a statement by Peter Thompson who stated that MD5 auth MUST NOT
3851         //   be b64 encoded in WBXML. Who knows???
3852         fRemoteRequestedAuthEnc=fmt_b64;
3853       } // syncml 1.1
3854       // auth data is 16 byte digest value in binary
3855       authdata=(uInt8 *)digest;
3856       authdatalen=16;
3857       isbinary=true;
3858       break;
3859     default : break;
3860   } // switch
3861   if (authdata) {
3862     // create cred
3863     credP = SML_NEW(SmlCred_t);
3864     // now add auth data (format if necessary)
3865     // - force b64 anyway if content is binary but transport isn't
3866     if (isbinary && getEncoding()==SML_XML) {
3867       fRemoteRequestedAuthEnc=fmt_b64;
3868       isbinary=false;
3869     }
3870     // - create formatted version of content. Use Opaque for binary content
3871     credP->data=newPCDataFormatted(authdata,authdatalen,fRemoteRequestedAuthEnc,isbinary);
3872     // create meta and get pointer
3873     credP->meta=newMeta();
3874     metinfP = smlPCDataToMetInfP(credP->meta);
3875     // add auth type meta
3876     metinfP->type=newPCDataString(authTypeSyncMLNames[fRemoteRequestedAuth]);
3877     // Note: aEncType==fmt_chr will not add format tag, as fmt_chr is the default
3878     metinfP->format=newPCDataFormat(fRemoteRequestedAuthEnc,false); // no format if default of fmt_chr
3879   }
3880   // free buffer
3881   if (tobefreed) b64::free(tobefreed);
3882   // return cred or NULL if none
3883   return credP;
3884 } // TSyncSession::newCredentials
3885
3886
3887 // check credentials
3888 // Note: should be called even if there are no credentials, as we
3889 //       need a Session login BEFORE generating status with next Nonce
3890 bool TSyncSession::checkCredentials(const char *aUserName, const SmlCredPtr_t aCredP, TStatusCommand &aStatusCommand)
3891 {
3892   TAuthTypes authtype=auth_basic; // default to basic
3893   char *tobefreed = NULL;
3894   const char *authdata = NULL;
3895   TFmtTypes authfmt = fmt_chr;
3896   bool authok = false;
3897
3898   #ifdef EXPIRES_AFTER_DATE
3899   // check for hard expiry again
3900   if (
3901     (fCopyOfScrambledNow>(SCRAMBLED_EXPIRY_VALUE*4+503))
3902     #ifdef SYSER_REGISTRATION
3903     && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
3904     #endif
3905   ) {
3906     aStatusCommand.setStatusCode(401); // seems to be hacked
3907     return false;
3908   }
3909   #endif
3910   // Check type of credentials
3911   if (!aCredP) {
3912     // Anonymous login attempt
3913     authtype=auth_none;
3914   }
3915   else {
3916     SmlMetInfMetInfPtr_t metaP;
3917     if ((metaP=smlPCDataToMetInfP(aCredP->meta))!=NULL) {
3918       // look for type
3919       if (metaP->type) {
3920         // get type (otherwise default to auth-basic)
3921         const char *ty = smlPCDataToCharP(metaP->type);
3922         sInt16 t;
3923         if (StrToEnum(authTypeSyncMLNames,numAuthTypes,t,ty)) {
3924           authtype=(TAuthTypes)t;
3925         }
3926         else {
3927           // bad auth schema
3928           authtype=auth_none; // disable checking below
3929           aStatusCommand.setStatusCode(406); // unsupported optional feature (auth method)
3930           aStatusCommand.addItemString(ty); // identify bad auth method
3931         }
3932       }
3933       // look for format
3934       // - get format
3935       if (!smlPCDataToFormat(metaP->format,authfmt)) {
3936         authtype=auth_none; // disable checking below
3937         aStatusCommand.setStatusCode(415); // unsupported format
3938         aStatusCommand.addItemString(smlPCDataToCharP(metaP->format)); // identify bad format
3939       }
3940       // - handle format and get auth data according to auth type
3941       authdata = smlPCDataToCharP(aCredP->data); // get it as is
3942       // Now check auth
3943       switch (authtype) {
3944         case auth_none:
3945           // anonymous login attempt
3946           // - no special measure needed
3947           break;
3948         case auth_basic:
3949           // basic is always b64 encoded
3950           #ifdef SYDEBUG
3951           if (authfmt!=fmt_b64)
3952             PDEBUGPRINTFX(DBG_ERROR,("Auth-basic has no <format>b64 spec --> assumed b64 anyway"));
3953           #endif
3954           authfmt=fmt_b64; // basic is ALWAYS b64
3955           break;
3956         case auth_md5:
3957           // verify that we have the username in clear text for MD5 auth
3958           if (!aUserName || *aUserName==0)
3959           {
3960             // username missing, probably strict (bad) SyncML 1.0 conformance,
3961             // we need SyncML 1.0.1 corrected auth (MD5 w/o username is almost
3962             // impossible to process)
3963             authtype=auth_none; // disable checking below
3964             aStatusCommand.setStatusCode(415); // unsupported format
3965             ADDDEBUGITEM(aStatusCommand,"Missing clear-text username in Source LocName (SyncML 1.0.1)");
3966             PDEBUGPRINTFX(DBG_ERROR,("Missing clear-text username in Source LocName (SyncML 1.0.1)"));
3967             break;
3968           }
3969           // MD5 can come as binary (for WBXML)
3970           if (getEncoding()==SML_WBXML) {
3971             if (authfmt==fmt_chr || authfmt==fmt_bin) {
3972               // assume unencoded MD5 digest (16 bytes binary), make b64
3973               uInt32 l;
3974               tobefreed=b64::encode((uInt8 *)authdata,16,&l);
3975               authdata=tobefreed;
3976               // now authdata is b64 as well
3977               authfmt=fmt_b64;
3978             }
3979           }
3980           break;
3981       case numAuthTypes:
3982           // invalid type?!
3983           break;
3984       }
3985     } // if meta
3986   }
3987   #ifndef MINIMAL_CODE
3988   // save user name for later reference
3989   if (aUserName) fSyncUserName.assign(aUserName);
3990   else fSyncUserName.erase();
3991   #endif
3992   // check credentials
3993   if (authtype==auth_none) {
3994     // check if we can login anonymously
3995     // NOTE: do it anyway, even if !isAuthTypeAllowed() to make sure
3996     //       SessionLogin is called
3997     authok=checkCredentials(aUserName,NULL,auth_none);
3998     if (!authok || !isAuthTypeAllowed(auth_none)) {
3999       // anonymous login not possible, request credentials
4000       aStatusCommand.setStatusCode(407); // unauthorized, missing credentials
4001       PDEBUGPRINTFX(DBG_PROTO,("Authorization required but none found in SyncHdr, sending status 407 + chal"));
4002       // - add challenge
4003       aStatusCommand.setChallenge(newSessionChallenge());
4004       authok=false;
4005     }
4006   }
4007   else {
4008     // verify format (must be MD5 by now)
4009     if (authfmt!=fmt_b64) {
4010       aStatusCommand.setStatusCode(415); // unsupported format
4011     }
4012     else if (!authdata || !*authdata) {
4013       aStatusCommand.setStatusCode(400); // missing data, malformed request
4014     }
4015     else {
4016       // first check credentials
4017       // NOTE: This must be done first, to force calling SessionLogin
4018       //       in all cases
4019       authok=checkCredentials(aUserName,authdata,authtype);
4020       // now check result
4021       if (!isAuthTypeAllowed(authtype)) {
4022         aStatusCommand.setStatusCode(401); // we need another auth type, tell client which one
4023         authok=false; // anyway, reject
4024         PDEBUGPRINTFX(DBG_ERROR,("Authorization failed (wrong type of creds), sending 401 + chal"));
4025       }
4026       else if (!authok) {
4027         // auth type allowed, but auth itself not ok
4028         aStatusCommand.setStatusCode(401); // unauthorized, bad credentials
4029         PDEBUGPRINTFX(DBG_ERROR,("Authorization failed (invalid credentials) sending 401 + chal"));
4030       }
4031       if (!authok) {
4032         // - add challenge
4033         aStatusCommand.setChallenge(newSessionChallenge());
4034       }
4035     }
4036   }
4037   // free buffer if any
4038   if (tobefreed) b64::free(tobefreed);
4039   // make sure we see what config was used in the log
4040   DebugShowCfgInfo();
4041   PDEBUGPRINTFX(DBG_HOT,(
4042     "==== Authorisation %s with SyncML Engine Version %d.%d.%d.%d",
4043     authok ?  "successful" : "failed",
4044     SYSYNC_VERSION_MAJOR,
4045     SYSYNC_VERSION_MINOR,
4046     SYSYNC_SUBVERSION,
4047     SYSYNC_BUILDNUMBER
4048   ));
4049   if (IS_SERVER) {
4050     #ifdef SYSYNC_SERVER
4051     PDEBUGPRINTFX(DBG_HOT,(
4052       "==== SyncML URL used = '%s', username as sent by remote = '%s'",
4053       fInitialLocalURI.c_str(),
4054       fSyncUserName.c_str()
4055     ));
4056     #endif
4057   } // server
4058   // return result
4059   return authok;
4060 } // TSyncSession::checkCredentials(SmlCredPtr_t...)
4061
4062
4063 // check credential string
4064 bool TSyncSession::checkCredentials(const char *aUserName, const char *aCred, TAuthTypes aAuthType)
4065 {
4066   // now check auth
4067   if (aAuthType==auth_basic) {
4068     // basic auth allows extracting clear-text password
4069     string user,password;
4070     getAuthBasicUserPass(aCred,user,password);
4071     #ifndef MINIMAL_CODE
4072     fSyncUserName = user;
4073     #endif
4074     if (aUserName && !(user==aUserName)) {
4075       // username does not match LocName (should, in SyncML 1.0.1 and later)
4076       PDEBUGPRINTFX(DBG_PROTO,(
4077         "basic_auth encoded username (%s) does not match LocName username (%s)",
4078         user.c_str(),
4079         aUserName ? aUserName : "[NULL Username]"
4080       ));
4081     }
4082     // we have the password in clear text
4083     return SessionLogin(user.c_str(), password.c_str(), sectyp_clearpass, fRemoteURI.c_str());
4084   }
4085   else if (aAuthType==auth_md5) {
4086     // login user and device to the service
4087     // - this is normally implemented in derived classes
4088     return SessionLogin(aUserName, aCred, fSyncMLVersion>=syncml_vers_1_1 ? sectyp_md5_V11 : sectyp_md5_V10, fRemoteURI.c_str());
4089   }
4090   else if (aAuthType==auth_none) {
4091     // even if we have no login, do a "login" with empty credentials
4092     return SessionLogin("anonymous", NULL, sectyp_anonymous, fRemoteURI.c_str());
4093   }
4094   else {
4095     return false; // unknown auth, is not ok
4096   }
4097 } // TSyncSession::checkCredentials(const char *...)
4098
4099
4100
4101 // Helper function:
4102 // check plain user / password / nonce combination
4103 // against given auth string.
4104 bool TSyncSession::checkAuthPlain(
4105   const char *aUserName, const char *aPassWord, const char *aNonce, // given values
4106   const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
4107 )
4108 {
4109   string upw;
4110
4111   #ifdef SYDEBUG
4112   PDEBUGPRINTFX(DBG_ADMIN,("Username                      = %s",aUserName));
4113   DEBUGPRINTFX(DBG_USERDATA+DBG_EXOTIC,("Password                      = %s",aPassWord));
4114   #endif
4115   if (aAuthStringType==sectyp_anonymous) {
4116     return (aPassWord==NULL || *aPassWord==0); // anonymous login ok if no password expected
4117   }
4118   else if (aAuthStringType==sectyp_clearpass) {
4119     return (strcmp(aAuthString,aPassWord)==0); // login ok if password matches
4120   }
4121   else {
4122     // must be MD5
4123     // - concatenate user:password
4124     upw = aUserName;
4125     upw+=':';
4126     upw.append(aPassWord);
4127     // depends on method
4128     if (aAuthStringType==sectyp_md5_V11) {
4129       // V1.1 requires MD5b64-ing user/pw before adding nonce
4130       MD5B64(upw.c_str(),upw.size(),upw);
4131     }
4132     // now check result
4133     return checkMD5WithNonce(upw.c_str(),aNonce,aAuthString);
4134   }
4135   // unknown auth secret type
4136   return false;
4137 } // TSyncSession::checkAuthPlain
4138
4139
4140 // Helper function:
4141 // check MD5B64(user:pw) / nonce combination
4142 // against given auth string.
4143 bool TSyncSession::checkAuthMD5(
4144   const char *aUserName, const char *aMD5B64, const char *aNonce, // given values
4145   const char *aAuthString, TAuthSecretTypes aAuthStringType // check against this
4146 )
4147 {
4148
4149   if (aAuthStringType==sectyp_md5_V11) {
4150     // we have a V11 authstring, check it against our MD5B64(user:pw)
4151     return checkMD5WithNonce(aMD5B64,aNonce,aAuthString);
4152   }
4153   else if (aAuthStringType==sectyp_clearpass) {
4154     // we must generate the MD5B64(user:pw) from clear text
4155     string myAuthString = aUserName;
4156     myAuthString+=':';
4157     myAuthString+=aAuthString;
4158     MD5B64(myAuthString.c_str(),myAuthString.size(),myAuthString);
4159     #ifdef SYDEBUG
4160     PDEBUGPRINTFX(DBG_ADMIN,("MD5B64(user:pw) stored in local DB     = %s",aMD5B64));
4161     PDEBUGPRINTFX(DBG_ADMIN,("calculated MD5B64(remoteuser:remotepw) = %s",myAuthString.c_str()));
4162     #endif
4163     // then we can directly compare them
4164     return myAuthString==aMD5B64;
4165   }
4166   else
4167     return false; // we cannot auth V1.0 MD5 against MD5B64(user:password)
4168 } // checkAuthMD5
4169
4170
4171 // Helper function:
4172 // check V1.1 MD5 type auth against known md5userpass
4173 // (B64 encoded MD5 digest of user:password) and nonce
4174 // Note: This works only with V1.1-type credentials!!!!
4175 bool TSyncSession::checkMD5WithNonce(
4176   const char *aStringBeforeNonce,
4177   const char *aNonce,
4178   const char *aMD5B64Creds
4179 )
4180 {
4181   string pattern; // pattern to match with
4182
4183   // see if user/pw/nonce matches given MD5
4184   // - add nonce to prepared string
4185   //   For V1.0 this is "user:password"
4186   //   For >=V1.1 this is MD5B64("user:password")
4187   pattern = aStringBeforeNonce;
4188   pattern+=':';
4189   pattern.append(aNonce);
4190   // - MD5 and B64 entire thing (again)
4191   MD5B64(pattern.c_str(),pattern.size(),pattern);
4192   #ifdef SYDEBUG
4193   DEBUGPRINTFX(DBG_ADMIN ,("String before Nonce         = %s",aStringBeforeNonce));
4194   DEBUGPRINTFX(DBG_ADMIN ,("Nonce used                  = %s",aNonce));
4195   PDEBUGPRINTFX(DBG_ADMIN,("Locally calculated MD5B64   = %s",pattern.c_str()));
4196   PDEBUGPRINTFX(DBG_ADMIN,("Received MD5B64 from remote = %s",aMD5B64Creds));
4197   #endif
4198   // - now compare with given credentials
4199   return strnncmp(aMD5B64Creds,pattern.c_str(),pattern.size())==0;
4200 } // TSyncSession::checkMD5WithNonce
4201
4202
4203 // helper: get user/password out of basic credential string, returns false if bad cred
4204 bool TSyncSession::getAuthBasicUserPass(const char *aBasicCreds, string &aUsername, string &aPassword)
4205 {
4206   // - convert to user/pw string
4207   uInt32 userpwlen;
4208   uInt8 *userpw=b64::decode(aBasicCreds, 0, &userpwlen);
4209   bool ok=false;
4210   if (userpw) {
4211     // adjust length if already null terminated
4212     if (userpw[userpwlen-1]==0) userpwlen=strlen((char *)userpw);
4213     // extract plain-text username first
4214     const char *p=strchr((const char *)userpw,':');
4215     if (p) {
4216       // save user name
4217       aUsername.assign((const char *)userpw,p-(const char *)userpw);
4218       // save password
4219       aPassword.assign(p+1);
4220       ok=true;
4221     }
4222   }
4223   b64::free(userpw);
4224   return ok;
4225 } // TSyncSession::getAuthBasicUserPass
4226
4227
4228 // check credential string (clear text pw, MD5, etc.)
4229 // This function is normally derived to provide checking of auth string
4230 // Notes:
4231 // - all auth requests are resolved using this function.
4232 // - For pre-SyncML 1.1 MD5 auth, credentials are checkable only
4233 //   against plain text passwords. It's up to the derived class to decide if
4234 //   this is possible or not.
4235 bool TSyncSession::SessionLogin(
4236   const char *aUserName,
4237   const char *aAuthString,
4238   TAuthSecretTypes aAuthStringType,
4239   const char *aDeviceID
4240 )
4241 {
4242   string nonce;
4243   // get config for session
4244   TSessionConfig *scP = getSessionConfig();
4245   // anonymous is always ok (because checking if anonymous allowed is done already)
4246   if (aAuthStringType==sectyp_anonymous) return true; // ok
4247   // check simple auth
4248   if (scP->fSimpleAuthUser.empty()) return false; // no simple auth
4249   // check user name
4250   if (strucmp(scP->fSimpleAuthUser.c_str(),aUserName)!=0) return false; // wrong user name
4251   // now check auth string
4252   if (aAuthStringType==sectyp_md5_V10 || aAuthStringType==sectyp_md5_V11) {
4253     // we need a nonce
4254     getAuthNonce(aDeviceID,nonce);
4255   }
4256   // now check
4257   return checkAuthPlain(
4258     scP->fSimpleAuthUser.c_str(),
4259     scP->fSimpleAuthPassword.c_str(),
4260     nonce.c_str(),
4261     aAuthString,
4262     aAuthStringType
4263   );
4264 } // TSyncSession::SessionLogin
4265
4266
4267
4268 // check remote devinf to detect special behaviour needed for some clients (or servers). Base class
4269 // does not do anything on server level (configured rules are handled at session level)
4270 // - NOTE: aDevInfP can be NULL to specify that remote device has not sent any devInf at all
4271 //         and this is a blind sync attempt (so best-guess workaround settings might apply)
4272 localstatus TSyncSession::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
4273 {
4274   #if defined(SYSER_REGISTRATION) || !defined(NO_REMOTE_RULES)
4275   localstatus sta = LOCERR_OK;
4276   #endif
4277
4278   // check hard-coded restrictions
4279   if (aDevInfP && (
4280     false
4281     #ifdef REMOTE_RESTR_DEVID
4282     || strwildcmp(smlPCDataToCharP(aDevInfP->devid),REMOTE_RESTR_DEVID)!=0
4283     #endif
4284     #ifdef REMOTE_RESTR_MAN
4285     || strwildcmp(smlPCDataToCharP(aDevInfP->man),REMOTE_RESTR_MAN)!=0
4286     #endif
4287     #ifdef REMOTE_RESTR_MOD
4288     || strwildcmp(smlPCDataToCharP(aDevInfP->mod),REMOTE_RESTR_MOD)!=0
4289     #endif
4290     #ifdef REMOTE_RESTR_OEM
4291     || strwildcmp(smlPCDataToCharP(aDevInfP->oem),REMOTE_RESTR_OEM)!=0
4292     #endif
4293     #ifdef REMOTE_RESTR_URI
4294     || strwildcmp(fRemoteURI.c_str(),REMOTE_RESTR_URI)!=0
4295     #endif
4296   )) {
4297     PDEBUGPRINTFX(DBG_ERROR,("Software not allowed syncing with this remote party"));
4298     AbortSession(403,true);
4299     return 403;
4300   }
4301
4302   // check license restrictions
4303   #ifdef SYSER_REGISTRATION
4304   sInt16 daysleft;
4305   string s;
4306
4307   // - get restriction string from licensed info
4308   sta = getSyncAppBase()->getAppEnableInfo(daysleft, NULL, &s);
4309   string restrid,restrval;
4310   const char *p = s.c_str(); // start of license info string
4311   while (sta==LOCERR_OK && (p=getSyncAppBase()->getLicenseRestriction(p,restrid,restrval))!=NULL) {
4312     const char *restr=NULL;
4313     if (restrid=="u") { // URL
4314       // we can check the remote URL without having devinf
4315       restr=fRemoteURI.c_str();
4316     }
4317     else {
4318       if (restrid.size()==1) {
4319         // there is a restriction
4320         if (!aDevInfP) {
4321           // we cannot check these restrictions without having a devInf
4322           sta = LOCERR_BADREG;
4323                       PDEBUGPRINTFX(DBG_ERROR,("License restriction needs devInf from remote but none found -> block sync"));
4324           break;
4325         }
4326         // we have devinf, we can check it
4327         switch (restrid[0]) {
4328           case 'i' : restr=smlPCDataToCharP(aDevInfP->devid); break;
4329           case 'm' : restr=smlPCDataToCharP(aDevInfP->man); break;
4330           case 't' : restr=smlPCDataToCharP(aDevInfP->mod); break;
4331           case 'o' : restr=smlPCDataToCharP(aDevInfP->oem); break;
4332         }
4333       }
4334     }
4335     // - now compare with wildcards allowed if we have anything to compare
4336     if (restr) sta = strwildcmp(restr,restrval.c_str())==0 ? LOCERR_OK : LOCERR_BADREG; // service unavailable
4337   }
4338   // - abort if not ok
4339   if (sta!=LOCERR_OK) {
4340     PDEBUGPRINTFX(DBG_ERROR,("License does not allow syncing with this remote party, status=%hd",sta));
4341     AbortSession(403,true,sta);
4342     return sta;
4343   }
4344   #endif // SYSER_REGISTRATION
4345   // check remote rules
4346   #ifndef NO_REMOTE_RULES
4347   PDEBUGBLOCKDESC("RemoteRules","Checking for remote rules");
4348   // get config for session
4349   TSessionConfig *scP = getSessionConfig();
4350   // look if we have matching rule(s) for this device
4351   TRemoteRulesList::iterator pos;
4352   for(pos=scP->fRemoteRulesList.begin();pos!=scP->fRemoteRulesList.end();pos++) {
4353     TRemoteRuleConfig *ruleP = *pos;
4354     // compare with devinf (or test for default-rule if aDevInfP is NULL
4355     if (
4356         !ruleP->fSubRule && // subrules never apply directly
4357       (ruleP->fManufacturer.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->man),ruleP->fManufacturer.c_str())==0)) &&
4358       (ruleP->fModel.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->mod),ruleP->fModel.c_str())==0)) &&
4359       (ruleP->fOem.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->oem),ruleP->fOem.c_str())==0)) &&
4360       (ruleP->fFirmwareVers.empty() || (aDevInfP && ruleP->fFirmwareVers==smlPCDataToCharP(aDevInfP->fwv))) &&
4361       (ruleP->fSoftwareVers.empty() || (aDevInfP && ruleP->fSoftwareVers==smlPCDataToCharP(aDevInfP->swv))) &&
4362       (ruleP->fHardwareVers.empty() || (aDevInfP && ruleP->fHardwareVers==smlPCDataToCharP(aDevInfP->hwv))) &&
4363       (ruleP->fDevId.empty() || (aDevInfP && ruleP->fDevId==smlPCDataToCharP(aDevInfP->devid))) &&
4364       (ruleP->fDevTyp.empty() || (aDevInfP && ruleP->fDevTyp==smlPCDataToCharP(aDevInfP->devtyp)))
4365     ) {
4366       // found matching rule
4367       PDEBUGPRINTFX(DBG_HOT,("Found <remoterule> '%s' matching for this peer",ruleP->getName()));
4368       // remember it
4369       fActiveRemoteRules.push_back(ruleP);
4370       // add included subrules
4371                   TRemoteRulesList::iterator spos;
4372                 for(spos=ruleP->fSubRulesList.begin();spos!=ruleP->fSubRulesList.end();spos++) {
4373               fActiveRemoteRules.push_back(*spos);
4374               PDEBUGPRINTFX(DBG_HOT,("- rule also activates sub-rule '%s'",(*spos)->getName()));
4375                         }
4376       // if this rule is final, don't check for further matches
4377       if (ruleP->fFinalRule) break;
4378     }  
4379   }
4380   // process activated rules and subrules
4381   for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {      
4382     // activate this rule
4383     TRemoteRuleConfig *ruleP = *pos;
4384     // - apply options that have a value
4385     if (ruleP->fLegacyMode>=0) fLegacyMode = ruleP->fLegacyMode;
4386     if (ruleP->fLenientMode>=0) fLenientMode = ruleP->fLenientMode;
4387     if (ruleP->fLimitedFieldLengths>=0) fLimitedRemoteFieldLengths = ruleP->fLimitedFieldLengths;
4388     if (ruleP->fDontSendEmptyProperties>=0) fDontSendEmptyProperties = ruleP->fDontSendEmptyProperties;
4389     if (ruleP->fDoQuote8BitContent>=0) fDoQuote8BitContent = ruleP->fDoQuote8BitContent;
4390     if (ruleP->fDoNotFoldContent>=0) fDoNotFoldContent = ruleP->fDoNotFoldContent;
4391     if (ruleP->fNoReplaceInSlowsync>=0) fNoReplaceInSlowsync = ruleP->fNoReplaceInSlowsync;
4392     if (ruleP->fTreatRemoteTimeAsLocal>=0) fTreatRemoteTimeAsLocal = ruleP->fTreatRemoteTimeAsLocal;
4393     if (ruleP->fTreatRemoteTimeAsUTC>=0) fTreatRemoteTimeAsUTC = ruleP->fTreatRemoteTimeAsUTC;
4394     if (ruleP->fVCal10EnddatesSameDay>=0) fVCal10EnddatesSameDay = ruleP->fVCal10EnddatesSameDay;
4395     if (ruleP->fIgnoreDevInfMaxSize>=0) fIgnoreDevInfMaxSize = ruleP->fIgnoreDevInfMaxSize;
4396     if (ruleP->fIgnoreCTCap>=0) fIgnoreCTCap = ruleP->fIgnoreCTCap;
4397     if (ruleP->fDSPathInDevInf>=0) fDSPathInDevInf = ruleP->fDSPathInDevInf;
4398     if (ruleP->fDSCgiInDevInf>=0) fDSCgiInDevInf = ruleP->fDSCgiInDevInf;
4399     if (ruleP->fUpdateClientDuringSlowsync>=0) fUpdateClientDuringSlowsync = ruleP->fUpdateClientDuringSlowsync;
4400     if (ruleP->fUpdateServerDuringSlowsync>=0) fUpdateServerDuringSlowsync = ruleP->fUpdateServerDuringSlowsync;
4401     if (ruleP->fAllowMessageRetries>=0) fAllowMessageRetries = ruleP->fAllowMessageRetries;
4402     if (ruleP->fStrictExecOrdering>=0) fStrictExecOrdering = ruleP->fStrictExecOrdering;
4403     if (ruleP->fTreatCopyAsAdd>=0) fTreatCopyAsAdd = ruleP->fTreatCopyAsAdd;
4404     if (ruleP->fCompleteFromClientOnly>=0) fCompleteFromClientOnly = ruleP->fCompleteFromClientOnly;
4405     if (ruleP->fRequestMaxTime>=0) fRequestMaxTime = ruleP->fRequestMaxTime;
4406     if (ruleP->fDefaultOutCharset!=chs_unknown) fDefaultOutCharset = ruleP->fDefaultOutCharset;
4407     if (ruleP->fDefaultInCharset!=chs_unknown) fDefaultInCharset = ruleP->fDefaultInCharset;
4408     // - possibly override decisions that are otherwise made by session
4409     //   Note: this is not a single option because we had this before rule options were tristates.
4410     if (ruleP->fForceUTC>0) fRemoteCanHandleUTC=true;
4411     if (ruleP->fForceLocaltime>0) fRemoteCanHandleUTC=false;
4412     // - descriptive name for the device (for log)
4413     #ifndef MINIMAL_CODE
4414     if (!ruleP->fRemoteDescName.empty()) fRemoteDescName = ruleP->fRemoteDescName;
4415     #endif
4416     // - test for rejection
4417     if (ruleP->fRejectStatusCode!=DONT_REJECT) {
4418       // reject operation with this device
4419       sta = ruleP->fRejectStatusCode;
4420       PDEBUGPRINTFX(DBG_ERROR,("remote party rejected by <remoterule> '%s', status=%hd",ruleP->getName(),sta));
4421       AbortSession(sta,true);
4422       return sta;
4423     }
4424     // - execute rule script
4425     #ifdef SCRIPT_SUPPORT
4426     if (!ruleP->fRuleScriptTemplate.empty()) {
4427         // copy from template
4428         string ruleScript = ruleP->fRuleScriptTemplate;
4429       // resolve variable references
4430       TScriptContext::linkIntoContext(ruleScript,fSessionScriptContextP,this);
4431       // execute now
4432       PDEBUGPRINTFX(DBG_HOT,("Executing rulescript for rule '%s'",ruleP->getName()));
4433       TScriptContext::execute(
4434         fSessionScriptContextP,
4435         ruleScript,
4436         NULL, // context's function table
4437         NULL // datastore pointer needed for context
4438       );
4439     }
4440     #endif
4441   } // for all activated rules
4442   PDEBUGENDBLOCK("RemoteRules");
4443   #endif // NO_REMOTE_RULES
4444   // Final adjustments
4445   #ifndef NO_REMOTE_RULES
4446   if (fActiveRemoteRules.empty())
4447   #endif
4448   {
4449         // no remote rule (none found or mechanism excluded by NO_REMOTE_RULES)
4450     if (!aDevInfP) {
4451         // no devinf -> blind sync attempt: apply best-guess workaround settings
4452       // Note that a blind sync attempt means that the remote party is at least partly non-compliant, as we always request a devInf!
4453                   PDEBUGPRINTFX(DBG_ERROR,("No remote information available -> applying best-guess workaround behaviour options"));
4454       #ifndef MINIMAL_CODE
4455       // set device description
4456       fRemoteDescName = fRemoteName.empty() ? "[unknown remote]" : fRemoteName.c_str();
4457       fRemoteDescName += " (no devInf)";
4458       #endif // MINIMAL_CODE
4459       // switch on legacy behaviour (conservative preferred types)
4460       fLegacyMode = true;
4461       if (IS_CLIENT) {
4462         // Client case
4463         fRemoteCanHandleUTC = true; // Assume server can handle UTC (it is very improbable a server can't)
4464       }
4465       else {
4466         // Server case
4467         fRemoteCanHandleUTC = fSyncMLVersion==syncml_vers_1_0 ? true : false; // Assume client cannot handle UTC (it is likely a client can't, or at least can't properly, so localtime is safer)
4468         fLimitedRemoteFieldLengths = true; // assume limited client field length (almost all clients have limited length)
4469       }
4470     }
4471   }
4472   // show summary
4473   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Summary of all behaviour options (possibly modified by remote rule(s))"));
4474   #ifndef MINIMAL_CODE
4475   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Remote Description        : %s",fRemoteDescName.c_str()));
4476   #endif
4477   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Legacy mode               : %s",boolString(fLegacyMode)));
4478   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Lenient mode              : %s",boolString(fLenientMode)));
4479   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Limited Field Lengths     : %s",boolString(fLimitedRemoteFieldLengths)));
4480   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Do not send empty props   : %s",boolString(fDontSendEmptyProperties)));
4481   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Quote 8bit content        : %s",boolString(fDoQuote8BitContent)));
4482   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Prevent Content Folding   : %s",boolString(fDoNotFoldContent)));
4483   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- No replace in slowsync    : %s",boolString(fNoReplaceInSlowsync)));
4484   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat remote TZ as local  : %s",boolString(fTreatRemoteTimeAsLocal)));
4485   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat remote TZ as UTC    : %s",boolString(fTreatRemoteTimeAsUTC)));
4486   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Use 23:59:59 end dates    : %s",boolString(fVCal10EnddatesSameDay)));
4487   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Ignore field maxSize      : %s",boolString(fIgnoreDevInfMaxSize)));
4488   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Ignore CTCap              : %s",boolString(fIgnoreCTCap)));
4489   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- send DS path in devInf    : %s",boolString(fDSPathInDevInf)));
4490   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- send DS CGI in devInf     : %s",boolString(fDSCgiInDevInf)));
4491   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Update Client in slowsync : %s",boolString(fUpdateClientDuringSlowsync)));
4492   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Update Server in slowsync : %s",boolString(fUpdateServerDuringSlowsync)));
4493   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Allow message retries     : %s",boolString(fAllowMessageRetries)));
4494   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Strict SyncML exec order  : %s",boolString(fStrictExecOrdering)));
4495   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Treat copy like add       : %s",boolString(fTreatCopyAsAdd)));
4496   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Complete From-Client-Only : %s",boolString(fCompleteFromClientOnly)));
4497   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Remote can handle UTC     : %s",boolString(fRemoteCanHandleUTC)));
4498   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Max Request time [sec]    : %ld",static_cast<long>(fRequestMaxTime)));
4499   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Content output charset    : %s",MIMECharSetNames[fDefaultOutCharset]));
4500   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Content input charset     : %s",MIMECharSetNames[fDefaultInCharset]));
4501   // done
4502   return LOCERR_OK;
4503 } // TSyncSession::checkRemoteSpecifics
4504
4505
4506 #ifndef NO_REMOTE_RULES
4507
4508 // check if given rule (by name, or if aRuleName=NULL by rule pointer) is active
4509 bool TSyncSession::isActiveRule(cAppCharP aRuleName, TRemoteRuleConfig *aRuleP)
4510 {
4511   TRemoteRulesList::iterator pos;
4512   for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {
4513         if (
4514         (aRuleName==NULL && (*pos)==aRuleP) || // match by pointer...
4515       (strucmp(aRuleName,(*pos)->getName())==0) // ...or name
4516     )
4517         return true;
4518   }
4519   // no match
4520   return false;
4521 } // TSyncSession::isActiveRule
4522
4523 #endif // NO_REMOTE_RULES
4524
4525
4526 // access to config
4527 TSessionConfig *TSyncSession::getSessionConfig(void)
4528 {
4529   TSessionConfig *scP;
4530   GET_CASTED_PTR(scP,TSessionConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TSessionConfig","sss1"));
4531   return scP;
4532 } // TSyncSession::getSessionConfig
4533
4534
4535
4536 // process a Map command in context of session
4537 bool TSyncSession::processMapCommand(
4538   SmlMapPtr_t aMapCommandP,       // the map command contents
4539   TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
4540   bool &aQueueForLater
4541 )
4542 {
4543   // if not overridden, we cannot process Map
4544   aStatusCommand.setStatusCode(403);
4545   ADDDEBUGITEM(aStatusCommand,"Map command not allowed in this context");
4546   return false; // failed
4547 } // TSyncSession::processMapCommand
4548
4549
4550 // called to issue custom get and put commands
4551 // may issue custom get and put commands
4552 void TSyncSession::issueCustomGetPut(bool aGotDevInf, bool aSentDevInf)
4553 {
4554   #ifdef SCRIPT_SUPPORT
4555   // call script that might issue GETs and PUTs
4556   // - set up context
4557   TGetPutResultFuncContext ctx;
4558   ctx.isPut=false;
4559   ctx.canIssue=true;
4560   ctx.statuscode=0;
4561   ctx.itemURI.erase();
4562   ctx.itemData.erase();
4563   ctx.metaType.erase();
4564   // - execute
4565   TScriptContext::execute(
4566     fSessionScriptContextP,
4567     getSessionConfig()->fCustomGetPutScript,
4568     &GetPutResultFuncTable,
4569     &ctx // caller context
4570   );
4571   #endif
4572 } // TSyncSession::issueCustomGetPut
4573
4574
4575 // called to issue custom put commands at end of session
4576 // may issue custom put commands (gets don't make sense at end of a session)
4577 void TSyncSession::issueCustomEndPut(void)
4578 {
4579   #ifdef SCRIPT_SUPPORT
4580   // call script that might issue GETs and PUTs
4581   // - set up context
4582   TGetPutResultFuncContext ctx;
4583   ctx.isPut=false;
4584   ctx.canIssue=true;
4585   ctx.statuscode=0;
4586   ctx.itemURI.erase();
4587   ctx.itemData.erase();
4588   ctx.metaType.erase();
4589   // - execute
4590   TScriptContext::execute(
4591     fSessionScriptContextP,
4592     getSessionConfig()->fCustomEndPutScript,
4593     &GetPutResultFuncTable,
4594     &ctx // caller context
4595   );
4596   #endif
4597 } // TSyncSession::issueCustomEndPut
4598
4599
4600
4601
4602
4603 // called to process unknown get item, may return a Results command. Must set status to non-404 if get could be served
4604 // (may be overridden by descendants, only called if no descendant can handle an item)
4605 TResultsCommand *TSyncSession::processGetItem(const char *aLocUri, TGetCommand *aGetCommandP, SmlItemPtr_t aGetItemP, TStatusCommand &aStatusCommand)
4606 {
4607   TResultsCommand *resultsCmdP = NULL;
4608   #ifdef SCRIPT_SUPPORT
4609   // first check if script handles it
4610   // - set up context
4611   TGetPutResultFuncContext ctx;
4612   ctx.isPut=false;
4613   ctx.canIssue=false;
4614   ctx.statuscode=aStatusCommand.getStatusCode();
4615   ctx.itemURI=aLocUri;
4616   ctx.itemData.erase();
4617   // - get meta type of item, if any
4618   AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aGetItemP->meta)));
4619   if (ctx.metaType.empty()) {
4620     // none in item, get from command
4621     AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aGetCommandP->getMeta())));
4622   }
4623   // - execute
4624   bool hasResult =
4625     TScriptContext::executeTest(
4626       false, // do not assume script handles the GET
4627       fSessionScriptContextP,
4628       getSessionConfig()->fCustomGetHandlerScript,
4629       &GetPutResultFuncTable,
4630       &ctx // caller context
4631     );
4632   // - update status, anyway
4633   aStatusCommand.setStatusCode(ctx.statuscode);
4634   // - create result, if script decides so
4635   if (hasResult) {
4636     // script returns true, so it has handled the GET command
4637     // - create a result command (%%% currently GET command itself does not carry src/targ URIs, only item does)
4638     resultsCmdP = new TResultsCommand(this,aGetCommandP,NULL,NULL);
4639     // - create data item
4640     SmlItemPtr_t resItemP = newItem();
4641     // - source is get item's URI reflected (if not changed by script)
4642     resItemP->source=newLocation(ctx.itemURI.c_str());
4643     // - data is just string
4644     resItemP->data = newPCDataString(ctx.itemData);
4645     // - add item to command
4646     resultsCmdP->addItem(resItemP);
4647     // - set result command meta if not empty string
4648     resultsCmdP->setMeta(newMetaType(ctx.metaType.c_str()));
4649     // get item handled, return
4650     return resultsCmdP;
4651   }
4652   #endif
4653   // look for ./devinf10 special case
4654   if (strucmp(aLocUri,SyncMLDevInfNames[getSyncMLVersion()])==0) {
4655     // status is ok
4656     aStatusCommand.setStatusCode(200);
4657     // prepare a devinf10 <Result>
4658     resultsCmdP = new TDevInfResultsCommand(this,aGetCommandP);
4659   }
4660   return resultsCmdP;
4661 } // TSyncSession::processGetItem
4662
4663
4664 // - put and results command processing
4665 //   (may be overridden by descendants, only called if no descendant can handle an item)
4666 void TSyncSession::processPutResultItem(bool aIsPut, const char *aLocUri, TSmlCommand *aPutResultsCommandP, SmlItemPtr_t aPutResultsItemP, TStatusCommand &aStatusCommand)
4667 {
4668   localstatus sta = aStatusCommand.getStatusCode();
4669   #ifdef SCRIPT_SUPPORT
4670   // first check if script handles it
4671   // - set up context
4672   TGetPutResultFuncContext ctx;
4673   ctx.isPut=aIsPut;
4674   ctx.canIssue=false;
4675   ctx.statuscode=sta;
4676   ctx.itemURI=aLocUri;
4677   // - get data of item, if any
4678   smlPCDataToStringObj(aPutResultsItemP->data,ctx.itemData);
4679   // - get meta type of item, if any
4680   AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aPutResultsItemP->meta)));
4681   if (ctx.metaType.empty()) {
4682     // none in item, get from command
4683     AssignString(ctx.metaType,smlMetaTypeToCharP(smlPCDataToMetInfP(aPutResultsCommandP->getMeta())));
4684   }
4685   // - execute
4686   bool hasProcessed =
4687     TScriptContext::executeTest(
4688       false, // do not assume script handles the PUT or RESULT
4689       fSessionScriptContextP,
4690       getSessionConfig()->fCustomPutResultHandlerScript,
4691       &GetPutResultFuncTable,
4692       &ctx // caller context
4693     );
4694   // update status code
4695   sta=ctx.statuscode;
4696   if (sta!=LOCERR_OK)
4697     aStatusCommand.setStatusCode(syncmlError(sta));
4698   // if processed, return now
4699   if (hasProcessed)
4700     return;
4701   #endif
4702   // check for ./devinfXX
4703   if (strucmp(aLocUri,SyncMLDevInfNames[getSyncMLVersion()])==0) {
4704     // remote is sending DevInf, receive it
4705     SmlDevInfDevInfPtr_t devinfP = smlPCDataToDevInfP(aPutResultsItemP->data);
4706     // save received devinf (if database supports it)
4707     saveRemoteDevInf(getRemoteURI(),devinfP);
4708     // analyze
4709     aStatusCommand.setStatusCode(200); // assume ok
4710     sta=analyzeRemoteDevInf(devinfP);
4711     if (sta!=LOCERR_OK)
4712       aStatusCommand.setStatusCode(syncmlError(sta));
4713   }
4714   else {
4715     // unknown
4716     PDEBUGPRINTFX(DBG_ERROR,(
4717       "Unknown %s-command with URI=%s received, returning default status=%hd",
4718       aIsPut ? "PUT" : "RESULTS",
4719       aLocUri,
4720       sta
4721     ));
4722   }
4723 } // TSyncSession::processPutResultItem
4724
4725
4726
4727 // process an alert item in context of session
4728 // Most handling takes place in derived classes,
4729 // this base class only implements basic stuff
4730 // - returns command to be issued after issuing status, NULL if none
4731 TSmlCommand *TSyncSession::processAlertItem(
4732   uInt16 aAlertCode,   // alert code
4733   SmlItemPtr_t aItemP, // alert item to be processed (as one alert can have multiple items)
4734   SmlCredPtr_t aCredP, // alert cred element, if any
4735   TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
4736   TLocalEngineDS *&aLocalDataStoreP // receives datastore pointer, if alert affects a datastore
4737 )
4738 {
4739   // no alert response command by default
4740   TSmlCommand *alertresponsecmdP=NULL;
4741   TLocalEngineDS *datastoreP;
4742   string optionsCGI,identifyingTargetURI;
4743
4744   // dispatch numeric alerts
4745   switch (aAlertCode) {
4746     // sync alerts
4747     case 200:
4748     case 201:
4749     case 202:
4750     case 203:
4751     case 204:
4752     case 205:
4753     // Sync resume alert
4754     case 225:
4755       // Synchronisation initialisation alerts
4756       // - test if context is ok
4757       if (fIncomingState!=psta_init && fIncomingState!=psta_initsync) {
4758         // Sync alert only allowed in init package or combined init/sync
4759         PDEBUGPRINTFX(DBG_ERROR,(
4760           "Sync Alert not allowed with incoming package state='%s'",
4761           PackageStateNames[fIncomingState]
4762         ));
4763         aStatusCommand.setStatusCode(403); // forbidden
4764         ADDDEBUGITEM(aStatusCommand,"Sync Alert only allowed in init package");
4765         return NULL; // no alert sent back
4766       }
4767       // find requested database by URI
4768       datastoreP = findLocalDataStoreByURI(
4769         smlSrcTargLocURIToCharP(aItemP->target), // target as sent from remote
4770         &optionsCGI, // options, if any
4771         &identifyingTargetURI // identifying part of URI (CGI removed)
4772       );
4773       if (!datastoreP) {
4774         // no such local datastore
4775         aStatusCommand.setStatusCode(404); // not found
4776       }
4777       else {
4778         // save alerted datastore pointer (will be returned to caller, which is TAlertCommand)
4779         aLocalDataStoreP=datastoreP;
4780         // get anchors
4781         const char *nextRemoteAnchor = smlMetaNextAnchorToCharP(smlPCDataToMetInfP(aItemP->meta));
4782         if (nextRemoteAnchor==NULL) nextRemoteAnchor=""; // some remotes may send NO anchor
4783         const char *lastRemoteAnchor = smlMetaLastAnchorToCharP(smlPCDataToMetInfP(aItemP->meta));
4784         if (lastRemoteAnchor==NULL) lastRemoteAnchor=""; // some remotes may send NO anchor
4785         // get URIs
4786         const char *targetURI = smlSrcTargLocURIToCharP(aItemP->target);
4787         const char *sourceURI = smlSrcTargLocURIToCharP(aItemP->source);
4788         // get Filter
4789         SmlFilterPtr_t targetFilter = aItemP->target ? aItemP->target->filter : NULL;
4790         // alert datastore of requested sync
4791         // - let datastore process alert and generate additional alert if needed
4792         //   NOTE: this might generate a PUT command if remote needs to see our
4793         //         devInf (config changed since last sync)
4794         alertresponsecmdP=datastoreP->engProcessSyncAlert(
4795           NULL,            // not as subdatastore
4796           aAlertCode,       // the alert code
4797           lastRemoteAnchor, // last anchor of client
4798           nextRemoteAnchor, // next anchor of client
4799           targetURI,        // target as sent from remote
4800           identifyingTargetURI.c_str(), // identifying part of URI (relative, options removed)
4801           optionsCGI.c_str(),           // extracted options (e.g. filtering) CGI
4802           targetFilter,     // DS 1.2 filter, if any (can be NULL if none)
4803           sourceURI,        // source URI
4804           aStatusCommand    // status that might be modified
4805         );
4806         // echo next anchor sent with item back in status
4807         // %%% specs say that only next anchor must be echoed, SCTS echoes both
4808         SmlItemPtr_t itemP = newItem(); // empty item
4809         // NOTE: anchor is MetInf, but is echoed in DATA part of item, not META!
4810         itemP->data = newMetaAnchor(nextRemoteAnchor,NULL); // only next (like specs)
4811         aStatusCommand.addItem(itemP); // add it to status
4812       }
4813       break;
4814     case 224 :
4815       // Suspend alert
4816       SuspendSession(514);
4817       break;
4818     case 100 :
4819       // DISPLAY
4820       PDEBUGPRINTFX(DBG_HOT,(
4821         "---------------- DISPLAY ALERT (100): %s",
4822         smlPCDataToCharP(aItemP->data)
4823       ));
4824       // show it on the console
4825       CONSOLEPRINTF((
4826         "***** Message from Remote: %s",
4827         smlPCDataToCharP(aItemP->data)
4828       ));
4829       // callback to allow GUI clients to display the message
4830       if (!OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_display100,NULL,uIntPtr(smlPCDataToCharP(aItemP->data)),0,0)) {
4831         // user answered no to our question "continue?"
4832         aStatusCommand.setStatusCode(514); // cancelled
4833         // Do NOT abort the session, so give the server a chance to do someting more sensible based on the 514 status.
4834         aStatusCommand.addItemString("User abort in response to Alert 100 message");
4835         PDEBUGPRINTFX(DBG_ERROR,("User abort after seeing Alert 100 message: %s",smlPCDataToCharP(aItemP->data)));
4836       }
4837       break;
4838     case 223:
4839       // Chunking error: missing end of chunk
4840       aStatusCommand.setStatusCode(223);
4841       aStatusCommand.addItemString("Missing end of chunk");
4842       PDEBUGPRINTFX(DBG_ERROR,(
4843         "Warning: Alert Code 223 -> Missing end of chunk for item localid='%s', remoteid='%s'",
4844         smlSrcTargLocURIToCharP(aItemP->target),
4845         smlSrcTargLocURIToCharP(aItemP->source)
4846       ));
4847       break;
4848     default :
4849       // unknown alert code
4850       aStatusCommand.setStatusCode(406);
4851       aStatusCommand.addItemString("Unimplemented Alert Code");
4852       PDEBUGPRINTFX(DBG_ERROR,("Unimplemented Alert Code %hd -> Status 406",aAlertCode));
4853       break;
4854   } // switch fAlertCode
4855   // return command generated (or NULL if none)
4856   return alertresponsecmdP;
4857 } // TSyncSession::processAlertItem
4858
4859
4860
4861 #ifdef SYDEBUG
4862   #define XML_TRANSLATION_ENABLED
4863 #else
4864   #undef XML_TRANSLATION_ENABLED
4865 #endif
4866
4867 #ifdef SYDEBUG
4868
4869 void TSyncSession::XMLTranslationIncomingStart(void)
4870 {
4871   // start translation instances
4872   #ifdef XML_TRANSLATION_ENABLED
4873   if (fXMLtranslate && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
4874     DEBUGPRINTFX(DBG_EXOTIC,("Initializing incoming XML translation instance"))
4875     if (!getSyncAppBase()->newSmlInstance(
4876       SML_XML,
4877       getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
4878       fIncomingXMLInstance
4879     )) {
4880       // if instance cannot be created, turn off XML translation to avoid crashes
4881       fXMLtranslate=false;
4882       PDEBUGPRINTFX(DBG_ERROR,("XML translation disabled because of lacking memory"))
4883     }
4884   }
4885   else
4886     fXMLtranslate=false;
4887   #endif
4888 } // TSyncSession::XMLTranslationIncomingStart
4889
4890
4891 void TSyncSession::XMLTranslationOutgoingStart(void)
4892 {
4893   #ifdef XML_TRANSLATION_ENABLED
4894   // start translation instances
4895   if (fXMLtranslate && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
4896     DEBUGPRINTFX(DBG_EXOTIC,("Initializing outgoing XML translation instance"))
4897     if (!getSyncAppBase()->newSmlInstance(
4898       SML_XML,
4899       getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
4900       fOutgoingXMLInstance
4901     )) {
4902       // if instance cannot be created, turn off XML translation to avoid crashes
4903       fXMLtranslate=false;
4904       PDEBUGPRINTFX(DBG_ERROR,("XML translation disabled because of lacking memory"))
4905     }
4906   }
4907   else
4908     fXMLtranslate=false;
4909   #endif
4910 } // TSyncSession::XMLTranslationOutgoingStart
4911
4912
4913 /// @todo
4914 /// rewrite this to use a TDbgOut object to write stuff
4915 // finish and output XML translation of incoming traffic
4916 void TSyncSession::XMLTranslationIncomingEnd(void)
4917 {
4918   #ifdef XML_TRANSLATION_ENABLED
4919   if (fIncomingXMLInstance && fXMLtranslate) {
4920     // write XML translation of input and output to files
4921     DEBUGPRINTFX(DBG_EXOTIC,("XML translation enabled..."))
4922     MemPtr_t XMLtext;
4923     MemSize_t XMLsize;
4924     string fname;
4925     // - incoming
4926     // - get XML
4927     DEBUGPRINTFX(DBG_EXOTIC,("- Writing incoming XML translation"))
4928     XMLtext=NULL; XMLsize=0;
4929     if (smlLockReadBuffer(fIncomingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
4930       // save to file
4931       TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
4932       if (dbgOutP) {
4933         // create base file name (trm = translated message)
4934         string dumpfilename;
4935         StringObjPrintf(dumpfilename,
4936           "%s_trm%03ld_%03ld_incoming",
4937           getDbgLogger()->getDebugPath(), // path + session log base name
4938           (long)getLastIncomingMsgID(),
4939           (long)++fDumpCount // to make sure it is unique even in case of retries
4940         );
4941         // open file in raw mode
4942         if (dbgOutP->openDbg(
4943           dumpfilename.c_str(),
4944           ".xml",
4945           dbgflush_none,
4946           false, // append to existing if any
4947           true // raw mode
4948         )) {
4949           // write out the entire message
4950           dbgOutP->putRawData(XMLtext, XMLsize);
4951           // close the file
4952           dbgOutP->closeDbg();
4953           // add a link into the session file to immediately get the file
4954           PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
4955             "Incoming %sXML message msgID=%ld &html;<a href=\"%s_trm%03ld_%03ld_incoming.xml\" target=\"_blank\">&html;saved as XML translation&html;</a>&html;",
4956             getEncoding()==SML_XML ? "" : "WB",
4957             (long)getLastIncomingMsgID(),
4958             getDbgLogger()->getDebugFilename(), // session log base name
4959             (long)getLastIncomingMsgID(),
4960             (long)fDumpCount
4961           ));
4962         }
4963         else {
4964           PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
4965         }
4966         delete dbgOutP;
4967       }
4968     }
4969   }
4970   if (fIncomingXMLInstance) {
4971     // finally free instance
4972     getSyncAppBase()->freeSmlInstance(fIncomingXMLInstance);
4973     fIncomingXMLInstance=NULL;
4974   }
4975   #endif
4976 } // TSyncSession::XMLTranslationIncomingEnd
4977
4978
4979 // finish and output XML translation of outgoing traffic
4980 /// @todo
4981 /// rewrite this to use a TDbgOut object to write stuff
4982 void TSyncSession::XMLTranslationOutgoingEnd(void)
4983 {
4984   #ifdef XML_TRANSLATION_ENABLED
4985   if (fOutgoingXMLInstance && fXMLtranslate) {
4986     // write XML translation of input and output to files
4987     DEBUGPRINTFX(DBG_EXOTIC,("XML translation enabled..."))
4988     MemPtr_t XMLtext;
4989     MemSize_t XMLsize;
4990     string fname;
4991     // - outgoing
4992     // write only if session is debug-enabled
4993     // - get XML
4994     DEBUGPRINTFX(DBG_EXOTIC,("- Writing outgoing XML translation"))
4995     XMLtext=NULL; XMLsize=0;
4996     if (smlLockReadBuffer(fOutgoingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
4997       // save to file
4998       TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
4999       if (dbgOutP) {
5000         // create base file name (trm = translated message)
5001         string dumpfilename;
5002         StringObjPrintf(dumpfilename,
5003           "%s_trm%03ld_%03ld_outgoing",
5004           getDbgLogger()->getDebugPath(), // path + session log base name
5005           (long)getOutgoingMsgID(),
5006           (long)++fDumpCount // to make sure it is unique even in case of retries
5007         );
5008         // open file in raw mode
5009         if (dbgOutP->openDbg(
5010           dumpfilename.c_str(),
5011           ".xml",
5012           dbgflush_none,
5013           false, // append to existing if any
5014           true // raw mode
5015         )) {
5016           // write out the entire message
5017           dbgOutP->putRawData(XMLtext, XMLsize);
5018           // close the file
5019           dbgOutP->closeDbg();
5020           // add a link into the session file to immediately get the file
5021           PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
5022             "Outgoing %sXML message msgID=%ld &html;<a href=\"%s_trm%03ld_%03ld_outgoing.xml\" target=\"_blank\">&html;saved as XML translation&html;</a>&html;",
5023             getEncoding()==SML_XML ? "" : "WB",
5024             (long)getOutgoingMsgID(),
5025             getDbgLogger()->getDebugFilename(), // session log base name
5026             (long)getOutgoingMsgID(),
5027             (long)fDumpCount
5028           ));
5029         }
5030         else {
5031           PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
5032         }
5033         delete dbgOutP;
5034       }
5035     }
5036   }
5037   if (fOutgoingXMLInstance) {
5038     // finally free instance
5039     getSyncAppBase()->freeSmlInstance(fOutgoingXMLInstance);
5040     fOutgoingXMLInstance=NULL;
5041   }
5042   #endif
5043 } // TSyncSession::XMLTranslationOutgoingEnd
5044
5045
5046 // dump message from specified buffer
5047 void TSyncSession::DumpSyncMLBuffer(MemPtr_t aBuffer, MemSize_t aBufSize, bool aOutgoing, Ret_t aDecoderError)
5048 {
5049   #ifdef MSGDUMP
5050   // log message currently in SML buffer
5051   if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
5052     TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
5053     if (dbgOutP) {
5054       // create base file name
5055       string dumpfilename;
5056       // regular message, 
5057       StringObjPrintf(dumpfilename,
5058         "%s_msg%03ld_%03ld_%sing",
5059         getDbgLogger()->getDebugPath(), // path + session log base name
5060         (long)(aOutgoing ? getOutgoingMsgID() : getLastIncomingMsgID()+1), // just generated msgID / expected next incoming ID
5061         (long)++fDumpCount, // to make sure it is unique even in case of retries
5062         aOutgoing ? "outgo" : "incom"
5063       );
5064       if (aDecoderError) {
5065         // append error code
5066         StringObjAppendPrintf(dumpfilename,"_ERR_0x%04X",aDecoderError);
5067       }
5068       // open file in raw mode
5069       if (dbgOutP->openDbg(
5070         dumpfilename.c_str(),
5071         getEncoding()==SML_XML ? ".xml" : ".wbxml",
5072         dbgflush_none,
5073         false, // append to existing if any
5074         true // raw mode
5075       )) {
5076         // write out the entire message
5077         dbgOutP->putRawData(aBuffer, aBufSize);
5078         // close the file
5079         dbgOutP->closeDbg();
5080         // add a link into the session file to immediately get the file if it is XML
5081         if (getEncoding()==SML_XML) {
5082           PDEBUGPRINTFX(DBG_HOT+DBG_PROTO,(
5083             "%sing XML message msgID=%ld &html;<a href=\"%s_msg%03ld_%03ld_%sing.xml\" target=\"_blank\">&html;dumped to file&html;</a>&html;",
5084             aOutgoing ? "Outgo" : "Incom",
5085             (long)getOutgoingMsgID(),
5086             getDbgLogger()->getDebugFilename(), // session log base name
5087             (long)(aOutgoing ? getOutgoingMsgID() : getLastIncomingMsgID()+1), // just generated msgID / expected next incoming ID
5088             (long)fDumpCount,
5089             aOutgoing ? "outgo" : "incom"
5090           ));
5091         }
5092       }
5093       else {
5094         PDEBUGPRINTFX(DBG_ERROR,("Cannot write <msgdump> file"));
5095       }
5096       delete dbgOutP;
5097     }
5098   }
5099   #endif // MSGDUMP
5100 } // TSyncSession::DumpSyncMLBuffer
5101
5102
5103 // Dump message in SML buffer to file
5104 void TSyncSession::DumpSyncMLMessage(bool aOutgoing)
5105 {
5106   // dump message if needed
5107   #ifdef MSGDUMP
5108   // log message currently in SML buffer
5109   if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
5110     // peek into buffer
5111     MemPtr_t data;
5112     MemSize_t datasize;
5113     if (smlPeekMessageBuffer(getSmlWorkspaceID(), aOutgoing, &data, &datasize)==SML_ERR_OK) {           
5114       DumpSyncMLBuffer(data,datasize,aOutgoing,SML_ERR_OK);
5115     }
5116   }
5117   #endif // MSGDUMP
5118 } // TSyncSession::DumpSyncMLMessage
5119
5120
5121 #endif // SYDEBUG
5122
5123
5124 // SyncML Toolkit callback handlers
5125 // ================================
5126
5127
5128 // start of SyncML message
5129 Ret_t TSyncSession::StartMessage(SmlSyncHdrPtr_t aContentP)
5130 {
5131   #ifdef SYDEBUG
5132   fSessionLogger.DebugDefineMainThread();
5133   #endif
5134   SessionUsed(); // session used
5135   fLastRequestStarted = getSystemNowAs(TCTX_UTC); // request started
5136   fMessageRetried = false; // we assume no message retry
5137   MP_SHOWCURRENT(DBG_PROFILE,"Start of incoming Message");
5138   TP_START(fTPInfo,TP_general); // could be new thread
5139   #ifdef EXPIRES_AFTER_DATE
5140   if (IS_SERVER) {
5141     // set 1/4 of the date here
5142     fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
5143   }
5144   #endif // EXPIRES_AFTER_DATE
5145   // dump it if configured
5146   // Note: this must happen here before answer writing to the instance buffer starts, as otherwise
5147   //       the already consumed part of the buffer might get overwritten (the SyncML message header in this case).
5148   #ifdef SYDEBUG
5149   DumpSyncMLMessage(false); // incoming
5150   #endif
5151   if (IS_SERVER) {
5152     // for server, SyncML_Outgoing is started here, as SyncML_Incoming ends before SyncML_Outgoing
5153     // but for client, document exchange starts with outgoing message
5154     PDEBUGBLOCKDESC("SyncML_Outgoing","preparing for response before starting to analyze new incoming message");
5155   }
5156   PDEBUGBLOCKFMT(("SyncML_Incoming","Starting to analyze incoming message",
5157     "RequestNo=%ld|SySyncVers=%d.%d.%d.%d",(long)fSyncAppBaseP->requestCount(),
5158     SYSYNC_VERSION_MAJOR,
5159     SYSYNC_VERSION_MINOR,
5160     SYSYNC_SUBVERSION,
5161     SYSYNC_BUILDNUMBER
5162   ));
5163   PDEBUGPRINTFX(DBG_HOT,(
5164     "=================> Starting to analyze incoming message, SySync V%d.%d.%d.%d, RequestNo=%ld",
5165     SYSYNC_VERSION_MAJOR,
5166     SYSYNC_VERSION_MINOR,
5167     SYSYNC_SUBVERSION,
5168     SYSYNC_BUILDNUMBER,
5169     (long)fSyncAppBaseP->requestCount()
5170   ));
5171   #ifdef SYDEBUG
5172   // Start incoming translation before decoding header
5173   XMLTranslationIncomingStart();
5174   if (fXMLtranslate && fIncomingXMLInstance) {
5175     // Note: we must find the SyncML version in advance, as fSyncMLVersion is not yet valid here
5176     sInt16 hdrVers;
5177     StrToEnum(SyncMLVerDTDNames,numSyncMLVersions,hdrVers,smlPCDataToCharP(aContentP->version));
5178     smlStartMessageExt(fIncomingXMLInstance,aContentP,SmlVersionCodes[hdrVers]);
5179   }
5180   #endif
5181   // update encoding
5182   smlGetEncoding(fSmlWorkspaceID,&fEncoding);
5183   // create command
5184   TSyncHeader *syncheaderP;
5185   MP_NEW(syncheaderP,DBG_OBJINST,"TSyncHeader",TSyncHeader(this,aContentP));
5186   // execute it (special case for header)
5187   return processHeader(syncheaderP);
5188 } // TSyncSession::StartMessage
5189
5190
5191 // special entry point to prematurely abort processing of a incoming message
5192 // and cause the necessary cleanup
5193 void TSyncSession::CancelMessageProcessing(void)
5194 {
5195   #ifdef SYDEBUG
5196   // Now dump XML translation of incoming message (as far as it was processed at all)
5197   XMLTranslationIncomingEnd();
5198   #endif
5199   // Show premature end of input processing
5200   PDEBUGPRINTFX(DBG_HOT,(
5201     "=================> Aborted processing message #%ld, request=%ld",
5202     (long)fIncomingMsgID,
5203     (long)fSyncAppBaseP->requestCount()
5204   ));
5205 } // TSyncSession::CancelMessageProcessing
5206
5207
5208 Ret_t TSyncSession::EndMessage(Boolean_t final)
5209 {
5210   #ifdef SYDEBUG
5211   // generate XML translation
5212   if (fXMLtranslate && fIncomingXMLInstance)
5213     smlEndMessage(fIncomingXMLInstance,final);
5214   // Now dump XML translation of incoming message
5215   XMLTranslationIncomingEnd();
5216   #endif
5217   // End of incoming message
5218   PDEBUGPRINTFX(DBG_HOT,(
5219     "=================> Finished processing incoming message #%ld (%sfinal), request=%ld",
5220     (long)fIncomingMsgID,
5221     final ? "" : "not ",
5222     (long)fSyncAppBaseP->requestCount()
5223   ));
5224   // start outgoing message if not already done so
5225   // Note: this should NOT happen, as EVERY message from the remote should contain a SyncHdr status which
5226   //       should have started the message already
5227   if (!fOutgoingStarted) {
5228         PDEBUGPRINTFX(DBG_ERROR,("Warning: incoming message #%ld did not contain a SyncHdr status (protocol violation)",(long)fIncomingMsgID));
5229     // try to continue by simply ignoring - might not always work out (e.g. when authorisation is not yet complete, this will fail) 
5230     issueHeader(false);
5231         }  
5232   // forget pending continue requests
5233   if (final)
5234     fNextMessageRequests=0; // no pending next message requests when a message is final
5235   // make sure peer gets devInf Put if needed (only if it didn't issue a GET)
5236   // Note: do it here because we have processed all commands (alerts) now but
5237   //       server response alerts are still in the fEndOfMessageCommands queue.
5238   //       This ensures that clients gets PUT before it gets ALERTs.
5239   if (!fRemoteGotDevinf && mustSendDevInf()) {
5240     // remote has not got devinf and should see it
5241     if (!getRootConfig()->fNeverPutDevinf) {
5242       // PUT devinf now
5243       PDEBUGPRINTFX(DBG_PROTO,("Remote must see our changed devInf -> creating PUT command"));
5244       TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
5245       issueRootPtr(putcmdP);
5246     }
5247     else {
5248       PDEBUGPRINTFX(DBG_PROTO,("Remote should see devinf, but PUT is suppressed: <neverputdevinf>"));
5249     }
5250   }
5251   // hook for placing custom GET and PUT
5252   if (!fCustomGetPutSent) {
5253     fCustomGetPutSent=true;
5254     issueCustomGetPut(fRemoteDevInfKnown,fRemoteGotDevinf);
5255   }
5256   // now issue all commands that may only be issued AFTER sending statuses for incoming commands
5257   TSmlCommandPContainer::iterator pos;
5258   while (true) {
5259     // first in list
5260     pos=fEndOfMessageCommands.begin();
5261     if (pos==fEndOfMessageCommands.end()) break; // done
5262     // take command out of the list
5263     TSmlCommand *cmdP=(*pos);
5264     fEndOfMessageCommands.erase(pos);
5265     PDEBUGPRINTFX(DBG_SESSION,("<--- Issuing command '%s' from EndOfMessage Queue",cmdP->getName()));
5266     // issue it (doesn't matter if cannot be sent with this message,
5267     //   it will then be moved into the fNextMessageCommands queue)
5268     issuePtr(cmdP,fNextMessageCommands,fInterruptedCommandP);
5269   }
5270   // now continue with package if it was discontinued in last message
5271   // %%% if (fNextMessageRequests>0) {
5272   // We have received a 222 Alert, so continue package now
5273   // %%% always continue, even if we didn't see a 222 alert
5274   ContinuePackageRoot();
5275   // %%% }
5276   // let client or server do what is needed
5277   if (fFakeFinalFlag) {
5278     PDEBUGPRINTFX(DBG_ERROR,("Warning: heavy workaround active - <final/> simulated to get resume without sync-from-client going"));
5279   }
5280   MessageEnded(final || fFakeFinalFlag);
5281   fFakeFinalFlag=false;
5282   #ifdef SYNCSTATUS_AT_SYNC_CLOSE
5283   // make sure sync status is disposed
5284   if (fSyncCloseStatusCommandP) delete fSyncCloseStatusCommandP;
5285   fSyncCloseStatusCommandP=NULL;
5286   #endif
5287   MP_SHOWCURRENT(DBG_PROFILE,"End of incoming message");
5288   // ok if no exception thrown
5289   return SML_ERR_OK;
5290 } // TSyncSession::EndMessage
5291
5292
5293
5294 Ret_t TSyncSession::StartSync(SmlSyncPtr_t aContentP)
5295 {
5296   #ifdef SYDEBUG
5297   // generate XML translation
5298   if (fXMLtranslate && fIncomingXMLInstance)
5299     smlStartSync(fIncomingXMLInstance,aContentP);
5300   #endif
5301   // create command object
5302   TSyncCommand *commandP = new TSyncCommand(this,fIncomingMsgID,aContentP);
5303   // process it
5304   return process(commandP);
5305 } // TSyncSession::StartSync
5306
5307
5308 Ret_t TSyncSession::EndSync(void)
5309 {
5310   #ifdef SYDEBUG
5311   // generate XML translation
5312   if (fXMLtranslate && fIncomingXMLInstance)
5313     smlEndSync(fIncomingXMLInstance);
5314   #endif
5315
5316   /* %%% old version: sync end is no command itself,
5317          makes queuing <sync> sequences for later processing
5318          impossible, so we made it be a separate command
5319   // process Sync End
5320   // %%% evtl. catch...
5321   PDEBUGPRINTFX(DBG_HOT,("End of <Sync> command"));
5322   // Note: do not call if previous Sync start might not have been processed
5323   if (!fIgnoreIncomingCommands) processSyncEnd();
5324   return SML_ERR_OK;
5325   */
5326   // create command object
5327   TSyncEndCommand *commandP = new TSyncEndCommand(this,fIncomingMsgID);
5328   // process it
5329   return process(commandP);
5330 } // TSyncSession::EndSync
5331
5332
5333 #ifdef ATOMIC_RECEIVE
5334 Ret_t TSyncSession::StartAtomic(SmlAtomicPtr_t aContentP)
5335 {
5336   #ifdef SYDEBUG
5337   // generate XML translation
5338   if (fXMLtranslate && fIncomingXMLInstance)
5339     smlStartAtomic(fIncomingXMLInstance,aContentP);
5340   #endif
5341   // NOTE from Specs: Nested Atomic commands are not legal. A nested Atomic
5342   //   command will generate an error 500 - command failed.
5343   // create command object
5344   // %%% create DUMMY command for now
5345   PDEBUGPRINTFX(DBG_HOT,("Start of Atomic bracket: return Status 406 unimplemented"));
5346   TUnimplementedCommand *commandP =
5347     new TUnimplementedCommand(
5348       this,
5349       fIncomingMsgID,
5350       aContentP->cmdID,
5351       0,
5352       scmd_copy,
5353       aContentP,
5354       406); // optional feature not supported
5355   // process it
5356   return process(commandP);
5357 } // TSyncSession::StartAtomic
5358
5359 Ret_t TSyncSession::EndAtomic(void)
5360 {
5361   #ifdef SYDEBUG
5362   // generate XML translation
5363   if (fXMLtranslate && fIncomingXMLInstance)
5364     smlEndAtomic(fIncomingXMLInstance);
5365   #endif
5366   // process Atomic end
5367   // %%% not implemented, just accept
5368   PDEBUGPRINTFX(DBG_HOT,("End of Atomic bracket"));
5369   return SML_ERR_OK;
5370 } // TSyncSession::EndAtomic
5371 #endif
5372
5373
5374 #ifdef SEQUENCE_RECEIVE
5375 Ret_t TSyncSession::StartSequence(SmlSequencePtr_t aContentP)
5376 {
5377   #ifdef SYDEBUG
5378   // generate XML translation
5379   if (fXMLtranslate && fIncomingXMLInstance)
5380     smlStartSequence(fIncomingXMLInstance,aContentP);
5381   #endif
5382   // %%% later, implement a nestable command object and derive Sequence,Atomic and Sync
5383   //     from it. Similar to nested command creation, maintain a chain of nested commands;
5384   //     session will have a pointer to most recent nest and ALL commands will have a pointer
5385   //     to owning command (or NULL if they are on root level).
5386   // Sequence is trivial as SySync executes command in sequence anyway
5387   // - simply keep track of nesting
5388   fSequenceNesting++;
5389   PDEBUGPRINTFX(DBG_HOT,("Start of Sequence bracket, nesting level is now %hd",fSequenceNesting));
5390   // get cmdid
5391   sInt32 cmdid;
5392   StrToLong(smlPCDataToCharP(aContentP->cmdID),cmdid);
5393   // make status
5394   TStatusCommand *statusCmdP = new TStatusCommand(
5395     this,                                 // associated session (for callbacks)
5396     cmdid,                                // referred-to command ID
5397     scmd_sequence,                        // referred-to command type (scmd_xxx)
5398     (aContentP->flags & SmlNoResp_f)!=0,  // set if no-Resp
5399     200                                   // status code
5400   ); // issue ok status
5401   // - return status
5402   issueRootPtr(statusCmdP);
5403   // - ok
5404   return SML_ERR_OK;
5405 } // TSyncSession::StartSequence
5406
5407
5408 Ret_t TSyncSession::EndSequence(void)
5409 {
5410   #ifdef SYDEBUG
5411   // generate XML translation
5412   if (fXMLtranslate && fIncomingXMLInstance)
5413     smlEndSequence(fIncomingXMLInstance);
5414   #endif
5415   // - keep track of nesting
5416   if (fSequenceNesting<1) {
5417     // error in nesting
5418     PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, MISSING PRECEEDING SEQUENCE START -> aborting session"));
5419     AbortSession(400,true); // bad nesting is severe, abort session
5420   }
5421   else {
5422     // nesting ok
5423     fSequenceNesting--;
5424     PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, nesting level is now %hd",fSequenceNesting));
5425   }
5426   return SML_ERR_OK;
5427 } // TSyncSession::EndSequence
5428 #endif
5429
5430
5431 Ret_t TSyncSession::AddCmd(SmlAddPtr_t aContentP)
5432 {
5433   #ifdef SYDEBUG
5434   // generate XML translation
5435   if (fXMLtranslate && fIncomingXMLInstance)
5436     smlAddCmd(fIncomingXMLInstance,aContentP);
5437   #endif
5438   // create SyncOp command object
5439   TSyncOpCommand *commandP = new TSyncOpCommand(
5440     this,
5441     fLocalSyncDatastoreP,
5442     fIncomingMsgID,
5443     sop_add,
5444     scmd_add,
5445     aContentP
5446   );
5447   // process it
5448   return process(commandP);
5449 } // TSyncSession::AddCmd
5450
5451
5452 Ret_t TSyncSession::AlertCmd(SmlAlertPtr_t aContentP)
5453 {
5454   #ifdef SYDEBUG
5455   // generate XML translation
5456   if (fXMLtranslate && fIncomingXMLInstance)
5457     smlAlertCmd(fIncomingXMLInstance,aContentP);
5458   #endif
5459   // create command object
5460   TAlertCommand *commandP = new TAlertCommand(this,fIncomingMsgID,aContentP);
5461   // process it
5462   return process(commandP);
5463 } // TSyncSession::AlertCmd
5464
5465
5466 Ret_t TSyncSession::DeleteCmd(SmlDeletePtr_t aContentP)
5467 {
5468   #ifdef SYDEBUG
5469   // generate XML translation
5470   if (fXMLtranslate && fIncomingXMLInstance)
5471     smlDeleteCmd(fIncomingXMLInstance,aContentP);
5472   #endif
5473   // determine type of delete
5474   TSyncOperation syncop;
5475   if (aContentP->flags & SmlArchive_f) syncop = sop_archive_delete;
5476   else if (aContentP->flags & SmlSftDel_f) syncop = sop_soft_delete;
5477   else syncop=sop_delete;
5478   // create SyncOp command object
5479   TSyncOpCommand *commandP = new TSyncOpCommand(
5480     this,
5481     fLocalSyncDatastoreP, // note that this one might be NULL in case previous sync command was delayed
5482     fIncomingMsgID,
5483     syncop,
5484     scmd_delete,
5485     aContentP
5486   );
5487   // process it
5488   return process(commandP);
5489 } // TSyncSession::DeleteCmd
5490
5491
5492 // process GET commands
5493 Ret_t TSyncSession::GetCmd(SmlGetPtr_t aContentP)
5494 {
5495   #ifdef SYDEBUG
5496   // generate XML translation
5497   if (fXMLtranslate && fIncomingXMLInstance)
5498     smlGetCmd(fIncomingXMLInstance,aContentP);
5499   #endif
5500   // create command object
5501   TGetCommand *commandP = new TGetCommand(this,fIncomingMsgID,aContentP);
5502   // process it
5503   return process(commandP);
5504 } // TSyncSession::GetCmd
5505
5506
5507 Ret_t TSyncSession::PutCmd(SmlPutPtr_t aContentP)
5508 {
5509   #ifdef SYDEBUG
5510   // generate XML translation
5511   if (fXMLtranslate && fIncomingXMLInstance)
5512     smlPutCmd(fIncomingXMLInstance,aContentP);
5513   #endif
5514   // create command object
5515   TPutCommand *commandP = new TPutCommand(this,fIncomingMsgID,aContentP);
5516   // process it
5517   return process(commandP);
5518 } // TSyncSession::PutCmd
5519
5520
5521 #ifdef MAP_RECEIVE
5522 Ret_t TSyncSession::MapCmd(SmlMapPtr_t aContentP)
5523 {
5524   #ifdef SYDEBUG
5525   // generate XML translation
5526   if (fXMLtranslate && fIncomingXMLInstance)
5527     smlMapCmd(fIncomingXMLInstance,aContentP);
5528   #endif
5529   // create command object
5530   TMapCommand *commandP = new TMapCommand(this,fIncomingMsgID,aContentP);
5531   // process it
5532   return process(commandP);
5533 } // TSyncSession::MapCmd
5534 #endif
5535
5536
5537 #ifdef RESULT_RECEIVE
5538 Ret_t TSyncSession::ResultsCmd(SmlResultsPtr_t aContentP)
5539 {
5540   #ifdef SYDEBUG
5541   // generate XML translation
5542   if (fXMLtranslate && fIncomingXMLInstance)
5543     smlResultsCmd(fIncomingXMLInstance,aContentP);
5544   #endif
5545   // create command object
5546   TResultsCommand *commandP = new TResultsCommand(this,fIncomingMsgID,aContentP);
5547   // process it
5548   return process(commandP);
5549 } // TSyncSession::ResultsCmd
5550 #endif
5551
5552
5553 Ret_t TSyncSession::StatusCmd(SmlStatusPtr_t aContentP)
5554 {
5555   #ifdef SYDEBUG
5556   // generate XML translation
5557   if (fXMLtranslate && fIncomingXMLInstance)
5558     smlStatusCmd(fIncomingXMLInstance,aContentP);
5559   #endif
5560   // create command object
5561   TStatusCommand *statuscommandP = new TStatusCommand(this,fIncomingMsgID,aContentP);
5562   // handle status (search for command that waits for this status)
5563   return handleStatus(statuscommandP);
5564 } // TSyncSession::StatusCmd
5565
5566
5567 Ret_t TSyncSession::ReplaceCmd(SmlReplacePtr_t aContentP)
5568 {
5569   #ifdef SYDEBUG
5570   // generate XML translation
5571   if (fXMLtranslate && fIncomingXMLInstance)
5572     smlReplaceCmd(fIncomingXMLInstance,aContentP);
5573   #endif
5574   // create SyncOp command object
5575   TSyncOpCommand *commandP = new TSyncOpCommand(
5576     this,
5577     fLocalSyncDatastoreP,
5578     fIncomingMsgID,
5579     sop_replace,
5580     scmd_replace,
5581     aContentP
5582   );
5583   // process it
5584   return process(commandP);
5585 } // TSyncSession::ReplaceCmd
5586
5587
5588 #ifdef COPY_RECEIVE
5589 Ret_t TSyncSession::CopyCmd(SmlReplacePtr_t aContentP)
5590 {
5591   #ifdef SYDEBUG
5592   #ifdef COPY_SEND
5593   // generate XML translation
5594   if (fXMLtranslate && fIncomingXMLInstance)
5595     smlCopyCmd(fIncomingXMLInstance,aContentP);
5596   #else
5597   #error "We will have incomplete XML translation when only COPY_RECEIVE is defined"
5598   #endif
5599   #endif
5600   // create SyncOp command object
5601   TSyncOpCommand *commandP = new TSyncOpCommand(
5602     this,
5603     fLocalSyncDatastoreP,
5604     fIncomingMsgID,
5605     fTreatCopyAsAdd ? sop_add : sop_copy,
5606     scmd_copy,
5607     aContentP
5608   );
5609   // process it
5610   return process(commandP);
5611 } // TSyncSession::CopyCmd
5612 #endif
5613
5614
5615 Ret_t TSyncSession::MoveCmd(SmlReplacePtr_t aContentP)
5616 {
5617   #ifdef SYDEBUG
5618   // generate XML translation
5619   if (fXMLtranslate && fIncomingXMLInstance)
5620     smlMoveCmd(fIncomingXMLInstance,aContentP);
5621   #endif
5622   // create SyncOp command object
5623   TSyncOpCommand *commandP = new TSyncOpCommand(
5624     this,
5625     fLocalSyncDatastoreP,
5626     fIncomingMsgID,
5627     sop_move,
5628     scmd_move,
5629     aContentP
5630   );
5631   // process it
5632   return process(commandP);
5633 } // TSyncSession::MoveCmd
5634
5635 // - error handling
5636 Ret_t TSyncSession::HandleError(void)
5637 {
5638   // %%% tbd
5639   DEBUGPRINTFX(DBG_ERROR,("HandleError reached"));
5640   return SML_ERR_OK; // %%%
5641 } // TSyncSession::HandleError
5642
5643
5644
5645 Ret_t TSyncSession::DummyHandler(const char* msg)
5646 {
5647   //DEBUGPRINTFX(DBG_ERROR,("DummyHandler: msg=%s",msg));
5648   return SML_ERR_OK;
5649 } // TSyncSession::DummyHandler
5650
5651
5652
5653 #ifdef ENGINEINTERFACE_SUPPORT
5654
5655 // Support for EngineModule common interface
5656 // =========================================
5657
5658 // open subkey by name (not by path!)
5659 // - this is the actual implementation
5660 TSyError TSessionKey::OpenSubKeyByName(
5661   TSettingsKeyImpl *&aSettingsKeyP,
5662   cAppCharP aName, stringSize aNameSize,
5663   uInt16 aMode
5664 ) {
5665   #ifdef SCRIPT_SUPPORT
5666   if (strucmp(aName,"sessionvars",aNameSize)==0) {
5667     // note: if no session scripts are used, context does not exist and is NULL.
5668     //       TScriptVarKey does not crash with a NULL, so we can give ok here (but no session vars
5669     //       will be accessible).
5670     aSettingsKeyP = new TScriptVarKey(fEngineInterfaceP,fSessionP->getSessionScriptContext());
5671   }
5672   else
5673   #endif
5674     return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
5675   // opened a key
5676   return LOCERR_OK;
5677 } // TSessionKey::OpenSubKeyByName
5678
5679 #endif // ENGINEINTERFACE_SUPPORT
5680
5681
5682 } // namespace sysync
5683
5684
5685 // factory methods of Session Config
5686 // =================================
5687
5688 // only one of XML2GO or SDK/Plugin can be on top of customagent
5689 #ifdef XML2GO_SUPPORT
5690   #include "xml2goapids.h"
5691 #elif defined(SDK_SUPPORT)
5692   #include "pluginapids.h"
5693 #endif
5694 // ODBC can be in-between if selected
5695 #ifdef SQL_SUPPORT
5696   #include "odbcapids.h"
5697 #endif
5698
5699
5700 namespace sysync {
5701
5702 #ifndef HARDCODED_CONFIG
5703
5704 // create new datastore config by name
5705 // returns NULL if none found
5706 TLocalDSConfig *TSessionConfig::newDatastoreConfig(const char *aName, const char *aType, TConfigElement *aParentP)
5707 {
5708   #ifdef XML2GO_SUPPORT
5709   if (aType && strucmp(aType,"xml2go")==0) {
5710     // xml2go enhanced datastore
5711     return new TXml2goDSConfig(aName,aParentP);
5712   }
5713   else
5714   #elif defined(SDK_SUPPORT)
5715   if (aType && strucmp(aType,"plugin")==0) {
5716     // APIDB enhanced datastore (on top of ODBC if SQL_SUPPORT is on)
5717     return new TPluginDSConfig(aName,aParentP);
5718   }
5719   else
5720   #endif
5721   #ifdef SQL_SUPPORT
5722   if (aType==0 || strucmp(aType,"odbc")==0 || strucmp(aType,"sql")==0)  {
5723     // ODBC enabled datastore
5724     return new TOdbcDSConfig(aName,aParentP);
5725   }
5726   else
5727   #endif
5728     return NULL; // unknown datastore
5729 } // TSessionConfig::newDatastoreConfig
5730
5731 #endif // HARDCODED_CONFIG
5732
5733 } // namespace sysync
5734
5735 #endif // not SYNCSESSION_PART1_EXCLUDE
5736
5737 // eof