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.
7 * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
9 * 2001-05-07 : luz : Created
14 #include "prefix_file.h"
17 #include "syncsession.h"
18 #include "syncagent.h"
19 #ifdef SUPERDATASTORES
20 #include "superdatastore.h"
23 #include "scriptcontext.h"
25 #ifdef MULTI_THREAD_SUPPORT
26 #include "platform_thread.h"
30 #ifndef SYNCSESSION_PART1_EXCLUDE
37 // SyncML version info
38 const char * const SyncMLVerProtoNames[numSyncMLVersions] = {
44 const SmlVersion_t SmlVersionCodes[numSyncMLVersions] = {
50 const char * const SyncMLVerDTDNames[numSyncMLVersions] = {
56 const char * const SyncMLDevInfNames[numSyncMLVersions] = {
62 #ifndef HARDCODED_CONFIG
63 // version for use in config files
64 const char * const SyncMLVersionNames[numSyncMLVersions] = {
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
82 const char * const SyncModeNames[numSyncModes] = {
91 // package state names
92 const char * const PackageStateNames[numPackageStates] = {
102 const char * const SyncOpNames[numSyncOperations] = {
113 "[none]" // should be last
118 // sync mode descriptions
119 const char * const SyncModeDescriptions[numSyncModes] = {
126 #ifdef SCRIPT_SUPPORT
128 // builtin functions for status-handling scripts
131 static void func_Status(TItemField *&aTermP, TScriptContext *aFuncContextP)
133 TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
134 aTermP->setAsInteger(
140 // void SETSTATUS(integer statuscode)
141 static void func_SetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
143 TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
144 errctxP->newstatuscode=
145 aFuncContextP->getLocalVar(0)->getAsInteger();
149 // void SETRESEND(boolean doresend)
150 static void func_SetResend(TItemField *&aTermP, TScriptContext *aFuncContextP)
152 TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
154 aFuncContextP->getLocalVar(0)->getAsBoolean();
158 // void ABORTDATASTORE(integer statuscode)
159 static void func_AbortDatastore(TItemField *&aTermP, TScriptContext *aFuncContextP)
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
165 } // func_AbortDatastore
169 static void func_StopAdding(TItemField *&aTermP, TScriptContext *aFuncContextP)
171 TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
172 if (errctxP->datastoreP) {
173 errctxP->datastoreP->engStopAddingToRemote();
179 // returns sync-operation as text
180 static void func_SyncOp(TItemField *&aTermP, TScriptContext *aFuncContextP)
182 TErrorFuncContext *errctxP = static_cast<TErrorFuncContext *>(aFuncContextP->getCallerContext());
184 SyncOpNames[errctxP->syncop]
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) };
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 },
202 const TFuncTable ErrorFuncTable = {
203 sizeof(ErrorFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
204 ErrorFuncDefs, // table pointer
205 NULL // no chain func
209 // void SETSTATUS(integer statuscode)
210 static void func_GetPutResSetStatus(TItemField *&aTermP, TScriptContext *aFuncContextP)
212 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
214 aFuncContextP->getLocalVar(0)->getAsInteger();
215 } // func_GetPutResSetStatus
219 static void func_IsPut(TItemField *&aTermP, TScriptContext *aFuncContextP)
221 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
222 aTermP->setAsBoolean(gprctxP->isPut);
227 static void func_ItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
229 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
230 aTermP->setAsString(gprctxP->itemURI);
234 // void SETITEMURI(string data)
235 static void func_SetItemURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
237 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
238 aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemURI);
243 static void func_ItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
245 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
246 aTermP->setAsString(gprctxP->itemData);
250 // void SETITEMDATA(string data)
251 static void func_SetItemData(TItemField *&aTermP, TScriptContext *aFuncContextP)
253 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
254 aFuncContextP->getLocalVar(0)->getAsString(gprctxP->itemData);
255 } // func_SetItemData
259 static void func_MetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
261 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
262 aTermP->setAsString(gprctxP->metaType);
266 // void SETMETATYPE(string data)
267 static void func_SetMetaType(TItemField *&aTermP, TScriptContext *aFuncContextP)
269 TGetPutResultFuncContext *gprctxP = static_cast<TGetPutResultFuncContext *>(aFuncContextP->getCallerContext());
270 aFuncContextP->getLocalVar(0)->getAsString(gprctxP->metaType);
271 } // func_SetMetaType
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)
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);
286 if (aFuncContextP->getLocalVar(0)->getAsBoolean()) putcommandP->allowFailure(); // allow failure (4xx or 5xx status)
287 aFuncContextP->getSession()->issueRootPtr(putcommandP,aFuncContextP->getLocalVar(1)->getAsBoolean());
292 // void ISSUEGET(boolean allowFailure)
293 // use ITEMURI and METATYPE to issue a GET command
294 static void func_IssueGet(TItemField *&aTermP, TScriptContext *aFuncContextP)
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()));
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
308 // void ISSUEALERT(boolean allowFailure, integer alertcode)
309 // use ITEMDATA to add an Alert item
310 static void func_IssueAlert(TItemField *&aTermP, TScriptContext *aFuncContextP)
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()));
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
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 }
341 const TFuncTable GetPutResultFuncTable = {
342 sizeof(GetPutResultFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
343 GetPutResultFuncDefs, // table pointer
344 NULL // no chain func
352 #ifndef NO_REMOTE_RULES
354 // Remote Rule Config
355 // ==================
357 #define DONT_REJECT 0xFFFF
359 // config constructor
360 TRemoteRuleConfig::TRemoteRuleConfig(const char *aElementName, TConfigElement *aParentElementP) :
361 TConfigElement(aElementName,aParentElementP)
364 } // TRemoteRuleConfig::TRemoteRuleConfig
368 TRemoteRuleConfig::~TRemoteRuleConfig()
371 } // TRemoteRuleConfig::~TRemoteRuleConfig
375 void TRemoteRuleConfig::clear(void)
379 fManufacturer.erase();
382 fFirmwareVers.erase();
383 fSoftwareVers.erase();
384 fHardwareVers.erase();
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
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;
416 fRemoteDescName.erase();
418 fSubRulesList.clear(); // no included subrules
419 fSubRule = false; // normal rule by default
420 // - rules are final by default
424 } // TRemoteRuleConfig::clear
427 #ifndef HARDCODED_CONFIG
429 // remote rule config element parsing
430 bool TRemoteRuleConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
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)
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);
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=""/>
507 const char* nam = getAttr(aAttributes,"rule");
509 return fail("<include> must specify \"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);
520 return fail("rule '%s' for <include> not found (must be defined before included)",nam);
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
530 else if (strucmp(aElementName,"descriptivename")==0)
531 expectString(fRemoteDescName);
534 else if (strucmp(aElementName,"finalrule")==0)
535 expectBool(fFinalRule);
538 return inherited::localStartElement(aElementName,aAttributes,aLine);
541 } // TRemoteRuleConfig::localStartElement
543 #endif // HARDCODED_CONFIG
545 #endif // NO_REMOTE_RULES
548 #endif // not SYNCSESSION_PART1_EXCLUDE
549 #ifndef SYNCSESSION_PART2_EXCLUDE
556 // config constructor
557 TSessionConfig::TSessionConfig(const char *aElementName, TConfigElement *aParentElementP) :
558 inherited(aElementName,aParentElementP)
561 } // TSessionConfig::TSessionConfig
565 TSessionConfig::~TSessionConfig()
568 } // TSessionConfig::~TSessionConfig
572 void TSessionConfig::clear(void)
575 #ifndef NO_REMOTE_RULES
577 TRemoteRulesList::iterator pos;
578 for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
580 fRemoteRulesList.clear();
583 TLocalDSList::iterator pos2;
584 for(pos2=fDatastores.begin();pos2!=fDatastores.end();pos2++)
588 fSimpleAuthUser.erase();
589 fSimpleAuthPassword.erase();
591 fSessionTimeout=60; // one minute, will be overridden by derived classes
592 // - set default maximum SyncML version enabled
593 fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION;
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;
610 // - Synthesis clients always behaved like that (sending 23:59:59), so we'll keep it as a default
611 fVCal10EnddatesSameDay = true;
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;
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
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
638 fDeletingGoneOK=true; // makes more sense as it avoids unnecessary session aborts
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();
662 fLogFileName.erase();
664 fLogFileFormat.assign(DEFAULT_LOG_FORMAT_SERVER);
665 fLogFileLabels.assign(DEFAULT_LOG_LABELS_SERVER);
668 fLogFileFormat.assign(DEFAULT_LOG_FORMAT_CLIENT);
669 fLogFileLabels.assign(DEFAULT_LOG_LABELS_CLIENT);
672 fDebugChunkMaxSize=0; // disabled
674 fRelyOnEarlyMaps=true; // we rely on early maps sent by clients for adds from the previous session
677 } // TSessionConfig::clear
680 // get local DS config pointer by database name or dbTypeID
681 TLocalDSConfig *TSessionConfig::getLocalDS(const char *aName, uInt32 aDBTypeID)
683 TLocalDSList::iterator pos;
684 for(pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
686 if ((*pos)->fLocalDBTypeID==aDBTypeID) return *pos; // found by DBTypeID
689 if (strucmp((*pos)->getName(),aName)==0) return *pos; // found by name
692 return NULL; // not found
693 } // TSessionConfig::getLocalDS
696 #ifndef HARDCODED_CONFIG
698 // server config element parsing
699 bool TSessionConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
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";
709 TRemoteRuleConfig *ruleP = new TRemoteRuleConfig(nam,this);
710 ruleP->fSubRule = isSubRule;
711 fRemoteRulesList.push_back(ruleP);
712 expectChildParsing(*ruleP);
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);
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);
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");
798 ReportError(true,"datastore missing 'name' attribute");
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);
807 ReportError(true,"datastore has unknown 'type' attribute");
810 fDatastores.push_back(datastorecfgP);
811 // - let element handle parsing
812 expectChildParsing(*datastorecfgP);
816 #ifdef SUPERDATASTORES
818 else if (strucmp(aElementName,"superdatastore")==0) {
819 // definition of a new datastore
820 const char* nam = getAttr(aAttributes,"name");
822 ReportError(true,"datastore missing 'name' attribute");
825 // create new named datastore
826 TLocalDSConfig *datastorecfgP = new TSuperDSConfig(nam,this);
828 fDatastores.push_back(datastorecfgP);
829 // - let element handle parsing
830 expectChildParsing(*datastorecfgP);
836 return inherited::localStartElement(aElementName,aAttributes,aLine);
839 } // TSessionConfig::localStartElement
845 void TSessionConfig::localResolve(bool aLastPass)
849 #ifndef NO_REMOTE_RULES
851 TRemoteRulesList::iterator pos;
852 for(pos=fRemoteRulesList.begin();pos!=fRemoteRulesList.end();pos++)
853 (*pos)->Resolve(aLastPass);
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;
861 // resolve all scripts in same context
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;
875 if (sccP) delete sccP;
881 inherited::localResolve(aLastPass);
882 } // TSessionConfig::localResolve
891 TSyncSession::TSyncSession(
892 TSyncAppBase *aSyncAppBaseP, // the owning application base (dispatcher/client base)
893 const char *aSessionID // a session ID
896 fSessionDebugLogs(0),
900 fSessionLogger(&fSessionZones),
902 fSyncAppBaseP(aSyncAppBaseP) // link to owning base (dispatcher/clienbase)
904 // Inherit globally defined time zones
905 // Note: this must be done as very first step as all time output routines will use the
907 fSessionZones = *(fSyncAppBaseP->getAppZones());
908 // now mark used to avoid early timeout (will be marked again at InternalResetSession())
910 fLastRequestStarted=getSessionLastUsed(); // set this in case we terminate before StartMessage()
911 fSessionStarted=fLastRequestStarted; // this is also the start of the session
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");
920 TP_START(fTPInfo,TP_general);
921 DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: Profiling initialized"));
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
930 #ifdef SCRIPT_SUPPORT
931 fSessionScriptContextP = NULL;
933 fInterruptedCommandP = NULL;
934 fIncompleteDataCommandP = NULL;
935 #ifdef SYNCSTATUS_AT_SYNC_CLOSE
936 fSyncCloseStatusCommandP=NULL;
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
949 fLenientMode = false;
951 //initialize the conditonal variables to keep valgrind happy
953 fRemoteRequestedAuth = auth_none;
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());
965 PDEBUGPRINTFX(DBG_HOT,("--------- START of embedded log for session ID '%s' ---------", fLocalSessionID.c_str()));
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() ?
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
980 if (getRootConfig()->fDebugConfig.fTimedSessionLogNames) {
981 fSessionLogger.appendToDebugPath("_");
983 TimestampToISO8601Str(t, getSystemNowAs(TCTX_UTC), TCTX_UTC, false, false);
984 fSessionLogger.appendToDebugPath(t.c_str());
986 fSessionLogger.appendToDebugPath("_s");
987 fSessionLogger.appendToDebugPath(fLocalSessionID.c_str());
990 fSessionLogger.DebugDefineMainThread();
991 // initialize session level dump flags
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
1000 // reset session at creation
1001 DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: calling InternalResetSession"));
1002 InternalResetSessionEx(false);
1003 DEBUGPRINTFX(DBG_EXOTIC,("TSyncSession::TSyncSession: InternalResetSession called"));
1005 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionstart,NULL,0,0,0);
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"
1012 #define CAN_BE_TEXT "Client"
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",
1020 SYSYNC_VERSION_MAJOR,
1021 SYSYNC_VERSION_MINOR,
1025 // show Product ID string
1026 PDEBUGPRINTFX(DBG_HOT,(
1027 "---- Hardcoded Product name: " CUST_SYNC_MODEL
1029 PDEBUGPRINTFX(DBG_HOT,(
1030 "---- Configured Model/Manufacturer: %s / %s",
1031 getSyncAppBase()->getModel().c_str(), getSyncAppBase()->getManufacturer().c_str()
1033 // show platform we're on
1035 getSyncAppBase()->getMyDeviceID(devid);
1036 PDEBUGPRINTFX(DBG_HOT,(
1037 "---- Running on " SYSYNC_PLATFORM_NAME ", URI/deviceID='%s'",
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",
1048 // show platform details
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'",
1059 PDEBUGPRINTFX(DBG_HOT,(
1060 "---- Configured Hardware Version = '%s', Firmware Version = '%s'",
1061 getSyncAppBase()->getHardwareVersion().c_str(),
1062 getSyncAppBase()->getFirmwareVersion().c_str()
1064 // show time zone infos
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)
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)) {
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
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
1109 } // TSyncSession::TSyncSession
1113 TSyncSession::~TSyncSession()
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)
1120 DEBUGPRINTFX(DBG_OBJINST,("-------- TSyncSession almost destroyed (except implicit member destruction)"));
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()));
1126 fSessionLogger.DebugThreadOutputDone();
1128 } // TSyncSession::~TSyncSession
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)
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;
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) {
1158 fLocalDataStores.clear(); // clear list
1159 // remove all local itemtypes
1160 TSyncItemTypePContainer::iterator pos2;
1161 for (pos2=fLocalItemTypes.begin(); pos2!=fLocalItemTypes.end(); ++pos2) {
1164 fLocalItemTypes.clear(); // clear list
1166 // save half-begun XML translations
1167 XMLTranslationOutgoingEnd();
1168 XMLTranslationIncomingEnd();
1170 // stop and show profiling info
1172 #ifdef TIME_PROFILING
1173 if (getDbgMask() & DBG_PROFILE) {
1176 PDEBUGPRINTFX(DBG_PROFILE,("Session CPU usage statistics: (system/user/total)"));
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",
1190 sy = TP_GETTOTALSYSTEMMS(fTPInfo);
1191 us = TP_GETTOTALUSERMS(fTPInfo);
1192 PDEBUGPRINTFX(DBG_PROFILE,(
1193 "- TOTAL : %10ld /%10ld /%10ld ms",
1199 uInt32 rt = TP_GETREALTIME(fTPInfo);
1200 PDEBUGPRINTFX(DBG_PROFILE,(
1201 "- Real Time : %10ld ms",
1205 PDEBUGPRINTFX(DBG_PROFILE,(
1206 "- CPU load : %10ld promille",
1207 rt ? (sy+us)*1000/rt : 0
1211 MP_SHOWCURRENT(DBG_PROFILE,"TSyncSession deleting");
1212 // show ending (if not normal, then ending was already shown in AbortSession())
1214 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sessionend,NULL, allsuccess ? 0 : LOCERR_INCOMPLETE,0,0);
1216 // is NOW terminated
1218 PDEBUGPRINTFX(DBG_EXOTIC,("Session is now terminated (but not yet deleted)"));
1219 } // if not already terminated
1220 } // TSyncSession::TerminateSession
1224 void TSyncSession::ResetSession(void)
1226 // terminate sync of datastores
1227 TerminateDatastores();
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
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()
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();
1246 } // TSyncSession::announceDestruction
1249 // - terminate all datastores
1250 void TSyncSession::TerminateDatastores(localstatus aAbortStatusCode)
1252 // terminate sync with all datastores
1253 TLocalDataStorePContainer::iterator pos;
1254 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1256 (*pos)->engTerminateDatastore(aAbortStatusCode);
1258 } // TSyncSession::TerminateDatastores
1261 // - resets session and removes all datastores (local and remote)
1262 void TSyncSession::ResetAndRemoveDatastores(void)
1264 // Must reset session before
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);
1272 (*pos) = NULL; // to avoid double deletes
1276 fLocalDataStores.clear(); // remove pointers
1277 } // TSyncSession::ResetAndRemoveDatastores
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
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)
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
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();
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;
1327 fSessionLogger.setEnabled(getRootConfig()->fDebugConfig.fSessionDebugLogs); // get default value
1329 #ifndef MINIMAL_CODE
1330 fLogEnabled = getSessionConfig()->fLogEnabled;
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
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();
1356 // - remove all remote datastores
1357 TRemoteDataStorePContainer::iterator pos1;
1358 for (pos1=fRemoteDataStores.begin(); pos1!=fRemoteDataStores.end(); ++pos1) {
1361 fRemoteDataStores.clear(); // clear list
1362 // - remove all remote itemtypes
1363 TSyncItemTypePContainer::iterator pos2;
1364 for (pos2=fRemoteItemTypes.begin(); pos2!=fRemoteItemTypes.end(); ++pos2) {
1367 fRemoteItemTypes.clear(); // clear list
1370 // empty command queues
1371 TSmlCommandPContainer::iterator pos;
1372 // - commands waiting for status
1373 for (pos=fStatusWaitCommands.begin(); pos!=fStatusWaitCommands.end(); ++pos) {
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(),
1383 (long)cmdP->getMsgID(),
1384 (long)cmdP->getCmdID()
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)
1393 DEBUGPRINTF(("- prevented deleting because command is interrupted"));
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)",
1404 (long)(*pos)->getMsgID(),
1405 (long)(*pos)->getCmdID()
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)",
1416 (long)(*pos)->getMsgID(),
1417 (long)(*pos)->getCmdID()
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)",
1428 (long)(*pos)->getMsgID(),
1429 (long)(*pos)->getCmdID()
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()
1443 delete fInterruptedCommandP;
1444 fInterruptedCommandP=NULL;
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)",
1452 (long)(*pos)->getMsgID(),
1453 (long)(*pos)->getCmdID()
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;
1464 // remove incomplete data command
1465 if (fIncompleteDataCommandP) delete fIncompleteDataCommandP;
1466 fIncompleteDataCommandP=NULL;
1468 SYSYNC_CATCH (exception &e)
1470 DEBUGPRINTFX(DBG_ERROR,(
1471 "WARNING: Exception during InternalResetSession(): %s",
1478 DEBUGPRINTFX(DBG_ERROR,(
1479 "WARNING: Unknown Exception during InternalResetSession()"
1483 // remember time of creation or last reset
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;
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
1545 } // TSyncSession::InternalResetSessionEx
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)
1556 return fSyncAppBaseP->getRootConfig();
1557 } // TSyncSession::getRootConfig
1560 // forget commands waiting to be sent when header is generated
1561 void TSyncSession::forgetHeaderWaitCommands(void)
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) {
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",
1572 (long)cmdP->getMsgID(),
1573 (long)cmdP->getCmdID()
1579 fHeaderWaitCommands.clear(); // clear list
1580 } // TSyncSession::forgetHeaderWaitCommands
1583 // abort processing commands in this message
1584 void TSyncSession::AbortCommandProcessing(TSyError aStatusCode)
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
1596 } // TSyncSession::AbortCommandProcessing
1599 // suspend session (that is: flag abortion)
1600 void TSyncSession::SuspendSession(TSyError aReason)
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",
1615 "Session will be suspended due to local error code %hd\n",
1621 // We can't suspend, we need to abort
1622 AbortSession(500,true,aReason);
1624 } // TSyncSession::SuspendSession
1627 void TSyncSession::MarkSuspendAlertSent(bool aSent)
1629 fSuspendAlertSent = aSent;
1633 // abort session (that is: flag abortion)
1634 void TSyncSession::AbortSession(TSyError aStatusCode, bool aLocalProblem, TSyError aReason)
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);
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",
1652 fLocalAbortReason ? "LOCAL" : "REMOTE"
1654 PDEBUGPRINTFX(DBG_ERROR,(
1655 "WARNING: Aborting Session with Reason Status %hd (%s problem) ***",
1657 fLocalAbortReason ? "LOCAL" : "REMOTE"
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);
1670 "Session aborted because of %s SyncML error code %hd\n",
1671 fLocalAbortReason ? "LOCAL" : "REMOTE",
1678 getAbortReasonStatus(),
1681 PDEBUGENDBLOCK("SessionAbort");
1683 } // TSyncSession::AbortSession
1686 // returns true if session was a complete success
1687 bool TSyncSession::isAllSuccess(void)
1689 return fFailedDatastores==0 && fErrorItemDatastores==0;
1690 } // TSyncSession::isAllSuccess
1693 // let session know that datastore has failed
1694 void TSyncSession::DatastoreFailed(TSyError aStatusCode, bool aLocalProblem)
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);
1702 } // TSyncSession::DatastoreFailed
1705 // set SyncML toolkit workspace ID
1706 void TSyncSession::setSmlWorkspaceID(InstanceID_t aSmlWorkspaceID)
1708 fSmlWorkspaceID=aSmlWorkspaceID;
1709 } // TSyncSession::setSmlWorkspaceID
1712 // show some information about the config
1713 void TSyncSession::DebugShowCfgInfo(void)
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(),
1724 #ifndef HARDCODED_CONFIG
1725 PDEBUGPRINTFX(DBG_HOT,(
1726 "==== Config ID string='%s'",
1727 getRootConfig()->fConfigIDString.c_str()
1731 } // TSyncSession::DebugShowCfgInfo
1734 #ifndef MINIMAL_CODE
1738 // write to sync log file
1739 void TSyncSession::WriteLogLine(const char *aLogline)
1741 if (getSessionConfig()->fLogFileName.empty()) return; // do not write without a path
1743 FILE * logfile=fopen(getSessionConfig()->fLogFileName.c_str(),"a");
1745 PDEBUGPRINTFX(DBG_ERROR,("**** Cannot write to logfile '%s' (errno=%ld)",getSessionConfig()->fLogFileName.c_str(),(long)errno));
1746 return; // cannot write
1748 // check if we need to write labels first
1749 if (!getSessionConfig()->fLogFileLabels.empty()) {
1751 if (ftell(logfile)==0) {
1752 // we are at the beginning, print labels first
1753 fputs(getSessionConfig()->fLogFileLabels.c_str(),logfile);
1756 // now write log line
1757 fputs(aLogline,logfile);
1760 } // TSyncSession::WriteLogLine
1766 // queue a SyncBody context command for issuing after incoming message has ended
1767 void TSyncSession::queueForIssueRoot(
1768 TSmlCommand * &aSyncCommandP // the command
1771 fEndOfMessageCommands.push_back(aSyncCommandP);
1773 } // TSyncSession::queueForIssueRoot
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
1782 if (fOutgoingState>=aPackageState) {
1783 issueRootPtr(aSyncCommandP);
1786 PDEBUGPRINTFX(DBG_SESSION,("%s queued for next package",aSyncCommandP->getName()));
1787 fNextPackageCommands.push_back(aSyncCommandP);
1789 } // TSyncSession::issueNotBeforePackage
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
1800 return issuePtr(aSyncCommandP,fNextMessageCommands,fInterruptedCommandP,aNoResp,aIsOKSyncHdrStatus);
1801 } // TSyncSession::issueRootPtr
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
1814 bool issued=true; // NULL issue is successful issue
1816 if (aSyncCommandP) {
1817 PDEBUGBLOCKFMT(("issue","issuing command",
1819 aSyncCommandP->getName()
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()
1828 delete aSyncCommandP;
1829 issued=true; // counts as issued
1832 // check if this commmand now triggers need to answer
1833 if (!aIsOKSyncHdrStatus) {
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()
1845 issued=true; // act as if issued
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
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"
1860 // check if enough room to send data
1861 sInt32 freeaftersend=aSyncCommandP->evalIssue(
1862 peekNextOutgoingCmdID(), // this will be the ID
1866 // - if room for new commands is smaller than expected message size
1867 sInt32 maxfree=getSmlWorkspaceFreeBytes();
1868 sInt32 sizetoend=maxfree-freeaftersend;
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(),
1874 (long)sizetoend, // size of command (+tags needed to end msg)
1875 (long)(maxfree-getNotUsableBufferBytes()),
1877 (long)freeaftersend,
1878 (long)getNotUsableBufferBytes()
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) {
1891 PDEBUGPRINTFX(DBG_ERROR,("Attention: Debug Chunking enabled, freeaftersend adjusted to %ld",(long)freeaftersend));
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)
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"));
1916 // reserve about 200 bytes for Meta Size
1917 splitCmdP = aSyncCommandP->splitCommand(getNotUsableBufferBytes()-freeaftersend+200);
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
1928 // - second choice: try to shrink (mainly devInf)
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)
1938 // - third choice: send it later as first command
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
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()
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).
1961 // check if command was made sendable by splitting off a part
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)
1969 } // command too large to be sent now
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()
1982 // - could not be issued, was queued
1986 // issue the command
1987 if (splitCmdP) fOutgoingMessageFull=true; // if we are sending a split command, message IS full after that!
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()
2000 // - queue for status
2001 aSyncCommandP->setWaitingForStatus(true); // increment waiting for status count of this command
2002 fStatusWaitCommands.push_back(aSyncCommandP);
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()
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)
2020 // test if finished issuing
2021 if (!aSyncCommandP->finished()) {
2022 // issuing was interrupted, continue at start of next message
2023 aInterruptedCommandP=aSyncCommandP; // remember
2026 PDEBUGPRINTFX(DBG_SESSION,("%s: issue not finished -> queued interrupted command",
2027 aSyncCommandP->getName()
2030 // - delete if not queued
2032 PDEBUGPRINTFX(DBG_SESSION,("%s: issue finished and not waiting for status -> deleting command",
2033 aSyncCommandP->getName()
2035 delete aSyncCommandP;
2038 PDEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
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
2048 PDEBUGENDBLOCK("issue");
2053 PDEBUGENDBLOCK("issue");
2054 } // if something to issue at all
2056 DEBUGPRINTFX(DBG_SESSION,("issuePtr called with NULL command"));
2058 // return true if completely issued, false if interrupted or not issued at all
2060 } // TSyncSession::issuePtr
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)
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
2071 return aDataBytes<=uInt32(fMaxRoomForData);
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;
2078 } // TSyncSession::dataSizeTransferable
2082 #ifndef USE_SML_EVALUATION
2083 #error "This implementation requires USE_SML_EVALUATION"
2086 // get how many bytes may not be used in the outgoing message buffer
2087 // because of maxMsgSize restrictions
2088 sInt32 TSyncSession::getNotUsableBufferBytes(void)
2090 if (fMaxOutgoingMsgSize) {
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;
2101 // limited only by workspace itself: no bytes need to be left free
2104 } // TSyncSession::getNotUsableBufferBytes
2107 // get max size outgoing message may have (either defined by remote's maxmsgsize or local buffer space)
2108 sInt32 TSyncSession::getMaxOutgoingSize(void)
2110 return fMaxOutgoingMsgSize>0 ? fMaxOutgoingMsgSize : getSmlWorkspaceFreeBytes();
2111 } // TSyncSession::getMaxOutgoingSize
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)
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
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
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
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)!
2137 } // TSyncSession::markPendingForResume
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
2148 // - all those that are in an interrupted command
2149 if (aInterruptedCommandP) {
2150 aInterruptedCommandP->markPendingForResume(aForDatastoreP,true); // these are unsent
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
2157 } // TSyncSession::markPendingForResume
2160 // continue interrupted or prevented issue of root level commands
2161 void TSyncSession::ContinuePackageRoot(void)
2163 // check if we have anything to send, and if so, reset the count
2164 if (fNextMessageCommands.size()>0 || fInterruptedCommandP) {
2166 if (fNextMessageRequests) {
2167 PDEBUGPRINTFX(DBG_PROTO,("Fulfilling %ld Next-Message-Request-Alerts 222 by sending commands now",(long)fNextMessageRequests));
2170 fNextMessageRequests=0; // sent something, request fulfilled
2172 ContinuePackage(fNextMessageCommands,fInterruptedCommandP);
2173 } // TSyncSession::ContinuePackageRoot
2176 // continue interrupted or prevented issue in next package
2177 void TSyncSession::ContinuePackage(
2178 TSmlCommandPContainer &aNextMessageCommands,
2179 TSmlCommand * &aInterruptedCommandP
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",
2191 (long)cmdP->getMsgID(),
2192 (long)cmdP->getCmdID()
2194 // aInterruptedCommandP is still set, prevents sending of queued commands
2197 PDEBUGPRINTFX(DBG_SESSION,("Sending command that was interrupted at end of last message"));
2198 // there is an interrupted command, continue it
2200 // if an interrupted command must be continued, this can't be an OK for SyncHdr
2202 bool newIssue=false;
2203 // now continue issuing
2204 bool queueForStatus=cmdP->continueIssue(newIssue);
2205 // check if complete re-issuing is needed
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
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)",
2222 (long)cmdP->getMsgID(),
2223 (long)cmdP->getCmdID()
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);
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()));
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
2243 PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue not finished -> queued interrupted command again",
2248 // no interrupted command any more
2249 aInterruptedCommandP=NULL;
2251 // delete if not queued
2253 PDEBUGPRINTFX(DBG_SESSION,("%s: continueIssue finished and not waiting for status -> deleting command",
2258 } // if status was received for interrupted command
2259 } // if interrupted command
2260 // send commands from queue (until interrupted again)
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()));
2266 TSmlCommandPContainer::iterator pos;
2267 while (!isAborted() && !isSuspending() && !aInterruptedCommandP && !outgoingMessageFull()) {
2269 pos=aNextMessageCommands.begin();
2270 if (pos==aNextMessageCommands.end()) break; // done
2271 // take command out of the list
2273 aNextMessageCommands.erase(pos);
2274 // issue it (without luck, might land in the queue again --> %%% endless retry??)
2275 if (!issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) break;
2277 } // TSyncSession::ContinuePackage
2281 // create, send and delete SyncHeader "command"
2282 void TSyncSession::issueHeader(bool aNoResp)
2285 // Start output translation before issuing outgoing header
2286 XMLTranslationOutgoingStart();
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"));
2295 #ifdef EXPIRES_AFTER_DATE
2296 // set 1/4 of the date here
2297 fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
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));
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);
2313 PDEBUGPRINTFX(DBG_PROTO,("SyncHdr: issued in MsgID=%ld, now deleting",(long)syncheaderP->getMsgID()));
2314 delete syncheaderP; // not used any more
2316 DEBUGPRINTFX(DBG_PROTO,("Outgoing Message size is now %ld bytes",(long)getOutgoingMessageSize()));
2317 // - init number of commands in message
2319 // - now issue all commands that could not yet be sent because outgoing
2320 // message was not started
2322 if (fHeaderWaitCommands.size()>0) {
2323 PDEBUGPRINTFX(DBG_SESSION,("Sending %ld commands that were queued because header was not yet generated",(long)fHeaderWaitCommands.size()));
2326 TSmlCommandPContainer::iterator pos;
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);
2335 DEBUGPRINTFX(DBG_ERROR,("There was a NULL command in the fHeaderWaitCommands queue?!?"));
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"));
2344 PDEBUGENDBLOCK("SyncHdr");
2347 PDEBUGENDBLOCK("SyncHdr");
2348 delete syncheaderP; // not used any more
2349 SYSYNC_RETHROW; // rethrow
2351 } // TSyncSession::IssueHeader
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)
2364 if (aSyncCommandP) {
2366 // first analyze it (before we can open the block, as we need to find cmdID fisrt)
2367 if (!aSyncCommandP->analyze(fIncomingState)) {
2369 PDEBUGPRINTFX(DBG_ERROR,("%s: command failed analyze() -> aborting session",aSyncCommandP->getName()));
2370 AbortSession(400,true); // local problem
2371 delete aSyncCommandP;
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()
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);
2393 // - delete unexecuted
2394 delete aSyncCommandP;
2397 fStrictExecOrdering &&
2398 fDelayedExecutionCommands.size()>0 &&
2399 aSyncCommandP->getCmdType()!=scmd_status &&
2400 aSyncCommandP->getCmdType()!=scmd_alert
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);
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;
2417 PDEBUGPRINTFX(DBG_SESSION,("%s: command NOT finished execution, NOT deleting now",aSyncCommandP->getName()));
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);
2427 // successfully processed
2428 PDEBUGENDBLOCK("processCmd");
2431 PDEBUGENDBLOCK("processCmd");
2434 } // if analyzed successfully
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();
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;
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;
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;
2471 } // TSyncSession::process
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)
2480 PDEBUGBLOCKDESC("processHdr","Processing incoming SyncHdr");
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
2491 bool tryagain=false;
2493 // first analyze header
2494 if (!aSyncHdrP->analyze(fIncomingState)) {
2496 PDEBUGPRINTFX(DBG_ERROR,("%s: failed analyze() -> deleting",aSyncHdrP->getName()));
2500 // command is ok, execute it
2501 fCmdIncomingState=aSyncHdrP->getPackageState();
2502 PDEBUGBLOCKFMT(("SyncHdr","Processing incoming SyncHdr",
2503 "IncomingMsgID=%ld",
2504 (long)fIncomingMsgID
2507 if (aSyncHdrP->execute()) {
2508 // show session info after processing header
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()));
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()));
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()) {
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",
2534 (long)cmdP->getMsgID(),
2535 (long)cmdP->getCmdID()
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
2546 // execution finished, can be deleted
2547 PDEBUGPRINTFX(DBG_SESSION,("%s: command finished execution -> deleting",cmdP->getName()));
2549 // delete from queue and get next
2550 pos=fDelayedExecutionCommands.erase(pos);
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");
2559 PDEBUGENDBLOCK("executeDelayedCmd");
2562 PDEBUGENDBLOCK("executeDelayedCmd");
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);
2577 PDEBUGPRINTFX(DBG_SESSION,("%ld delayed commands could not yet be executed and are left in the queue for next message",(long)fDelayedExecutionCommands.size()));
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));
2587 // done sending next package commands
2588 fNextPackageCommands.clear(); // clear list
2590 // done, don't try again
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
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);
2607 PDEBUGENDBLOCK("SyncHdr");
2612 PDEBUGENDBLOCK("SyncHdr");
2614 SYSYNC_CATCH (TSmlException &e)
2615 // Sml error exception somewhere in command processing
2616 // - make sure command is deleted (as issue owns it now)
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();
2623 SYSYNC_CATCH (exception &e)
2624 // C++ exception somewhere in command processing
2625 // - make sure command is deleted (as issue owns it now)
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;
2633 // other exception during command processing
2634 // - make sure command is deleted (as issue owns it now)
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;
2643 PDEBUGENDBLOCK("processHdr");
2645 } // TSyncSession::processHeader
2648 #pragma segment session2
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)
2662 if (aStatusCommandP) {
2663 PDEBUGBLOCKDESC("processStatus","Processing incoming Status");
2666 if (!aStatusCommandP->analyze(fIncomingState)) {
2668 PDEBUGPRINTFX(DBG_SESSION,("%s: status failed analyze() -> deleting",aStatusCommandP->getName()));
2669 delete aStatusCommandP;
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
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"));
2687 // let descendants know when we process a required status
2688 if ((*pos)->statusEssential()) {
2689 essentialStatusReceived();
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
2701 PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status and allows to be deleted",(*pos)->getName()));
2705 PDEBUGPRINTFX(DBG_SESSION,("Status: command '%s' has handled status, but not finished() -> NOT deleted",(*pos)->getName()));
2707 // - anyway, remove from list
2708 fStatusWaitCommands.erase(pos);
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"));
2715 } // else normal processing
2716 break; // exit for loop (iterator is not ok any more)
2720 // no matching command found
2721 PDEBUGPRINTFX(DBG_ERROR,("No command found for status -> ignoring"));
2723 // now delete status
2724 delete aStatusCommandP;
2725 } // if analyzed successfully
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();
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;
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;
2756 PDEBUGENDBLOCK("processStatus");
2758 } // TSyncSession::handleStatus
2761 // Session level meta
2762 SmlPcdataPtr_t TSyncSession::newHeaderMeta(void)
2764 SmlPcdataPtr_t metaP = NULL;
2766 // create meta for initialisation message only
2767 #ifdef SEND_MAXMSGSIZE_ON_INIT_ONLY
2768 if (fOutgoingState<=psta_init)
2772 SmlMetInfMetInfPtr_t metinfP = smlPCDataToMetInfP(metaP);
2773 // - add max message size
2774 metinfP->maxmsgsize=newPCDataLong(getRootConfig()->fLocalMaxMsgSize);
2776 (getRootConfig()->fLocalMaxObjSize>0) &&
2777 (fSyncMLVersion>=syncml_vers_1_1)
2779 // SyncML 1.1 has object size
2780 metinfP->maxobjsize=newPCDataLong(getRootConfig()->fLocalMaxObjSize);
2784 } // TSyncSession::newHeaderMeta
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)
2792 SmlSyncHdrPtr_t headerP;
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
2799 // reset message size counting
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);
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]);
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
2824 (scramblednow4>SCRAMBLED_EXPIRY_VALUE*4)
2825 #ifdef SYSER_REGISTRATION
2826 && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
2829 getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
2831 // - no hard expiry date, just check if license is still valid
2833 !getSyncAppBase()->fRegOK || // no registered at all
2834 getSyncAppBase()->fDaysLeft==0 // or expired
2836 getSyncAppBase()->fAppExpiryStatus=LOCERR_EXPIRED;
2839 headerP->msgID=newPCDataLong(fOutgoingMsgID);
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(),
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(
2857 getUsernameForRemote() // user name for remote login, NULL if none available
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();
2864 headerP->respURI=NULL;
2865 // add credentials if remote needs them
2866 headerP->cred=newCredentialsForRemote();
2867 // agent-specific meta
2868 headerP->meta=newHeaderMeta();
2871 // make sure header is disposed
2872 smlFreeProtoElement(headerP);
2873 SYSYNC_RETHROW; // re-throw
2877 } // TSyncSession::NewOutgoingSyncHdr
2880 // delay command for execution at beginning of next received message
2881 void TSyncSession::delayExecUntilNextRequest(TSmlCommand *aCommand)
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;
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;
2898 } // TSyncSession::delayExecUntilNextRequest
2901 // remote party requests next message by Alert 222
2902 void TSyncSession::nextMessageRequest(void)
2904 // count the request
2905 fNextMessageRequests++;
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
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"));
2931 "Resume_Sim_Sync","Simulated empty sync to get resume going",
2935 TStatusCommand *fakeStatusCmdP = new TStatusCommand(this);
2936 bool queueforlater=false;
2941 queueforlater // will be set if command must be queued for later re-execution
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;
2950 PDEBUGPRINTFX(DBG_ERROR,("simulated <Sync> can't be processed now, we'll try again later"));
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");
2961 } // TSyncSession::nextMessageRequest
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()
2975 return true; // must continue
2977 // if no status to wait for: session may be deleted now
2978 if (fStatusWaitCommands.empty()) return false;
2979 TSmlCommandPContainer::iterator pos;
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 " : "",
2988 (long)cmdP->getMsgID(),
2989 (long)cmdP->getCmdID()
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
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)
3007 PDEBUGPRINTFX(DBG_HOT,("SESSION CANNOT END - Not yet received REQUIRED status for some of %ld commands",(long)fStatusWaitCommands.size()));
3010 } // TSyncSession::sessionMustContinue
3013 // returns true if session has pending commands
3014 bool TSyncSession::hasPendingCommands(void)
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
3021 } // TSyncSession::hasPendingCommands
3024 // finish outgoing Message, returns true if final message of package
3025 bool TSyncSession::FinishMessage(bool aAllowFinal, bool aForceNonFinal)
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
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 "
3049 // - close message now
3050 sInt32 bytesbeforeissue=getSmlWorkspaceFreeBytes();
3051 fOutgoingStarted=false; // done now
3052 fOutgoingMessageFull=false; // message finished, not full any more
3054 if (fXMLtranslate && fOutgoingXMLInstance)
3055 smlEndMessage(fOutgoingXMLInstance,final);
3056 // Now dump XML translation of outgoing message
3057 XMLTranslationOutgoingEnd();
3059 if ((err=smlEndMessage(fSmlWorkspaceID, final))!=SML_ERR_OK) {
3060 SYSYNC_THROW(TSmlException("smlEndMessage",err));
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
3071 DumpSyncMLMessage(true); // outgoing
3074 // if this message ends with <Final/>, next message will be in new package
3075 fNewOutgoingPackage=final;
3077 } // TSyncSession::FinishMessage
3080 // get name of current encoding
3081 const char *TSyncSession::getEncodingName(void)
3083 return SyncMLEncodingMIMENames[fEncoding];
3084 } // TSyncSession::getEncodingName
3087 // add current encoding spec to given (type-)string
3088 void TSyncSession::addEncoding(string &aString)
3090 aString+=SYNCML_ENCODING_SEPARATOR;
3091 aString+=getEncodingName();
3092 } // TSyncSession::addEncoding
3095 // set encoding for session
3096 void TSyncSession::setEncoding(SmlEncoding_t aEncoding)
3098 Ret_t err=smlSetEncoding(fSmlWorkspaceID,aEncoding);
3099 if (err==SML_ERR_OK) {
3100 fEncoding = aEncoding;
3102 } // TSyncSession::setEncoding
3105 // find remote datastore by (remote party specified) URI
3106 TRemoteDataStore *TSyncSession::findRemoteDataStore(const char *aDatastoreURI)
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) {
3114 if ((*pos)->isDatastore(aDatastoreURI) > bestNumMatched) {
3115 bestMatchP = *pos; // best so far, but check all
3118 return bestMatchP; // return NULL if no match found or best matching
3119 } // TSyncSession::findRemoteDataStore
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)
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,'?');
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);
3141 if (aOptions) aOptions->erase();
3143 // assign identifying part of URL now
3144 if (aIdentifyingURI) {
3145 aIdentifyingURI->assign(dblocuri);
3147 // find datastore now
3148 DEBUGPRINTF(("Determined relative, identifying URI (w/o CGI): %s",dblocuri));
3149 return findLocalDataStore(dblocuri);
3150 } // TSyncSession::findLocalDataStorebyURI
3153 // find local datastore by (relative) URI
3154 TLocalEngineDS *TSyncSession::findLocalDataStore(const char *aDatastoreURI)
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
3165 return NULL; // none found
3166 } // TSyncSession::findLocalDataStore
3169 // - find local datastore by datastore handle (=config pointer)
3170 TLocalEngineDS *TSyncSession::findLocalDataStore(void *aDSHandle)
3172 TLocalDataStorePContainer::iterator pos;
3173 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
3175 TLocalEngineDS *ldsP = (*pos);
3176 if ((void *)(ldsP->getDSConfig())==aDSHandle)
3177 return (ldsP); // found
3179 return NULL; // none found
3180 } // TSyncSession::findLocalDataStore
3183 TLocalEngineDS *TSyncSession::addLocalDataStore(TLocalDSConfig *aLocalDSConfigP)
3185 TLocalEngineDS *ldsP=aLocalDSConfigP->newLocalDataStore(this);
3186 fLocalDataStores.push_back(ldsP);
3188 } // TSyncSession::addLocalDataStore
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)
3196 TSyncItemTypePContainer::iterator pos;
3197 for (pos=fLocalItemTypes.begin(); pos!=fLocalItemTypes.end(); ++pos) {
3199 if ((void *)((*pos)->getTypeConfig())==aDataTypeConfigP)
3200 return (*pos); // found
3202 return NULL; // none found
3203 } // TSyncSession::findLocalType
3206 // - find implemented remote datatype by config pointer (and related datastore, if any)
3207 TSyncItemType *TSyncSession::findRemoteType(TDataTypeConfig *aDataTypeConfigP, TSyncDataStore *aRelatedRemoteDS)
3209 TSyncItemTypePContainer::iterator pos;
3210 for (pos=fRemoteItemTypes.begin(); pos!=fRemoteItemTypes.end(); ++pos) {
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
3218 return NULL; // none found
3219 } // TSyncSession::findRemoteType
3225 // get new list of all local datastores
3226 SmlDevInfDatastoreListPtr_t TSyncSession::newDevInfDataStoreList(bool aAlertedOnly, bool aWithoutCTCapProps)
3228 SmlDevInfDatastoreListPtr_t rootP,*insertpos;
3229 SmlDevInfDatastorePtr_t datastoreP;
3231 // no list at beginning
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)
3240 if (!(*pos1)->testState(dssta_clientsentalert))
3241 continue; // not alerted, do not show this one
3243 // see if we have info at all
3244 datastoreP = (*pos1)->getDatastoreDevinf(IS_SERVER, aWithoutCTCapProps);
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);
3255 } // TSyncSession::newDevInfDataStoreList
3258 // get common sync capabilities mask of this session (datastores might modify it)
3259 uInt32 TSyncSession::getSyncCapMask(void)
3263 (getSessionConfig()->fAcceptServerAlerted ? SCAP_MASK_SERVER_ALERTED : 0);
3264 } // TSyncSession::getSyncCapMask
3267 // get new list of all local item types
3268 SmlDevInfCtcapListPtr_t TSyncSession::newLocalCTCapList(bool aAlertedOnly, TLocalEngineDS *aOnlyForDS, bool aWithoutCTCapProps)
3270 SmlDevInfCtcapListPtr_t rootP,*insertpos;
3271 SmlDevInfCTCapPtr_t ctcapP;
3273 // no list at beginning
3277 TTypeVariantDescriptor variantDesc = NULL;
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) {
3287 // check for restriction to certain datastore
3289 // only show if specified datastore is using the type
3290 showTy = aOnlyForDS->doesUseType(*pos2, &variantDesc);
3292 // see if datatype is used by any of the alerted datastores
3293 if (showTy && aAlertedOnly) {
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)) {
3305 // now show if selected
3307 // see if we have info at all
3308 ctcapP = (*pos2)->getCTCapDevInf(aOnlyForDS, variantDesc, aWithoutCTCapProps);
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);
3320 } // TSyncSession::newLocalCTCapList
3323 // build DevInf of this session
3324 SmlDevInfDevInfPtr_t TSyncSession::newDevInf(bool aAlertedOnly, bool aWithoutCTCapProps)
3326 SmlDevInfDevInfPtr_t devinfP;
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);
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)
3359 devinfP->flags |= SmlDevInfUTC_f; // we can handle UTC
3360 // - we support large object
3361 devinfP->flags |= SmlDevInfLargeObject_f;
3363 // Now get extensions info
3365 // %%% tdb, optional
3368 } // TSyncSession::newDevInf
3371 // get devInf for this session (caller is passed ownership)
3372 SmlItemPtr_t TSyncSession::getLocalDevInfItem(bool aAlertedOnly, bool aWithoutCTCapProps)
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());
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);
3400 } // TSyncSession::getLocalDevInfItem
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
3409 localstatus sta = LOCERR_OK;
3410 PDEBUGBLOCKDESC("DevInf_Analyze","Analyzing remote devInf");
3412 PDEBUGPRINTFX(DBG_ERROR,("Warning: No DevInf found (possible cause: improperly encoded devInf from remote)"));
3413 sta=400; // no devInf
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))
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))
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))
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;
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"
3479 // detect remote specific server behaviour if needed
3480 sta = checkRemoteSpecifics(aDevInfP);
3481 if (sta!=LOCERR_OK) {
3482 remoteAnalyzed(); // analyzed to reject
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"
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");
3502 PDEBUGPRINTFX(DBG_REMOTEINFO+DBG_HOT,("Remote rule prevents looking at CTCap"));
3508 // create appropriate remote data itemtypes
3509 if (TSyncItemType::analyzeCTCapAndCreateItemTypes(
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
3516 // we have CTCap info of at least one remote type
3517 fRemoteDataTypesKnown=true;
3520 PDEBUGPRINTFX(DBG_ERROR,("CTCap could not be used (missing version)"));
3524 // - go to next item
3528 PDEBUGENDBLOCK("RemoteTypes");
3530 // now get datastores
3531 PDEBUGBLOCKDESC("RemoteDatastores", "Analyzing remote datastores");
3532 SmlDevInfDatastoreListPtr_t dslP = aDevInfP->datastore;
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(
3544 fLocalItemTypes, // look up for datatypes here first
3545 fRemoteItemTypes // but add types here
3548 PDEBUGPRINTFX(DBG_ERROR,("Invalid DataStore devInf"));
3549 delete datastoreP; // forget invalid data store
3553 // CTCap set successfully, new type created
3554 // - save it in the list of remote types
3555 fRemoteDataStores.push_back(datastoreP);
3557 PDEBUGENDBLOCK("RemoteDSDevInf");
3559 // - go to next item
3562 PDEBUGENDBLOCK("RemoteDatastores");
3563 } // else sync not started yet
3565 // give descendants possibility to do something with the analyzed data
3569 PDEBUGENDBLOCK("DevInf_Analyze");
3571 } // TSyncSession::analyzeRemoteDevInf
3574 #ifdef SYSYNC_SERVER
3576 // Initialize Sync: set up datastores and types for server sync session
3577 localstatus TSyncSession::initSync(
3578 const char *aLocalDatastoreURI,
3579 const char *aRemoteDatastoreURI
3582 localstatus sta = LOCERR_OK;
3584 // search for local datastore first
3586 // - search for datastore and obtain possible CGI
3587 fLocalSyncDatastoreP = findLocalDataStoreByURI(SessionRelativeURI(aLocalDatastoreURI),&cgiOptions);
3588 if (!fLocalSyncDatastoreP) {
3589 // no such local datastore
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(
3600 true // we are parsing options from <sync> target URI
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)"
3612 #ifdef OBJECT_FILTERING
3613 // Show filter summary
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
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
3627 #endif // OBJECT_FILTERING
3630 } // TSyncSession::initSync
3632 #endif // SYSYNC_SERVER
3636 // end sync group (of client sync commands)
3637 bool TSyncSession::processSyncEnd(bool &aQueueForLater)
3642 if (fLocalSyncDatastoreP) {
3643 // let datastore process it
3644 ok=fLocalSyncDatastoreP->engProcessSyncCmdEnd(aQueueForLater);
3647 #ifdef SYNCSTATUS_AT_SYNC_CLOSE
3648 // %%% status for sync command sent AFTER statuses for contained commands
3649 if (fSyncCloseStatusCommandP)
3650 issueRoot(fSyncCloseStatusCommandP);
3652 // no local datastore active
3653 fLocalSyncDatastoreP=NULL;
3655 } // TSyncSession::processSyncEnd
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
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"));
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
3696 // process Sync operation sent by remote
3698 PDEBUGPRINTFX(DBG_DATA,(
3699 "Remote sent %s-operation:",
3700 SyncOpNames[aSyncOp]
3702 PDEBUGPRINTFX(DBG_DATA,(
3703 "- Source: remoteID ='%s', remoteName='%s'",
3704 smlSrcTargLocURIToCharP(aItemP->source),
3705 smlSrcTargLocNameToCharP(aItemP->source)
3707 PDEBUGPRINTFX(DBG_DATA,(
3708 "- Target: localID ='%s', remoteName='%s'",
3709 smlSrcTargLocURIToCharP(aItemP->target),
3710 smlSrcTargLocNameToCharP(aItemP->target)
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;
3723 TScriptContext::executeTest(
3724 regular, // pass through regular status
3725 fSessionScriptContextP,
3726 getSessionConfig()->fReceivedItemStatusScript,
3728 &errctx // caller context
3730 // not completely handled, use possibly modified status code
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]));
3736 aStatusCommand.setStatusCode(errctx.newstatuscode);
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()
3746 fLocalSyncDatastoreP->fLocalItemsError++; // count this as an error, as remote will see it as such
3749 PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
3750 "processSyncOpItem: Irregularity while processing item, status=%hd",
3751 aStatusCommand.getStatusCode()
3757 } // TSyncSession::processSyncOpItem
3760 #endif // not SYNCSESSION_PART2_EXCLUDE
3761 #ifndef SYNCSESSION_PART1_EXCLUDE
3764 // generate challenge for session
3765 SmlChalPtr_t TSyncSession::newSessionChallenge(void)
3768 getNextNonce(fRemoteURI.c_str(),nonce);
3769 PDEBUGPRINTFX(DBG_PROTO,(
3770 "Challenge for next auth: AuthType=%s, Nonce='%s', binary %sallowed",
3771 authTypeSyncMLNames[requestedAuthType()],
3773 getEncoding()==SML_WBXML ? "" : "NOT "
3775 return newChallenge(requestedAuthType(),nonce,getEncoding()==SML_WBXML);
3776 } // TSyncSession::newSessionChallenge
3779 // generate credentials (based on fRemoteNonce, fRemoteRequestedAuth, fRemoteRequestedAuthEnc)
3780 SmlCredPtr_t TSyncSession::newCredentials(const char *aUser, const char *aPassword)
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
3791 // - build basic user/pw string
3793 userpw.assign(aUser);
3795 userpw.append(aPassword);
3797 switch (fRemoteRequestedAuth) {
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
3811 // basic auth is always b64 encoded
3812 fRemoteRequestedAuthEnc=fmt_b64;
3813 authdata=(uInt8 *)userpw.c_str();
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
3822 userpw+=fRemoteNonce; // append nonce string, might contain NULs
3825 md5::SYSYNC_MD5_CTX context;
3826 md5::Init (&context);
3827 md5::Update (&context, (unsigned const char *)userpw.c_str(), userpw.size());
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
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;
3854 // auth data is 16 byte digest value in binary
3855 authdata=(uInt8 *)digest;
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;
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
3881 if (tobefreed) b64::free(tobefreed);
3882 // return cred or NULL if none
3884 } // TSyncSession::newCredentials
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)
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;
3898 #ifdef EXPIRES_AFTER_DATE
3899 // check for hard expiry again
3901 (fCopyOfScrambledNow>(SCRAMBLED_EXPIRY_VALUE*4+503))
3902 #ifdef SYSER_REGISTRATION
3903 && (!getSyncAppBase()->fRegOK) // only abort if no registration (but accept timed registration)
3906 aStatusCommand.setStatusCode(401); // seems to be hacked
3910 // Check type of credentials
3912 // Anonymous login attempt
3916 SmlMetInfMetInfPtr_t metaP;
3917 if ((metaP=smlPCDataToMetInfP(aCredP->meta))!=NULL) {
3920 // get type (otherwise default to auth-basic)
3921 const char *ty = smlPCDataToCharP(metaP->type);
3923 if (StrToEnum(authTypeSyncMLNames,numAuthTypes,t,ty)) {
3924 authtype=(TAuthTypes)t;
3928 authtype=auth_none; // disable checking below
3929 aStatusCommand.setStatusCode(406); // unsupported optional feature (auth method)
3930 aStatusCommand.addItemString(ty); // identify bad auth method
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
3940 // - handle format and get auth data according to auth type
3941 authdata = smlPCDataToCharP(aCredP->data); // get it as is
3945 // anonymous login attempt
3946 // - no special measure needed
3949 // basic is always b64 encoded
3951 if (authfmt!=fmt_b64)
3952 PDEBUGPRINTFX(DBG_ERROR,("Auth-basic has no <format>b64 spec --> assumed b64 anyway"));
3954 authfmt=fmt_b64; // basic is ALWAYS b64
3957 // verify that we have the username in clear text for MD5 auth
3958 if (!aUserName || *aUserName==0)
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)"));
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
3974 tobefreed=b64::encode((uInt8 *)authdata,16,&l);
3976 // now authdata is b64 as well
3987 #ifndef MINIMAL_CODE
3988 // save user name for later reference
3989 if (aUserName) fSyncUserName.assign(aUserName);
3990 else fSyncUserName.erase();
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"));
4003 aStatusCommand.setChallenge(newSessionChallenge());
4008 // verify format (must be MD5 by now)
4009 if (authfmt!=fmt_b64) {
4010 aStatusCommand.setStatusCode(415); // unsupported format
4012 else if (!authdata || !*authdata) {
4013 aStatusCommand.setStatusCode(400); // missing data, malformed request
4016 // first check credentials
4017 // NOTE: This must be done first, to force calling SessionLogin
4019 authok=checkCredentials(aUserName,authdata,authtype);
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"));
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"));
4033 aStatusCommand.setChallenge(newSessionChallenge());
4037 // free buffer if any
4038 if (tobefreed) b64::free(tobefreed);
4039 // make sure we see what config was used in the log
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,
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()
4060 } // TSyncSession::checkCredentials(SmlCredPtr_t...)
4063 // check credential string
4064 bool TSyncSession::checkCredentials(const char *aUserName, const char *aCred, TAuthTypes aAuthType)
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;
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)",
4079 aUserName ? aUserName : "[NULL Username]"
4082 // we have the password in clear text
4083 return SessionLogin(user.c_str(), password.c_str(), sectyp_clearpass, fRemoteURI.c_str());
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());
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());
4095 return false; // unknown auth, is not ok
4097 } // TSyncSession::checkCredentials(const char *...)
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
4112 PDEBUGPRINTFX(DBG_ADMIN,("Username = %s",aUserName));
4113 DEBUGPRINTFX(DBG_USERDATA+DBG_EXOTIC,("Password = %s",aPassWord));
4115 if (aAuthStringType==sectyp_anonymous) {
4116 return (aPassWord==NULL || *aPassWord==0); // anonymous login ok if no password expected
4118 else if (aAuthStringType==sectyp_clearpass) {
4119 return (strcmp(aAuthString,aPassWord)==0); // login ok if password matches
4123 // - concatenate user:password
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);
4133 return checkMD5WithNonce(upw.c_str(),aNonce,aAuthString);
4135 // unknown auth secret type
4137 } // TSyncSession::checkAuthPlain
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
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);
4153 else if (aAuthStringType==sectyp_clearpass) {
4154 // we must generate the MD5B64(user:pw) from clear text
4155 string myAuthString = aUserName;
4157 myAuthString+=aAuthString;
4158 MD5B64(myAuthString.c_str(),myAuthString.size(),myAuthString);
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()));
4163 // then we can directly compare them
4164 return myAuthString==aMD5B64;
4167 return false; // we cannot auth V1.0 MD5 against MD5B64(user:password)
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,
4178 const char *aMD5B64Creds
4181 string pattern; // pattern to match with
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;
4189 pattern.append(aNonce);
4190 // - MD5 and B64 entire thing (again)
4191 MD5B64(pattern.c_str(),pattern.size(),pattern);
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));
4198 // - now compare with given credentials
4199 return strnncmp(aMD5B64Creds,pattern.c_str(),pattern.size())==0;
4200 } // TSyncSession::checkMD5WithNonce
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)
4206 // - convert to user/pw string
4208 uInt8 *userpw=b64::decode(aBasicCreds, 0, &userpwlen);
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,':');
4217 aUsername.assign((const char *)userpw,p-(const char *)userpw);
4219 aPassword.assign(p+1);
4225 } // TSyncSession::getAuthBasicUserPass
4228 // check credential string (clear text pw, MD5, etc.)
4229 // This function is normally derived to provide checking of auth string
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
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
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) {
4254 getAuthNonce(aDeviceID,nonce);
4257 return checkAuthPlain(
4258 scP->fSimpleAuthUser.c_str(),
4259 scP->fSimpleAuthPassword.c_str(),
4264 } // TSyncSession::SessionLogin
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)
4274 #if defined(SYSER_REGISTRATION) || !defined(NO_REMOTE_RULES)
4275 localstatus sta = LOCERR_OK;
4278 // check hard-coded restrictions
4281 #ifdef REMOTE_RESTR_DEVID
4282 || strwildcmp(smlPCDataToCharP(aDevInfP->devid),REMOTE_RESTR_DEVID)!=0
4284 #ifdef REMOTE_RESTR_MAN
4285 || strwildcmp(smlPCDataToCharP(aDevInfP->man),REMOTE_RESTR_MAN)!=0
4287 #ifdef REMOTE_RESTR_MOD
4288 || strwildcmp(smlPCDataToCharP(aDevInfP->mod),REMOTE_RESTR_MOD)!=0
4290 #ifdef REMOTE_RESTR_OEM
4291 || strwildcmp(smlPCDataToCharP(aDevInfP->oem),REMOTE_RESTR_OEM)!=0
4293 #ifdef REMOTE_RESTR_URI
4294 || strwildcmp(fRemoteURI.c_str(),REMOTE_RESTR_URI)!=0
4297 PDEBUGPRINTFX(DBG_ERROR,("Software not allowed syncing with this remote party"));
4298 AbortSession(403,true);
4302 // check license restrictions
4303 #ifdef SYSER_REGISTRATION
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();
4318 if (restrid.size()==1) {
4319 // there is a restriction
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"));
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;
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
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);
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
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)))
4366 // found matching rule
4367 PDEBUGPRINTFX(DBG_HOT,("Found <remoterule> '%s' matching for this peer",ruleP->getName()));
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()));
4376 // if this rule is final, don't check for further matches
4377 if (ruleP->fFinalRule) break;
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;
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);
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);
4432 PDEBUGPRINTFX(DBG_HOT,("Executing rulescript for rule '%s'",ruleP->getName()));
4433 TScriptContext::execute(
4434 fSessionScriptContextP,
4436 NULL, // context's function table
4437 NULL // datastore pointer needed for context
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())
4449 // no remote rule (none found or mechanism excluded by NO_REMOTE_RULES)
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)
4463 fRemoteCanHandleUTC = true; // Assume server can handle UTC (it is very improbable a server can't)
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)
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()));
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]));
4503 } // TSyncSession::checkRemoteSpecifics
4506 #ifndef NO_REMOTE_RULES
4508 // check if given rule (by name, or if aRuleName=NULL by rule pointer) is active
4509 bool TSyncSession::isActiveRule(cAppCharP aRuleName, TRemoteRuleConfig *aRuleP)
4511 TRemoteRulesList::iterator pos;
4512 for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {
4514 (aRuleName==NULL && (*pos)==aRuleP) || // match by pointer...
4515 (strucmp(aRuleName,(*pos)->getName())==0) // ...or name
4521 } // TSyncSession::isActiveRule
4523 #endif // NO_REMOTE_RULES
4527 TSessionConfig *TSyncSession::getSessionConfig(void)
4529 TSessionConfig *scP;
4530 GET_CASTED_PTR(scP,TSessionConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TSessionConfig","sss1"));
4532 } // TSyncSession::getSessionConfig
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
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
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)
4554 #ifdef SCRIPT_SUPPORT
4555 // call script that might issue GETs and PUTs
4557 TGetPutResultFuncContext ctx;
4561 ctx.itemURI.erase();
4562 ctx.itemData.erase();
4563 ctx.metaType.erase();
4565 TScriptContext::execute(
4566 fSessionScriptContextP,
4567 getSessionConfig()->fCustomGetPutScript,
4568 &GetPutResultFuncTable,
4569 &ctx // caller context
4572 } // TSyncSession::issueCustomGetPut
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)
4579 #ifdef SCRIPT_SUPPORT
4580 // call script that might issue GETs and PUTs
4582 TGetPutResultFuncContext ctx;
4586 ctx.itemURI.erase();
4587 ctx.itemData.erase();
4588 ctx.metaType.erase();
4590 TScriptContext::execute(
4591 fSessionScriptContextP,
4592 getSessionConfig()->fCustomEndPutScript,
4593 &GetPutResultFuncTable,
4594 &ctx // caller context
4597 } // TSyncSession::issueCustomEndPut
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)
4607 TResultsCommand *resultsCmdP = NULL;
4608 #ifdef SCRIPT_SUPPORT
4609 // first check if script handles it
4611 TGetPutResultFuncContext ctx;
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())));
4625 TScriptContext::executeTest(
4626 false, // do not assume script handles the GET
4627 fSessionScriptContextP,
4628 getSessionConfig()->fCustomGetHandlerScript,
4629 &GetPutResultFuncTable,
4630 &ctx // caller context
4632 // - update status, anyway
4633 aStatusCommand.setStatusCode(ctx.statuscode);
4634 // - create result, if script decides so
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
4653 // look for ./devinf10 special case
4654 if (strucmp(aLocUri,SyncMLDevInfNames[getSyncMLVersion()])==0) {
4656 aStatusCommand.setStatusCode(200);
4657 // prepare a devinf10 <Result>
4658 resultsCmdP = new TDevInfResultsCommand(this,aGetCommandP);
4661 } // TSyncSession::processGetItem
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)
4668 localstatus sta = aStatusCommand.getStatusCode();
4669 #ifdef SCRIPT_SUPPORT
4670 // first check if script handles it
4672 TGetPutResultFuncContext ctx;
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())));
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
4694 // update status code
4697 aStatusCommand.setStatusCode(syncmlError(sta));
4698 // if processed, return now
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);
4709 aStatusCommand.setStatusCode(200); // assume ok
4710 sta=analyzeRemoteDevInf(devinfP);
4712 aStatusCommand.setStatusCode(syncmlError(sta));
4716 PDEBUGPRINTFX(DBG_ERROR,(
4717 "Unknown %s-command with URI=%s received, returning default status=%hd",
4718 aIsPut ? "PUT" : "RESULTS",
4723 } // TSyncSession::processPutResultItem
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
4739 // no alert response command by default
4740 TSmlCommand *alertresponsecmdP=NULL;
4741 TLocalEngineDS *datastoreP;
4742 string optionsCGI,identifyingTargetURI;
4744 // dispatch numeric alerts
4745 switch (aAlertCode) {
4753 // Sync resume alert
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]
4763 aStatusCommand.setStatusCode(403); // forbidden
4764 ADDDEBUGITEM(aStatusCommand,"Sync Alert only allowed in init package");
4765 return NULL; // no alert sent back
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)
4774 // no such local datastore
4775 aStatusCommand.setStatusCode(404); // not found
4778 // save alerted datastore pointer (will be returned to caller, which is TAlertCommand)
4779 aLocalDataStoreP=datastoreP;
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
4786 const char *targetURI = smlSrcTargLocURIToCharP(aItemP->target);
4787 const char *sourceURI = smlSrcTargLocURIToCharP(aItemP->source);
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
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
4816 SuspendSession(514);
4820 PDEBUGPRINTFX(DBG_HOT,(
4821 "---------------- DISPLAY ALERT (100): %s",
4822 smlPCDataToCharP(aItemP->data)
4824 // show it on the console
4826 "***** Message from Remote: %s",
4827 smlPCDataToCharP(aItemP->data)
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)));
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)
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));
4854 } // switch fAlertCode
4855 // return command generated (or NULL if none)
4856 return alertresponsecmdP;
4857 } // TSyncSession::processAlertItem
4862 #define XML_TRANSLATION_ENABLED
4864 #undef XML_TRANSLATION_ENABLED
4869 void TSyncSession::XMLTranslationIncomingStart(void)
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(
4877 getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
4878 fIncomingXMLInstance
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"))
4886 fXMLtranslate=false;
4888 } // TSyncSession::XMLTranslationIncomingStart
4891 void TSyncSession::XMLTranslationOutgoingStart(void)
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(
4899 getRootConfig()->fLocalMaxMsgSize * 3, // XML should not be more than 3 times larger than WBXML
4900 fOutgoingXMLInstance
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"))
4908 fXMLtranslate=false;
4910 } // TSyncSession::XMLTranslationOutgoingStart
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)
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..."))
4927 DEBUGPRINTFX(DBG_EXOTIC,("- Writing incoming XML translation"))
4928 XMLtext=NULL; XMLsize=0;
4929 if (smlLockReadBuffer(fIncomingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
4931 TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
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
4941 // open file in raw mode
4942 if (dbgOutP->openDbg(
4943 dumpfilename.c_str(),
4946 false, // append to existing if any
4949 // write out the entire message
4950 dbgOutP->putRawData(XMLtext, XMLsize);
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(),
4964 PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
4970 if (fIncomingXMLInstance) {
4971 // finally free instance
4972 getSyncAppBase()->freeSmlInstance(fIncomingXMLInstance);
4973 fIncomingXMLInstance=NULL;
4976 } // TSyncSession::XMLTranslationIncomingEnd
4979 // finish and output XML translation of outgoing traffic
4981 /// rewrite this to use a TDbgOut object to write stuff
4982 void TSyncSession::XMLTranslationOutgoingEnd(void)
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..."))
4992 // write only if session is debug-enabled
4994 DEBUGPRINTFX(DBG_EXOTIC,("- Writing outgoing XML translation"))
4995 XMLtext=NULL; XMLsize=0;
4996 if (smlLockReadBuffer(fOutgoingXMLInstance,&XMLtext,&XMLsize)==SML_ERR_OK) {
4998 TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
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
5008 // open file in raw mode
5009 if (dbgOutP->openDbg(
5010 dumpfilename.c_str(),
5013 false, // append to existing if any
5016 // write out the entire message
5017 dbgOutP->putRawData(XMLtext, XMLsize);
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(),
5031 PDEBUGPRINTFX(DBG_ERROR,("Cannot write <xmltranslate> file"));
5037 if (fOutgoingXMLInstance) {
5038 // finally free instance
5039 getSyncAppBase()->freeSmlInstance(fOutgoingXMLInstance);
5040 fOutgoingXMLInstance=NULL;
5043 } // TSyncSession::XMLTranslationOutgoingEnd
5046 // dump message from specified buffer
5047 void TSyncSession::DumpSyncMLBuffer(MemPtr_t aBuffer, MemSize_t aBufSize, bool aOutgoing, Ret_t aDecoderError)
5050 // log message currently in SML buffer
5051 if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
5052 TDbgOut *dbgOutP = getSyncAppBase()->newDbgOutputter(false);
5054 // create base file name
5055 string dumpfilename;
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"
5064 if (aDecoderError) {
5065 // append error code
5066 StringObjAppendPrintf(dumpfilename,"_ERR_0x%04X",aDecoderError);
5068 // open file in raw mode
5069 if (dbgOutP->openDbg(
5070 dumpfilename.c_str(),
5071 getEncoding()==SML_XML ? ".xml" : ".wbxml",
5073 false, // append to existing if any
5076 // write out the entire message
5077 dbgOutP->putRawData(aBuffer, aBufSize);
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
5089 aOutgoing ? "outgo" : "incom"
5094 PDEBUGPRINTFX(DBG_ERROR,("Cannot write <msgdump> file"));
5100 } // TSyncSession::DumpSyncMLBuffer
5103 // Dump message in SML buffer to file
5104 void TSyncSession::DumpSyncMLMessage(bool aOutgoing)
5106 // dump message if needed
5108 // log message currently in SML buffer
5109 if (fMsgDump && !getRootConfig()->fDebugConfig.fDebugInfoPath.empty()) {
5113 if (smlPeekMessageBuffer(getSmlWorkspaceID(), aOutgoing, &data, &datasize)==SML_ERR_OK) {
5114 DumpSyncMLBuffer(data,datasize,aOutgoing,SML_ERR_OK);
5118 } // TSyncSession::DumpSyncMLMessage
5124 // SyncML Toolkit callback handlers
5125 // ================================
5128 // start of SyncML message
5129 Ret_t TSyncSession::StartMessage(SmlSyncHdrPtr_t aContentP)
5132 fSessionLogger.DebugDefineMainThread();
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
5141 // set 1/4 of the date here
5142 fCopyOfScrambledNow=((getSyncAppBase()->fScrambledNow)<<2)+503; // scramble again a little
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).
5149 DumpSyncMLMessage(false); // incoming
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");
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,
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,
5169 (long)fSyncAppBaseP->requestCount()
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
5177 StrToEnum(SyncMLVerDTDNames,numSyncMLVersions,hdrVers,smlPCDataToCharP(aContentP->version));
5178 smlStartMessageExt(fIncomingXMLInstance,aContentP,SmlVersionCodes[hdrVers]);
5182 smlGetEncoding(fSmlWorkspaceID,&fEncoding);
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
5191 // special entry point to prematurely abort processing of a incoming message
5192 // and cause the necessary cleanup
5193 void TSyncSession::CancelMessageProcessing(void)
5196 // Now dump XML translation of incoming message (as far as it was processed at all)
5197 XMLTranslationIncomingEnd();
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()
5205 } // TSyncSession::CancelMessageProcessing
5208 Ret_t TSyncSession::EndMessage(Boolean_t final)
5211 // generate XML translation
5212 if (fXMLtranslate && fIncomingXMLInstance)
5213 smlEndMessage(fIncomingXMLInstance,final);
5214 // Now dump XML translation of incoming message
5215 XMLTranslationIncomingEnd();
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()
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)
5232 // forget pending continue requests
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) {
5243 PDEBUGPRINTFX(DBG_PROTO,("Remote must see our changed devInf -> creating PUT command"));
5244 TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
5245 issueRootPtr(putcmdP);
5248 PDEBUGPRINTFX(DBG_PROTO,("Remote should see devinf, but PUT is suppressed: <neverputdevinf>"));
5251 // hook for placing custom GET and PUT
5252 if (!fCustomGetPutSent) {
5253 fCustomGetPutSent=true;
5254 issueCustomGetPut(fRemoteDevInfKnown,fRemoteGotDevinf);
5256 // now issue all commands that may only be issued AFTER sending statuses for incoming commands
5257 TSmlCommandPContainer::iterator pos;
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);
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();
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"));
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;
5287 MP_SHOWCURRENT(DBG_PROFILE,"End of incoming message");
5288 // ok if no exception thrown
5290 } // TSyncSession::EndMessage
5294 Ret_t TSyncSession::StartSync(SmlSyncPtr_t aContentP)
5297 // generate XML translation
5298 if (fXMLtranslate && fIncomingXMLInstance)
5299 smlStartSync(fIncomingXMLInstance,aContentP);
5301 // create command object
5302 TSyncCommand *commandP = new TSyncCommand(this,fIncomingMsgID,aContentP);
5304 return process(commandP);
5305 } // TSyncSession::StartSync
5308 Ret_t TSyncSession::EndSync(void)
5311 // generate XML translation
5312 if (fXMLtranslate && fIncomingXMLInstance)
5313 smlEndSync(fIncomingXMLInstance);
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
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();
5326 // create command object
5327 TSyncEndCommand *commandP = new TSyncEndCommand(this,fIncomingMsgID);
5329 return process(commandP);
5330 } // TSyncSession::EndSync
5333 #ifdef ATOMIC_RECEIVE
5334 Ret_t TSyncSession::StartAtomic(SmlAtomicPtr_t aContentP)
5337 // generate XML translation
5338 if (fXMLtranslate && fIncomingXMLInstance)
5339 smlStartAtomic(fIncomingXMLInstance,aContentP);
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(
5354 406); // optional feature not supported
5356 return process(commandP);
5357 } // TSyncSession::StartAtomic
5359 Ret_t TSyncSession::EndAtomic(void)
5362 // generate XML translation
5363 if (fXMLtranslate && fIncomingXMLInstance)
5364 smlEndAtomic(fIncomingXMLInstance);
5366 // process Atomic end
5367 // %%% not implemented, just accept
5368 PDEBUGPRINTFX(DBG_HOT,("End of Atomic bracket"));
5370 } // TSyncSession::EndAtomic
5374 #ifdef SEQUENCE_RECEIVE
5375 Ret_t TSyncSession::StartSequence(SmlSequencePtr_t aContentP)
5378 // generate XML translation
5379 if (fXMLtranslate && fIncomingXMLInstance)
5380 smlStartSequence(fIncomingXMLInstance,aContentP);
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
5389 PDEBUGPRINTFX(DBG_HOT,("Start of Sequence bracket, nesting level is now %hd",fSequenceNesting));
5392 StrToLong(smlPCDataToCharP(aContentP->cmdID),cmdid);
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
5400 ); // issue ok status
5402 issueRootPtr(statusCmdP);
5405 } // TSyncSession::StartSequence
5408 Ret_t TSyncSession::EndSequence(void)
5411 // generate XML translation
5412 if (fXMLtranslate && fIncomingXMLInstance)
5413 smlEndSequence(fIncomingXMLInstance);
5415 // - keep track of nesting
5416 if (fSequenceNesting<1) {
5418 PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, MISSING PRECEEDING SEQUENCE START -> aborting session"));
5419 AbortSession(400,true); // bad nesting is severe, abort session
5424 PDEBUGPRINTFX(DBG_HOT,("End of Sequence bracket, nesting level is now %hd",fSequenceNesting));
5427 } // TSyncSession::EndSequence
5431 Ret_t TSyncSession::AddCmd(SmlAddPtr_t aContentP)
5434 // generate XML translation
5435 if (fXMLtranslate && fIncomingXMLInstance)
5436 smlAddCmd(fIncomingXMLInstance,aContentP);
5438 // create SyncOp command object
5439 TSyncOpCommand *commandP = new TSyncOpCommand(
5441 fLocalSyncDatastoreP,
5448 return process(commandP);
5449 } // TSyncSession::AddCmd
5452 Ret_t TSyncSession::AlertCmd(SmlAlertPtr_t aContentP)
5455 // generate XML translation
5456 if (fXMLtranslate && fIncomingXMLInstance)
5457 smlAlertCmd(fIncomingXMLInstance,aContentP);
5459 // create command object
5460 TAlertCommand *commandP = new TAlertCommand(this,fIncomingMsgID,aContentP);
5462 return process(commandP);
5463 } // TSyncSession::AlertCmd
5466 Ret_t TSyncSession::DeleteCmd(SmlDeletePtr_t aContentP)
5469 // generate XML translation
5470 if (fXMLtranslate && fIncomingXMLInstance)
5471 smlDeleteCmd(fIncomingXMLInstance,aContentP);
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(
5481 fLocalSyncDatastoreP, // note that this one might be NULL in case previous sync command was delayed
5488 return process(commandP);
5489 } // TSyncSession::DeleteCmd
5492 // process GET commands
5493 Ret_t TSyncSession::GetCmd(SmlGetPtr_t aContentP)
5496 // generate XML translation
5497 if (fXMLtranslate && fIncomingXMLInstance)
5498 smlGetCmd(fIncomingXMLInstance,aContentP);
5500 // create command object
5501 TGetCommand *commandP = new TGetCommand(this,fIncomingMsgID,aContentP);
5503 return process(commandP);
5504 } // TSyncSession::GetCmd
5507 Ret_t TSyncSession::PutCmd(SmlPutPtr_t aContentP)
5510 // generate XML translation
5511 if (fXMLtranslate && fIncomingXMLInstance)
5512 smlPutCmd(fIncomingXMLInstance,aContentP);
5514 // create command object
5515 TPutCommand *commandP = new TPutCommand(this,fIncomingMsgID,aContentP);
5517 return process(commandP);
5518 } // TSyncSession::PutCmd
5522 Ret_t TSyncSession::MapCmd(SmlMapPtr_t aContentP)
5525 // generate XML translation
5526 if (fXMLtranslate && fIncomingXMLInstance)
5527 smlMapCmd(fIncomingXMLInstance,aContentP);
5529 // create command object
5530 TMapCommand *commandP = new TMapCommand(this,fIncomingMsgID,aContentP);
5532 return process(commandP);
5533 } // TSyncSession::MapCmd
5537 #ifdef RESULT_RECEIVE
5538 Ret_t TSyncSession::ResultsCmd(SmlResultsPtr_t aContentP)
5541 // generate XML translation
5542 if (fXMLtranslate && fIncomingXMLInstance)
5543 smlResultsCmd(fIncomingXMLInstance,aContentP);
5545 // create command object
5546 TResultsCommand *commandP = new TResultsCommand(this,fIncomingMsgID,aContentP);
5548 return process(commandP);
5549 } // TSyncSession::ResultsCmd
5553 Ret_t TSyncSession::StatusCmd(SmlStatusPtr_t aContentP)
5556 // generate XML translation
5557 if (fXMLtranslate && fIncomingXMLInstance)
5558 smlStatusCmd(fIncomingXMLInstance,aContentP);
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
5567 Ret_t TSyncSession::ReplaceCmd(SmlReplacePtr_t aContentP)
5570 // generate XML translation
5571 if (fXMLtranslate && fIncomingXMLInstance)
5572 smlReplaceCmd(fIncomingXMLInstance,aContentP);
5574 // create SyncOp command object
5575 TSyncOpCommand *commandP = new TSyncOpCommand(
5577 fLocalSyncDatastoreP,
5584 return process(commandP);
5585 } // TSyncSession::ReplaceCmd
5589 Ret_t TSyncSession::CopyCmd(SmlReplacePtr_t aContentP)
5593 // generate XML translation
5594 if (fXMLtranslate && fIncomingXMLInstance)
5595 smlCopyCmd(fIncomingXMLInstance,aContentP);
5597 #error "We will have incomplete XML translation when only COPY_RECEIVE is defined"
5600 // create SyncOp command object
5601 TSyncOpCommand *commandP = new TSyncOpCommand(
5603 fLocalSyncDatastoreP,
5605 fTreatCopyAsAdd ? sop_add : sop_copy,
5610 return process(commandP);
5611 } // TSyncSession::CopyCmd
5615 Ret_t TSyncSession::MoveCmd(SmlReplacePtr_t aContentP)
5618 // generate XML translation
5619 if (fXMLtranslate && fIncomingXMLInstance)
5620 smlMoveCmd(fIncomingXMLInstance,aContentP);
5622 // create SyncOp command object
5623 TSyncOpCommand *commandP = new TSyncOpCommand(
5625 fLocalSyncDatastoreP,
5632 return process(commandP);
5633 } // TSyncSession::MoveCmd
5636 Ret_t TSyncSession::HandleError(void)
5639 DEBUGPRINTFX(DBG_ERROR,("HandleError reached"));
5640 return SML_ERR_OK; // %%%
5641 } // TSyncSession::HandleError
5645 Ret_t TSyncSession::DummyHandler(const char* msg)
5647 //DEBUGPRINTFX(DBG_ERROR,("DummyHandler: msg=%s",msg));
5649 } // TSyncSession::DummyHandler
5653 #ifdef ENGINEINTERFACE_SUPPORT
5655 // Support for EngineModule common interface
5656 // =========================================
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,
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());
5674 return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
5677 } // TSessionKey::OpenSubKeyByName
5679 #endif // ENGINEINTERFACE_SUPPORT
5682 } // namespace sysync
5685 // factory methods of Session Config
5686 // =================================
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"
5694 // ODBC can be in-between if selected
5696 #include "odbcapids.h"
5702 #ifndef HARDCODED_CONFIG
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)
5708 #ifdef XML2GO_SUPPORT
5709 if (aType && strucmp(aType,"xml2go")==0) {
5710 // xml2go enhanced datastore
5711 return new TXml2goDSConfig(aName,aParentP);
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);
5722 if (aType==0 || strucmp(aType,"odbc")==0 || strucmp(aType,"sql")==0) {
5723 // ODBC enabled datastore
5724 return new TOdbcDSConfig(aName,aParentP);
5728 return NULL; // unknown datastore
5729 } // TSessionConfig::newDatastoreConfig
5731 #endif // HARDCODED_CONFIG
5733 } // namespace sysync
5735 #endif // not SYNCSESSION_PART1_EXCLUDE