4 * Author: Lukas Zeller (luz@synthesis.ch)
6 * TSyncAgent: Provides functionality to run client or server
8 * Unifies former TSyncClient and TSyncServer
10 * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
12 * 2009-09-30 : luz : created from syncclient.cpp and syncserver.cpp
18 #include "prefix_file.h"
20 #include "syncagent.h"
21 #include "syncappbase.h"
23 #ifdef HARD_CODED_SERVER_URI
27 // includes that can't be in .h due to circular references
29 #include "syncsessiondispatch.h"
32 #include "syncclientbase.h"
45 // Support for SySync Diagnostic Tool
46 // ==================================
49 // test login into database
50 int testLogin(int argc, const char *argv[])
54 CONSOLEPRINTF((" login [<username> <password>] [<deviceid>]"));
55 CONSOLEPRINTF((" test login to database with syncml user/password and optional deviceid"));
59 TSyncSession *sessionP = NULL;
60 const char *username = NULL;
61 const char *password = NULL;
62 const char *deviceid = "sysytool_test";
66 // no user/password, test anonymous login
67 if (argc>0) deviceid = argv[0];
70 // login with user/password
79 // get session to work with
81 static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
87 // real login with user and password
88 authok = sessionP->SessionLogin(username, password, sectyp_clearpass, deviceid);
91 // anonymous - do a "login" with empty credentials
92 authok = sessionP->SessionLogin("anonymous", NULL, sectyp_anonymous, deviceid);
96 CONSOLEPRINTF(("+++++ Successfully authorized"));
99 CONSOLEPRINTF(("----- Authorisation failed"));
106 // convert user data into internal format and back
107 int convertData(int argc, const char *argv[])
111 CONSOLEPRINTF((" convert <datastore name> <data file, vcard or vcalendar etc.> [<explicit input type>] [<output type>]"));
112 CONSOLEPRINTF((" Convert data to internal format of specified datastore and back"));
116 TSyncSession *sessionP = NULL;
117 const char *datastore = NULL;
118 const char *rawfilename = NULL;
119 const char *inputtype = NULL;
120 const char *outputtype = NULL;
122 // check for argument
124 CONSOLEPRINTF(("required datatype name and raw file name arguments"));
128 rawfilename = argv[1];
130 // third arg is explicit input type
133 outputtype=inputtype; // default to input type
135 // fourth arg is explicit output type
139 // get session to work with
141 static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
143 sessionP->fRemoteCanHandleUTC = true; // run generator and parser in UTC enabled mode
146 // switch mimimal debugging on
147 sessionP->getDbgLogger()->setMask(sessionP->getDbgLogger()->getMask() | (DBG_PARSE+DBG_GEN));
150 TLocalEngineDS *datastoreP = sessionP->findLocalDataStore(datastore);
151 TSyncItemType *inputtypeP = NULL;
152 TSyncItemType *outputtypeP = NULL;
154 CONSOLEPRINTF(("datastore type '%s' not found",datastore));
160 // search in datastore
161 inputtypeP=datastoreP->getReceiveType(inputtype,NULL);
164 // use preferred rx type
165 inputtypeP=datastoreP->getPreferredRxItemType();
168 CONSOLEPRINTF(("input type not found"));
173 // search in datastore
174 outputtypeP=datastoreP->getSendType(outputtype,NULL);
177 // use preferred rx type
178 outputtypeP=datastoreP->getPreferredTxItemType();
181 CONSOLEPRINTF(("output type not found"));
184 // prepare type usage
185 if (inputtypeP==outputtypeP)
186 inputtypeP->initDataTypeUse(datastoreP, true, true);
188 inputtypeP->initDataTypeUse(datastoreP, false, true);
189 outputtypeP->initDataTypeUse(datastoreP, true, false);
192 // now open file and read data item
197 infile = fopen(rawfilename,"rb");
199 CONSOLEPRINTF(("Cannot open input file '%s' (%d)",rawfilename,errno));
202 // - get size of file
203 fseek(infile,0,SEEK_END);
204 insize=ftell(infile);
205 fseek(infile,0,SEEK_SET);
206 // - create buffer of appropriate size
207 databuffer = new uInt8[insize];
209 CONSOLEPRINTF(("Not enough memory to read input file '%s' (%d)",rawfilename,errno));
213 if (fread(databuffer,1,insize,infile)<insize) {
214 CONSOLEPRINTF(("Error reading input file '%s' (%d)",rawfilename,errno));
217 CONSOLEPRINTF(("\nNow converting into internal field representation\n"));
219 TStatusCommand statusCmd(sessionP);
220 SmlItemPtr_t smlitemP = newItem();
221 smlitemP->data=newPCDataStringX(databuffer,true,insize);
223 // create and fill a Sync item
224 TSyncItem *syncitemP = inputtypeP->newSyncItem(
225 smlitemP, // SyncML toolkit item Data to be converted into SyncItem
226 sop_replace, // the operation to be performed with this item
227 fmt_chr, // assume default (char) format
228 inputtypeP, // target myself
229 datastoreP, // local datastore
230 statusCmd // status command that might be modified in case of error
232 // forget SyncML version
233 smlFreeItemPtr(smlitemP);
235 CONSOLEPRINTF(("Error converting input file to internal format (SyncML status code=%hd)",statusCmd.getStatusCode()));
239 CONSOLEPRINTF(("\nNow copying item and convert back to transport format\n"));
241 // make new for output type
242 TSyncItem *outsyncitemP = outputtypeP->newSyncItem(
243 outputtypeP, // target myself
244 datastoreP // local datastore
247 outsyncitemP->replaceDataFrom(*syncitemP);
250 smlitemP=outputtypeP->newSmlItem(
251 outsyncitemP, // the syncitem to be represented as SyncML
252 datastoreP // local datastore
255 CONSOLEPRINTF(("Could not convert back item data"));
259 // forget converted back item
260 smlFreeItemPtr(smlitemP);
265 #endif // SYSYNC_TOOL
270 #ifdef PRECONFIGURED_SYNCREQUESTS
272 // Implementation of TSyncReqConfig
273 // ================================
275 // config for databases to sync with
276 TSyncReqConfig::TSyncReqConfig(TLocalDSConfig *aLocalDSCfg, TConfigElement *aParentElement) :
277 TConfigElement("syncrequest",aParentElement),
278 fLocalDSConfig(aLocalDSCfg)
281 } // TSyncReqConfig::TSyncReqConfig
284 TSyncReqConfig::~TSyncReqConfig()
287 } // TSyncReqConfig::~TSyncReqConfig
291 void TSyncReqConfig::clear(void)
294 // - local client datatstore subselection path or CGI (such as "test" in "contact/test")
295 fLocalPathExtension.erase();
296 // - remote server DB layer auth
299 // - remote server datastore path
300 fServerDBPath.erase();
302 fSyncMode=smo_twoway;
303 fSlowSync=false; // default to non-slow
304 // - DS 1.2 filtering parameters
305 fRecordFilterQuery.erase();
306 fFilterInclusive=false;
309 } // TSyncReqConfig::clear
312 // config element parsing
313 bool TSyncReqConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
315 // checking the elements
316 if (strucmp(aElementName,"localpathextension")==0)
317 expectString(fLocalPathExtension);
318 else if (strucmp(aElementName,"dbuser")==0)
319 expectString(fDBUser);
320 else if (strucmp(aElementName,"dbpassword")==0)
321 expectString(fDBPassword);
322 else if (strucmp(aElementName,"dbpath")==0)
323 expectString(fServerDBPath);
324 else if (strucmp(aElementName,"syncmode")==0)
325 expectEnum(sizeof(fSyncMode),&fSyncMode,SyncModeNames,numSyncModes);
326 else if (strucmp(aElementName,"slowsync")==0)
327 expectBool(fSlowSync);
328 else if (strucmp(aElementName,"recordfilter")==0)
329 expectString(fRecordFilterQuery);
330 else if (strucmp(aElementName,"filterinclusive")==0)
331 expectBool(fFilterInclusive);
335 return inherited::localStartElement(aElementName,aAttributes,aLine);
338 } // TSyncReqConfig::localStartElement
342 void TSyncReqConfig::localResolve(bool aLastPass)
345 // check for required settings
349 inherited::localResolve(aLastPass);
350 } // TSyncReqConfig::localResolve
353 // create appropriate type of local datastore from config and init sync parameters
354 TLocalEngineDS *TSyncReqConfig::initNewLocalDataStore(TSyncSession *aSessionP)
356 // - create appropriate type of localdsP
357 TLocalEngineDS *localdsP = fLocalDSConfig->newLocalDataStore(aSessionP);
359 localdsP->dsSetClientSyncParams(
362 fServerDBPath.c_str(),
365 fLocalPathExtension.c_str(),
366 fRecordFilterQuery.c_str(),
370 } // TSyncReqConfig::initNewLocalDataStore
372 #endif // PRECONFIGURED_SYNCREQUESTS
375 // Implementation of TAgentConfig
376 // ==============================
379 TAgentConfig::TAgentConfig(const char* aName, TConfigElement *aParentElement) :
380 inherited(aName,aParentElement)
383 } // TAgentConfig::TAgentConfig
386 TAgentConfig::~TAgentConfig()
389 } // TAgentConfig::~TAgentConfig
393 void TAgentConfig::clear(void)
397 // Note: we always clear both client and server fields - even if we'll only use one set later
399 // init client auth defaults (note that these MUST correspond with the defaults set by loadRemoteParams() !!!
400 fAssumedServerAuth=auth_none; // start with no auth
401 fAssumedServerAuthEnc=fmt_chr; // start with char encoding
402 fAssumedNonce.erase(); // start with no nonce
403 // auth retry options (mainly for stupid servers like SCTS)
404 #ifdef SCTS_COMPATIBILITY_HACKS
405 fNewSessionForAuthRetry=false;
406 fNoRespURIForAuthRetry=false;
408 fNewSessionForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
409 fNoRespURIForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
411 fSmartAuthRetry=true; // try to be smart and try different auth retry (different from fNewSessionForAuthRetry/fNoRespURIForAuthRetry) if first attempts fail
413 fPutDevInfAtSlowSync=true; // smartner server needs it, and it does not harm so we have it on by default
414 #ifndef NO_LOCAL_DBLOGIN
415 fLocalDBUser.erase();
416 fLocalDBPassword.erase();
417 fNoLocalDBLogin=false;
419 #ifdef PRECONFIGURED_SYNCREQUESTS
420 fEncoding=SML_XML; // default to more readable XML
422 fServerPassword.erase();
424 fTransportUser.erase();
425 fTransportPassword.erase();
429 fProxyPassword.erase();
430 // remove sync db specifications
431 TSyncReqList::iterator pos;
432 for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
434 fSyncRequests.clear();
438 // modify timeout after inherited sets it
439 fSessionTimeout=DEFAULT_CLIENTSESSIONTIMEOUT;
440 // SyncML version support
441 fAssumedServerVersion=MAX_SYNCML_VERSION; // try with highest version we support
442 fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION; // support what we request (overrides session default)
445 // init server defaults
446 fRequestedAuth = auth_md5;
447 fRequiredAuth = auth_md5;
449 fConstantNonce.erase();
450 fExternalURL.erase();
451 fMaxGUIDSizeSent = 32; // reasonable size, but prevent braindamaged Exchange-size IDs to be sent
453 fRespURIOnlyWhenDifferent = true;
454 // modify timeout after inherited sets it
455 fSessionTimeout=DEFAULT_SERVERSESSIONTIMEOUT;
457 } // TAgentConfig::clear
460 #ifndef HARDCODED_CONFIG
462 // config element parsing
463 bool TAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
466 // check the client elements
468 // - defaults for starting a session
469 if (strucmp(aElementName,"defaultsyncmlversion")==0)
470 expectEnum(sizeof(fAssumedServerVersion),&fAssumedServerVersion,SyncMLVersionNames,numSyncMLVersions);
471 else if (strucmp(aElementName,"defaultauth")==0)
472 expectEnum(sizeof(fAssumedServerAuth),&fAssumedServerAuth,authTypeNames,numAuthTypes);
473 else if (strucmp(aElementName,"defaultauthencoding")==0)
474 expectEnum(sizeof(fAssumedServerAuthEnc),&fAssumedServerAuthEnc,encodingFmtSyncMLNames,numFmtTypes);
475 else if (strucmp(aElementName,"defaultauthnonce")==0)
476 expectString(fAssumedNonce);
477 else if (strucmp(aElementName,"newsessionforretry")==0)
478 expectBool(fNewSessionForAuthRetry);
479 else if (strucmp(aElementName,"originaluriforretry")==0)
480 expectBool(fNoRespURIForAuthRetry);
481 else if (strucmp(aElementName,"smartauthretry")==0)
482 expectBool(fSmartAuthRetry);
484 else if (strucmp(aElementName,"putdevinfatslowsync")==0)
485 expectBool(fPutDevInfAtSlowSync);
486 else if (strucmp(aElementName,"fakedeviceid")==0)
487 expectString(fFakeDeviceID);
489 #ifndef NO_LOCAL_DBLOGIN
490 if (strucmp(aElementName,"localdbuser")==0)
491 expectString(fLocalDBUser);
492 else if (strucmp(aElementName,"localdbpassword")==0)
493 expectString(fLocalDBPassword);
494 else if (strucmp(aElementName,"nolocaldblogin")==0)
495 expectBool(fNoLocalDBLogin);
498 // serverURL is always available to allow define fixed URL in config that can't be overridden in profiles
499 if (strucmp(aElementName,"serverurl")==0)
500 expectString(fServerURI);
502 #ifdef PRECONFIGURED_SYNCREQUESTS
503 if (strucmp(aElementName,"syncmlencoding")==0)
504 expectEnum(sizeof(fEncoding),&fEncoding,SyncMLEncodingNames,numSyncMLEncodings);
505 else if (strucmp(aElementName,"serveruser")==0)
506 expectString(fServerUser);
507 else if (strucmp(aElementName,"serverpassword")==0)
508 expectString(fServerPassword);
509 else if (strucmp(aElementName,"sockshost")==0)
510 expectString(fSocksHost);
511 else if (strucmp(aElementName,"proxyhost")==0)
512 expectString(fProxyHost);
513 else if (strucmp(aElementName,"proxyuser")==0)
514 expectString(fProxyUser);
515 else if (strucmp(aElementName,"proxypassword")==0)
516 expectString(fProxyPassword);
517 else if (strucmp(aElementName,"transportuser")==0)
518 expectString(fTransportUser);
519 else if (strucmp(aElementName,"transportpassword")==0)
520 expectString(fTransportPassword);
521 // - Sync DB specification
522 else if (strucmp(aElementName,"syncrequest")==0) {
523 // definition of a new datastore
524 const char* nam = getAttr(aAttributes,"datastore");
526 ReportError(true,"syncrequest missing 'datastore' attribute");
530 TLocalDSConfig *localDSCfgP = getLocalDS(nam);
532 return fail("unknown local datastore '%s' specified",nam);
533 // create new syncDB config linked to that datastore
534 TSyncReqConfig *syncreqcfgP = new TSyncReqConfig(localDSCfgP,this);
536 fSyncRequests.push_back(syncreqcfgP);
537 // - let element handle parsing
538 expectChildParsing(*syncreqcfgP);
544 return inherited::localStartElement(aElementName,aAttributes,aLine);
545 #endif // SYSYNC_CLIENT
549 // check the server elements
550 if (strucmp(aElementName,"requestedauth")==0)
551 expectEnum(sizeof(fRequestedAuth),&fRequestedAuth,authTypeNames,numAuthTypes);
552 else if (strucmp(aElementName,"requiredauth")==0)
553 expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
554 // here to maintain compatibility with old pre 1.0.5.3 config files
555 else if (strucmp(aElementName,"reqiredauth")==0)
556 expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
557 else if (strucmp(aElementName,"autononce")==0)
558 expectBool(fAutoNonce);
559 else if (strucmp(aElementName,"constantnonce")==0)
560 expectString(fConstantNonce);
561 else if (strucmp(aElementName,"externalurl")==0)
562 expectString(fExternalURL);
563 else if (strucmp(aElementName,"maxguidsizesent")==0)
564 expectUInt16(fMaxGUIDSizeSent);
565 else if (strucmp(aElementName,"sendrespuri")==0)
566 expectBool(fUseRespURI);
567 else if (strucmp(aElementName,"respurionlywhendifferent")==0)
568 expectBool(fRespURIOnlyWhenDifferent);
571 return inherited::localStartElement(aElementName,aAttributes,aLine);
572 #endif // SYSYNC_SERVER
576 } // TAgentConfig::localStartElement
578 #endif // HARDCODED_CONFIG
582 void TAgentConfig::localResolve(bool aLastPass)
587 #ifdef PRECONFIGURED_SYNCREQUESTS
588 // - resolve requests
589 TSyncReqList::iterator pos;
590 for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
591 (*pos)->Resolve(aLastPass);
593 #endif // SYSYNC_CLIENT
597 if (!fAutoNonce && fConstantNonce.empty())
598 ReportError(false,"Warning: 'constantnonce' should be defined when 'autononce' is not set");
599 #endif // SYSYNC_SERVER
603 inherited::localResolve(aLastPass);
604 } // TAgentConfig::localResolve
608 // Implementation of TSyncAgent
609 // =============================
613 TSyncAgent::TSyncAgent(
614 TSyncAppBase *aAppBaseP,
615 TSyncSessionHandle *aSessionHandleP,
616 const char *aSessionID // a session ID
618 TSyncSession(aAppBaseP,aSessionID)
622 #ifdef HARD_CODED_SERVER_URI
625 #ifdef ENGINE_LIBRARY
627 fClientEngineState = ces_idle;
629 // reset session now to get correct initial state
630 InternalResetSession();
631 // restart with session numbering at 1 (incremented before use)
633 #endif // SYSYNC_CLIENT
637 // init answer buffer
638 fBufferedAnswer = NULL;
639 fBufferedAnswerSize = 0;
643 #ifdef ENGINE_LIBRARY
645 fServerEngineState = ses_needdata;
649 InternalResetSession();
650 // save session handle
651 fSessionHandleP = aSessionHandleP; // link to handle
652 // get config defaults
653 TAgentConfig *configP = static_cast<TAgentConfig *>(aAppBaseP->getRootConfig()->fAgentConfigP);
654 fUseRespURI = configP->fUseRespURI;
655 // create all locally available datastores from config
656 TLocalDSList::iterator pos;
657 for (pos=configP->fDatastores.begin(); pos!=configP->fDatastores.end(); pos++) {
658 // create the datastore
659 addLocalDataStore(*pos);
661 #endif // SYSYNC_SERVER
663 } // TSyncAgent::TSyncAgent
667 TSyncAgent::~TSyncAgent()
670 // make sure everything is terminated BEFORE destruction of hierarchy begins
675 // forget any buffered answers
676 bufferAnswer(NULL,0);
678 InternalResetSession();
679 // show session data transfer
680 PDEBUGPRINTFX(DBG_HOT,(
681 "Session data transfer statistics: incoming bytes=%ld, outgoing bytes=%ld",
682 (long)fIncomingBytes,
685 // DO NOT remove session from dispatcher here,
686 // this is the task of the dispatcher itself!
687 CONSOLEPRINTF(("Terminated SyncML session (server id=%s)\n",getLocalSessionID()));
688 // show end of session in global level
689 POBJDEBUGPRINTFX(getSyncAppBase(),DBG_HOT,(
690 "TSyncAgent::~TSyncAgent: Deleted SyncML session (local session id=%s)",
693 #endif // SYSYNC_SERVER
695 } // TSyncAgent::~TSyncAgent
699 void TSyncAgent::TerminateSession()
702 if (IS_CLIENT && !fTerminated) {
703 InternalResetSession();
704 #ifdef ENGINE_LIBRARY
705 // switch state to done to prevent any further activity via SessionStep()
706 fClientEngineState = ces_done;
709 #endif // SYSYNC_CLIENT
710 inherited::TerminateSession();
711 } // TSyncAgent::TerminateSession
716 void TSyncAgent::InternalResetSession(void)
720 // use remote URI as specified to start a session
721 fRespondURI = fRemoteURI;
722 #ifdef HARD_CODED_SERVER_URI
723 #if defined(CUSTOM_URI_SUFFIX) && !defined(HARD_CODED_SERVER_URI_LEN)
724 #error "HARD_CODED_SERVER_URI_LEN must be defined when using CUSTOM_URI_SUFFIX"
726 #ifdef HARD_CODED_SERVER_URI_LEN
727 // only part of URL must match (max HARD_CODED_SERVER_URI_LEN chars will be added, less if URI is shorter)
728 fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false, HARD_CODED_SERVER_URI_LEN);
730 // entire URL (except prefix) must match
731 fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false);
734 // set SyncML version
735 // Note: will be overridden with call to loadRemoteParams()
736 fSyncMLVersion = syncml_vers_unknown; // unknown
737 // will be cleared to suppress automatic use of DS 1.2 SINCE/BEFORE filters
738 // (e.g. for date range in func_SetDaysRange())
739 fServerHasSINCEBEFORE = true;
740 // no outgoing alert 222 sent so far
741 fOutgoingAlert222Count = 0;
746 // %%% remove this as soon as Server is 1.1 compliant
747 //fSyncMLVersion=syncml_vers_1_0; // only accepts 1.0 for now %%%%
750 } // TSyncAgent::InternalResetSession
754 void TSyncAgent::ResetSession(void)
756 // let ancestor do its stuff
757 TSyncSession::ResetSession();
758 // do my own stuff (and probably modify settings of ancestor!)
759 InternalResetSession();
760 } // TSyncAgent::ResetSession
763 bool TSyncAgent::MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
767 return ClientMessageStarted(aContentP,aStatusCommand,aBad);
768 #endif // SYSYNC_CLIENT
772 return ServerMessageStarted(aContentP,aStatusCommand,aBad);
773 #endif // SYSYNC_SERVER
777 void TSyncAgent::MessageEnded(bool aIncomingFinal)
781 ClientMessageEnded(aIncomingFinal);
782 #endif // SYSYNC_CLIENT
786 ServerMessageEnded(aIncomingFinal);
787 #endif // SYSYNC_SERVER
792 // starting with engine version 2.0.8.7 a client's device ID (in devinf) is no longer
793 // a constant string, but the device's unique ID
794 string TSyncAgent::getDeviceID(void)
796 if (fLocalURI.empty()) {
798 return SYSYNC_SERVER_DEVID; // return default ID
800 return SYSYNC_CLIENT_DEVID; // return default ID
804 } // TSyncAgent::getDeviceID
807 // ask syncappbase for device type
808 string TSyncAgent::getDeviceType(void)
810 // taken from configuration or default for engine type (client/server)
811 return getSyncAppBase()->getDevTyp();
812 } // TSyncAgent::getDeviceType
817 // initialize the client session and link it with the SML toolkit
818 localstatus TSyncAgent::InitializeSession(uInt32 aProfileSelector, bool aAutoSyncSession)
822 // Select profile now (before creating instance, as encoding is dependent on profile)
823 sta=SelectProfile(aProfileSelector, aAutoSyncSession);
825 // Start a SyncML toolkit instance now and set the encoding from config
826 InstanceID_t myInstance;
827 if (!getSyncAppBase()->newSmlInstance(
829 getRootConfig()->fLocalMaxMsgSize * 2, // twice the message size
832 return LOCERR_SMLFATAL;
834 // let toolkit know the session pointer
835 if (getSyncAppBase()->setSmlInstanceUserData(myInstance,this)!=SML_ERR_OK) // toolkit must know session (as userData)
836 return LOCERR_SMLFATAL;
837 // remember the instance myself
838 setSmlWorkspaceID(myInstance); // session must know toolkit workspace
841 } // TSyncAgent::InitializeSession
845 // select a profile (returns false if profile not found)
846 // Note: base class just tries to retrieve information from
848 localstatus TSyncAgent::SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession)
850 #ifndef PRECONFIGURED_SYNCREQUESTS
851 // no profile settings in config -> error
854 // get profile settings from config
855 TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
857 fEncoding=configP->fEncoding; // SyncML encoding
858 // - set server access details
859 fRemoteURI=configP->fServerURI; // Remote URI = Server URI
860 fTransportUser=configP->fTransportUser; // transport layer user (e.g. HTTP auth)
861 fTransportPassword=configP->fTransportPassword; // transport layer password (e.g. HTTP auth)
862 fServerUser=configP->fServerUser; // Server layer authentification user name
863 fServerPassword=configP->fServerPassword; // Server layer authentification password
864 #ifndef NO_LOCAL_DBLOGIN
865 fLocalDBUser=configP->fLocalDBUser; // Local DB authentification user name (empty if local DB is single user)
866 fNoLocalDBLogin=configP->fNoLocalDBLogin; // if set, no local DB auth takes place, but fLocalDBUser is used as userkey (depending on DB implementation)
867 fLocalDBPassword=configP->fLocalDBPassword; // Local DB authentification password
869 fProxyHost=configP->fProxyHost; // Proxy host
870 fSocksHost=configP->fSocksHost; // Socks host
871 fProxyUser=configP->fProxyUser;
872 fProxyPassword=configP->fProxyPassword;
873 // Reset session after profile change
874 // and also remove any datastores we might have
875 ResetAndRemoveDatastores();
876 // if tunnel, that's all for now
877 if (aProfileSelector==TUNNEL_PROFILE_ID) return LOCERR_OK;
878 // - create and init datastores needed for this session from config
879 // Note: probably config has no sync requests, but they are created later
881 TSyncReqList::iterator pos;
882 for (pos=configP->fSyncRequests.begin(); pos!=configP->fSyncRequests.end(); pos++) {
883 // create and init the datastore
884 fLocalDataStores.push_back(
885 (*pos)->initNewLocalDataStore(this)
888 // create "new" session ID (derivates will do this better)
892 } // TSyncAgent::SelectProfile
895 // make sure we are logged in to local datastore
896 localstatus TSyncAgent::LocalLogin(void)
898 #ifndef NO_LOCAL_DBLOGIN
899 if (!fNoLocalDBLogin && !fLocalDBUser.empty()) {
900 // check authorisation (login to correct user) in local DB
901 if (!SessionLogin(fLocalDBUser.c_str(),fLocalDBPassword.c_str(),sectyp_clearpass,fRemoteURI.c_str())) {
902 return localError(401); // done & error
907 } // TSyncAgent::LocalLogin
911 // process message in the instance buffer
912 localstatus TSyncAgent::processAnswer(void)
914 InstanceID_t myInstance = getSmlWorkspaceID();
918 DEBUGPRINTF(("===> now calling smlProcessData"));
920 MemPtr_t data = NULL;
922 smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
924 fIgnoreMsgErrs=false;
930 // dump the message that failed to process
932 if (data) DumpSyncMLBuffer(data,datasize,false,err);
934 if (!fIgnoreMsgErrs) {
935 PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)err));
936 // other problem or already using SyncML 1.0 --> error
937 return LOCERR_PROCESSMSG;
940 // now check if this is a session restart
942 // this is still the beginning of a session
943 return LOCERR_SESSIONRST;
946 } // TSyncAgentBase::processAnswer
950 // let session produce (or finish producing) next message into
952 // - returns aDone if no answer needs to be sent (=end of session)
953 // - returns 0 if successful
954 // - returns SyncML status code if unsucessfully aborted session
955 localstatus TSyncAgent::NextMessage(bool &aDone)
957 TLocalDataStorePContainer::iterator pos;
960 TP_START(fTPInfo,TP_general); // could be new thread
961 // default to not continuing
963 #ifdef PROGRESS_EVENTS
964 // check for user suspend
965 if (!getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
966 SuspendSession(LOCERR_USERSUSPEND);
969 // done if session was aborted by last received commands
970 if (isAborted()) return getAbortReasonStatus(); // done & error
971 // check package state
972 if (fOutgoingState==psta_idle) {
973 // if suspended here, we'll just stop - nothing has happened yet
974 if (isSuspending()) {
975 AbortSession(fAbortReasonStatus,true);
976 return getAbortReasonStatus();
978 // start of an entirely new client session
979 #ifdef HARD_CODED_SERVER_URI
980 // extra check to limit hacking
981 if (fServerURICRC != SERVER_URI_CRC) {
982 // someone has tried to change the URI
983 DEBUGPRINTFX(DBG_ERROR,("hardcoded Server URI CRC mismatch"));
984 return LOCERR_LIMITED; // user will not know what this means, but we will
987 // - check if we have client requests
988 if (fLocalDataStores.size()<1) {
989 PDEBUGPRINTFX(DBG_ERROR,("No datastores defined to sync with"));
992 // %%% later, we could probably load cached info about
993 // server requested auth, devinf etc. here
994 // use remote URI as specified to start a session
995 fRespondURI=fRemoteURI;
996 // get default params for sending first message to remote
997 // Note: may include remote flag settings that influence creation of my own ID below, that's why we do it now here
999 // get info about my own URI, whatever that is
1000 #ifndef HARDCODED_CONFIG
1001 if (!static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID.empty()) {
1002 // return fake Device ID if we have one defined in the config file (useful for testing)
1003 fLocalURI = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID;
1008 if (!getSyncAppBase()->getMyDeviceID(fLocalURI) || devidWithUserHash()) {
1009 // Device ID is not really unique, make a hash including user name to make it pseudo-unique
1010 // create MD5 hash from non-unique ID and user name
1011 // Note: when compiled with GUARANTEED_UNIQUE_DEVICID, devidWithUserHash() is always false.
1012 md5::SYSYNC_MD5_CTX context;
1013 uInt8 digest[16]; // for MD5 digest
1014 md5::Init (&context);
1015 // - add what we got for ID
1016 md5::Update (&context, (uInt8 *)fLocalURI.c_str(), fLocalURI.size());
1017 // - add user name, if any
1018 if (fLocalURI.size()>0) md5::Update (&context, (uInt8 *)fServerUser.c_str(), fServerUser.size());
1020 md5::Final (digest, &context);
1021 // now make hex string of that
1022 fLocalURI = devidWithUserHash() ? 'x' : 'X'; // start with X to document this special case (lowercase = forced by remoteFlag)
1023 for (int n=0; n<16; n++) {
1024 AppendHexByte(fLocalURI,digest[n]);
1028 // get my own name (if any)
1029 getPlatformString(pfs_device_name,fLocalName);
1030 // override some of these if not set by loadRemoteParams()
1031 if (fSyncMLVersion==syncml_vers_unknown)
1032 fSyncMLVersion=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerVersion;
1033 if (fRemoteRequestedAuth==auth_none)
1034 fRemoteRequestedAuth=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuth;
1035 if (fRemoteRequestedAuthEnc==fmt_chr)
1036 fRemoteRequestedAuthEnc=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuthEnc;
1037 if (fRemoteNonce.empty())
1038 fRemoteNonce=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedNonce;
1040 // we are not yet authenticated for the entire session
1042 // now ready for init
1043 fOutgoingState=psta_init; // %%%% could also set psta_initsync for combined init/sync
1044 fIncomingState=psta_idle; // remains idle until first answer SyncHdr with OK status is received
1045 fInProgress=true; // assume in progress
1046 // set session ID string
1047 StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)fClientSessionNo);
1048 // now we have a session id, can now display debug stuff
1051 StringObjTimestamp(t,getSystemNowAs(TCTX_SYSTEM));
1052 PDEBUGPRINTFX(DBG_HOT,("\n[%s] =================> Starting new client session",t.c_str()));
1054 // - make sure we are logged into the local database (if needed)
1055 status=LocalLogin();
1056 if (status!=LOCERR_OK) return status;
1057 // create header for first message no noResp
1061 // check for proper end of session (caused by MessageEnded analysis)
1063 // give an opportunity to let make outgoing message end and flush xml end message
1064 FinishMessage(true, false);
1065 // end sync in all datastores (save anchors etc.)
1066 PDEBUGPRINTFX(DBG_PROTO,("Successful end of session -> calling engFinishDataStoreSync() for datastores now"));
1067 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
1068 (*pos)->engFinishDataStoreSync(); // successful end
1069 PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
1070 return LOCERR_OK; // done & ok
1073 // check expired case
1074 #ifdef APP_CAN_EXPIRE
1075 if (getClientBase()->fAppExpiryStatus!=LOCERR_OK) {
1076 PDEBUGPRINTFX(DBG_ERROR,("Evaluation Version expired - Please contact Synthesis AG for release version"));
1077 return getClientBase()->fAppExpiryStatus; // payment required, done & error
1080 if (fOutgoingState==psta_init || fOutgoingState==psta_initsync) {
1081 // - if suspended in init, nothing substantial has happened already, so just exit
1082 if (isSuspending() && fOutgoingState==psta_init) {
1083 AbortSession(fAbortReasonStatus,true);
1084 return getAbortReasonStatus();
1086 // - prepare Alert(s) for databases to sync
1087 bool anyfirstsyncs=false;
1088 bool anyslowsyncs=false;
1089 TLocalEngineDS *localDS;
1090 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1091 // prepare alert (Note: datastore may be run by a superdatastore)
1093 status=localDS->engPrepareClientSyncAlert();
1094 if (status!=LOCERR_OK) {
1095 // local database error
1096 return localError(status); // not found
1098 if (localDS->fFirstTimeSync) anyfirstsyncs=true;
1099 if (localDS->fSlowSync) anyslowsyncs=true;
1101 // send devinf in Put command right away with init message if either...
1102 // - mustSendDevInf() returns true signalling an external condition that suggests sending devInf (like changed config)
1103 // - any datastore is doing first time sync
1104 // - fPutDevInfAtSlowSync is true and any datastore is doing slow sync
1108 (anyslowsyncs && static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync)
1110 TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
1111 issueRootPtr(putcmdP);
1113 // try to load devinf from cache (only if we don't know it already)
1114 if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
1115 SmlDevInfDevInfPtr_t devinfP;
1116 if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
1117 // we have cached devinf, analyze it now
1118 analyzeRemoteDevInf(devinfP);
1121 // GET the server's info if server didn't send it and we haven't cached at least the datastores
1122 if (!fRemoteDataStoresKnown) {
1123 // if we know datastores here, but not types, this means that remote does not have
1124 // CTCap, so it makes no sense to issue a GET again.
1125 #ifndef NO_DEVINF_GET
1126 PDEBUGPRINTFX(DBG_REMOTEINFO,("Nothing known about server, request DevInf using GET command"));
1127 TGetCommand *getcommandP = new TGetCommand(this);
1128 getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
1129 string devinftype=SYNCML_DEVINF_META_TYPE;
1130 addEncoding(devinftype);
1131 getcommandP->setMeta(newMetaType(devinftype.c_str()));
1132 ISSUE_COMMAND_ROOT(this,getcommandP);
1135 // - create Alert(s) for databases to sync
1136 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1137 // create alert for non-subdatastores
1139 if (!localDS->isSubDatastore()) {
1140 TAlertCommand *alertcmdP;
1141 status = localDS->engGenerateClientSyncAlert(alertcmdP);
1143 // local database error
1144 return status; // not found
1146 ///%%%% unneeded (probably got here by copy&paste accidentally): if (localDS->fFirstTimeSync) anyfirstsyncs=true;
1148 issueRootPtr(alertcmdP);
1151 // append sync phase if we have combined init/sync
1152 if (fOutgoingState==psta_initsync) fOutgoingState=psta_sync;
1154 // process sync/syncop/map generating phases after init
1155 if (!isSuspending()) {
1156 // normal, session continues
1157 if (fOutgoingState==psta_sync) {
1158 // hold back sync until server has finished first package (init or initsync)
1159 if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1160 // start sync for alerted datastores
1161 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1162 // Note: some datastores might be aborted due to unsuccessful alert.
1163 if ((*pos)->isActive()) {
1164 // prepare engine for sync (%%% new routine in 3.2.0.3, summarizing engInitForSyncOps() and
1165 // switching to dssta_dataaccessstarted, i.e. loading sync set), but do in only once
1166 if (!((*pos)->testState(dssta_syncsetready))) {
1168 status = (*pos)->engInitForClientSync();
1169 if (status!=LOCERR_OK ) {
1171 if (status!=LOCERR_DATASTORE_ABORT) {
1172 AbortSession(status,true);
1173 return getAbortReasonStatus();
1177 // start or continue (which is largely nop, as continuing works via unfinished sync command)
1178 // generating sync items
1179 (*pos)->engClientStartOfSyncMessage();
1184 else if (fOutgoingState==psta_map) {
1185 // hold back map until server has started sync at least (incominstate >=psta_sync)
1186 // NOTE: This is according to the specs, which says that client can begin
1187 // with Map/update status package BEFORE sync package from server is
1188 // completely received.
1189 // NOTE: Starfish server expects this and generates a few 222 alerts
1190 // if we wait here, but then goes to map as well
1191 // (so (fIncomingState==psta_map)-version works as well here!
1192 // %%%% other version: wait until server has started map phase as well
1193 // %%%% if (fIncomingState==psta_map) {
1194 if (fIncomingState>=psta_sync) {
1195 // start map for synced datastores
1196 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1197 // Note: some datastores might be aborted due to unsuccessful alert.
1198 if ((*pos)->isActive()) {
1199 // now call datastore to generate map command if not already done
1200 (*pos)->engClientStartOfMapMessage(fIncomingState<psta_map);
1205 else if (fOutgoingState==psta_supplement) {
1206 // we are waiting for the server to complete a pending phase altough we are already done
1207 // with everything we want to send.
1208 // -> just generate a Alert 222 and wait for server to complete
1209 PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Client finished so far, but needs to wait in supplement outgoing state until server finishes phase"));
1211 else if (fOutgoingState!=psta_init) {
1212 // NOTE: can be psta_init because "if" begins again after psta_init checking
1213 // to allow psta_init appending psta_sync for combined init/sync
1215 return 9999; // %%%%%
1217 } // if not suspended
1219 // security only: exit here if session got aborted in between
1221 return getAbortReasonStatus(); // done & error
1223 return 9999; // that's fine with us
1224 // now, we know that we will (most probably) send a message, so default for aDone is false from now on
1228 // check for suspend
1229 if (isSuspending()) {
1230 // make sure we send a Suspend Alert
1231 TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)224);
1232 // - we just put local and remote URIs here
1233 SmlItemPtr_t itemP = newItem();
1234 itemP->target = newLocation(fRemoteURI.c_str());
1235 itemP->source = newLocation(fLocalURI.c_str());
1236 alertCmdP->addItem(itemP);
1237 ISSUE_COMMAND_ROOT(this,alertCmdP);
1238 // outgoing message is final, regardless of any session state
1240 MarkSuspendAlertSent(true);
1243 // Determine if package can be final and if we need an 222 Alert
1244 // NOTE: if any commands were interruped or not sent due to outgoing message
1245 // size limits, FinishMessage() will prevent final anyway, so no
1246 // separate checking for enOfSync or endOfMap is needed.
1247 // - can finalize message when server has at least started answering current package
1248 // OR if this is the first message (probably repeatedly) sent
1249 outgoingfinal = fIncomingState >= fOutgoingState || fIncomingState==psta_idle;
1250 if (outgoingfinal) {
1251 // allow early success here in case of nothing to respond, and nothing pending
1252 // StarFish server does need this...
1253 if (!fNeedToAnswer) {
1254 if (hasPendingCommands()) {
1255 // we have pending commands, cannot be final message
1256 outgoingfinal=false;
1259 // no pending commands -> we're done now
1260 PDEBUGPRINTFX(DBG_PROTO,("Early end of session (nothing to send to server any more) -> calling engFinishDataStoreSync() for datastores now"));
1261 // - end sync in all datastores (save anchors etc.)
1262 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
1263 (*pos)->engFinishDataStoreSync(); // successful end
1264 PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
1272 /* A dummy alert indicates this is a message with only alert222 request*/
1273 bool dummyAlert = false;
1274 if (!outgoingfinal) {
1275 // - send Alert 222 if we need to continue package but have nothing to send
1276 // (or ALWAYS_CONTINUE222 defined)
1277 #ifndef ALWAYS_CONTINUE222
1281 /* End-less loop detection
1282 * Some servers will never end and triggers client sends
1283 * ALERT222 forever. Detect this scenario and abort the session if
1285 * It is still valid for the server to use ALERT222 to "keep-alive" the
1287 * Therefore the loop detection criteria is:
1288 * - Nothing to send except the 222 Alert (!fNeedToAnswer)
1289 * - 5 adjacent 222 alerts within 20 seconds
1290 * - no status for an actual sync op command received (fOutgoingAlert222Count will be reset by those)
1291 * because a server sending pending status in small chunks could also trigger the detector otherwise
1293 if (!fNeedToAnswer) {
1295 if (fOutgoingAlert222Count++ == 0) {
1296 // start of 222 loop detection time
1297 fLastOutgoingAlert222 = getSystemNowAs(TCTX_UTC);
1298 } else if (fOutgoingAlert222Count > 5) {
1299 lineartime_t curTime = getSystemNowAs(TCTX_UTC);
1300 if (curTime - fLastOutgoingAlert222 < 20*secondToLinearTimeFactor) {
1301 PDEBUGPRINTFX(DBG_ERROR,(
1302 "Warning: More than 5 consecutive Alert 222 within 20 seconds- "
1303 "looks like endless loop, abort session"
1305 AbortSession(400, false);
1306 return getAbortReasonStatus();
1308 fOutgoingAlert222Count = 0;
1312 // not final, and nothing to answer otherwise: create alert-Command to request more info
1313 TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
1314 // %%% not clear from spec what has to be in item for 222 alert code
1315 // but there MUST be an Item for the Alert command according to SyncML TK
1316 // - we just put local and remote URIs here
1317 SmlItemPtr_t itemP = newItem();
1318 itemP->target = newLocation(fRemoteURI.c_str());
1319 itemP->source = newLocation(fLocalURI.c_str());
1320 alertCmdP->addItem(itemP);
1321 ISSUE_COMMAND_ROOT(this,alertCmdP);
1324 // We send a response with no dummy alert, so reset the alert detector
1326 fOutgoingAlert222Count = 0;
1329 // send custom end-of session puts
1330 if (!isSuspending() && outgoingfinal && fOutgoingState==psta_map) {
1331 // End of outgoing map package; let custom PUTs which may transmit some session statistics etc. happen now
1332 issueCustomEndPut();
1334 // message complete, now finish it
1336 outgoingfinal, // allowed if possible
1337 false // final not prevented
1339 // Note, now fNewOutgoingPackage is set (by FinishMessage())
1340 // if next message will be responded to with a new package
1344 if (PDEBUGMASK & DBG_SESSION) {
1345 PDEBUGPRINTFX(DBG_SESSION,(
1346 "---> NextMessage, outgoing state='%s', incoming state='%s'",
1347 PackageStateNames[fOutgoingState],
1348 PackageStateNames[fIncomingState]
1350 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1351 // Show state of local datastores
1352 PDEBUGPRINTFX(DBG_SESSION,(
1353 "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
1355 (*pos)->isAborted() ? "ABORTED - " : "",
1356 (*pos)->getDSStateName(),
1357 (*pos)->isResuming() ? "RESUMED " : "",
1358 (*pos)->fSlowSync ? "SLOW" : "normal",
1359 SyncModeDescriptions[(*pos)->fSyncMode],
1360 (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1364 PDEBUGENDBLOCK("SyncML_Outgoing");
1365 if (getLastIncomingMsgID()>0) {
1366 // we have already received an incoming message, so we have started an "SyncML_Incoming" blocks sometime
1367 PDEBUGENDBLOCK("SyncML_Incoming"); // terminate debug block of previous incoming message as well
1371 return LOCERR_OK; // ok
1372 } // TSyncAgent::NextMessage
1375 // called after successful decoding of an incoming message
1376 bool TSyncAgent::ClientMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
1378 // message not authorized by default
1379 fMessageAuthorized=false;
1381 // Check information from SyncHdr
1384 (!(fSynchdrSessionID==smlPCDataToCharP(aContentP->sessionID))) ||
1385 (!(fLocalURI==smlSrcTargLocURIToCharP(aContentP->target)))
1388 PDEBUGPRINTFX(DBG_ERROR,(
1389 "Bad SyncHeader from Server. Syntax %s, SessionID (rcvd/correct) = '%s' / '%s', LocalURI (rcvd/correct) = '%s' / '%s'",
1390 aBad ? "ok" : "BAD",
1391 smlPCDataToCharP(aContentP->sessionID),
1392 fSynchdrSessionID.c_str(),
1393 smlSrcTargLocURIToCharP(aContentP->target),
1396 aStatusCommand.setStatusCode(400); // bad response/request
1397 AbortSession(400,true);
1400 // check for suspend: if we are suspended at this point, this means that we have sent the Suspend Alert already
1401 // in the previous message (due to user suspend request), so we can now terminate the session
1402 if (isSuspending() && isSuspendAlertSent()) {
1403 AbortSession(514,true,LOCERR_USERSUSPEND);
1406 // - RespURI (remote URI to respond to)
1407 if (aContentP->respURI) {
1408 fRespondURI=smlPCDataToCharP(aContentP->respURI);
1409 DEBUGPRINTFX(DBG_PROTO,("RespURI set to = '%s'",fRespondURI.c_str()));
1411 // authorization check
1412 // Note: next message will be started not before status for last one
1413 // has been processed. Commands issued before will automatically
1414 // be queued by issuePtr()
1416 fSessionAuthorized=true;
1417 fMessageAuthorized=true;
1418 // returns false on BAD header (but true on wrong/bad/missing cred)
1420 } // TSyncAgent::ClientMessageStarted
1423 // determines new package states and sets fInProgress
1424 void TSyncAgent::ClientMessageEnded(bool aIncomingFinal)
1426 TLocalDataStorePContainer::iterator pos;
1428 // show status before processing
1429 PDEBUGPRINTFX(DBG_SESSION,(
1430 "MessageEnded starts : old outgoing state='%s', old incoming state='%s', %sNeedToAnswer",
1431 PackageStateNames[fOutgoingState],
1432 PackageStateNames[fIncomingState],
1433 fNeedToAnswer ? "" : "NO "
1435 bool allFromClientOnly=false;
1436 // process exceptions
1438 PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
1439 fOutgoingState=psta_idle;
1440 fIncomingState=psta_idle;
1443 else if (!fMessageAuthorized) {
1444 // not authorized messages will just be ignored, so
1445 // nothing changes in states
1446 // %%% this will probably not really work, as we would need to repeat the last
1447 // message in this (unlikely) case that fMessageAuthorized is not set for
1448 // a non-first message (first message case is handled in handleHeaderStatus)
1449 DEBUGPRINTFX(DBG_ERROR,("***** received Message not authorized, ignore and DONT end package"));
1453 fInProgress=true; // assume we need to continue
1454 // Note: the map phase will not take place, if all datastores are in
1455 // send-to-server-only mode and we are not in non-conformant old
1456 // synthesis-compatible fCompleteFromClientOnly mode.
1457 if (!fCompleteFromClientOnly) {
1458 // let all local datastores know that message has ended
1459 allFromClientOnly=true;
1460 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1462 if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
1463 allFromClientOnly=false;
1468 // new outgoing state is determined by the incomingState of this message
1469 // (which is the answer to the <final/> message of the previous outgoing package)
1470 if (fNewOutgoingPackage && fIncomingState!=psta_idle) {
1471 // last message sent was an end-of-package, so next will be a new package
1472 if (fIncomingState==psta_init) {
1473 // server has responded (or is still responding) to our finished init,
1474 // so client enters sync state now (but holds back sync until server
1475 // has finished init)
1476 fOutgoingState=psta_sync;
1478 else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1479 // server has started (or already finished) sending statuses for our
1480 // <sync> or its own <sync>
1481 // client can enter map state (but holds back maps until server
1482 // has finished sync/initsync). In case of allFromClientOnly, we skip the map phase
1483 // but only if there is no need to answer.
1484 // Otherwise, this is most probably an old (pre 2.9.8.2) Synthesis server that has
1485 // sent an empty <sync> (and the status for it has set fNeedToAnswer), so we still
1487 if (allFromClientOnly && !fNeedToAnswer) {
1488 fOutgoingState=psta_supplement; // all datastores are from-client-only, skip map phase
1489 PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode, and no need to answer: skip map phase"));
1492 fOutgoingState=psta_map; // Some datastores do from-server-only or twoway, so we need a map phase
1493 allFromClientOnly=false; // do not skip map phase
1497 // map is finished as well, we might need extra packages just to
1498 // finish getting results for map commands
1499 fOutgoingState=psta_supplement;
1502 // New incoming state is simply derived from the incoming state of
1504 if (aIncomingFinal && fIncomingState!=psta_idle) {
1505 if (fIncomingState==psta_init) {
1507 fIncomingState=psta_sync;
1509 else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1511 if (allFromClientOnly) {
1512 // no need to answer and allFromClientOnly -> this is the end of the session
1513 fIncomingState=psta_supplement;
1514 fInProgress=false; // normally, at end of map answer, we are done
1517 fIncomingState=psta_map;
1521 // end of a map phase - end of session (if no fNeedToAnswer)
1522 fIncomingState=psta_supplement;
1523 // this only ALLOWS ending the session, but it will continue as long
1524 // as more than OK for SyncHdr (fNeedToAnswer) must be sent
1525 fInProgress=false; // normally, at end of map answer, we are done
1528 // continue anyway as long as we need to answer
1529 if (fNeedToAnswer) fInProgress=true;
1532 PDEBUGPRINTFX(DBG_HOT,(
1533 "MessageEnded finishes : new outgoing state='%s', new incoming state='%s', %sNeedToAnswer",
1534 PackageStateNames[fOutgoingState],
1535 PackageStateNames[fIncomingState],
1536 fNeedToAnswer ? "" : "NO "
1538 // let all local datastores know that message has ended
1539 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1541 (*pos)->engEndOfMessage();
1542 // Show state of local datastores
1543 PDEBUGPRINTFX(DBG_HOT,(
1544 "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
1546 (*pos)->isAborted() ? "ABORTED - " : "",
1547 (*pos)->getDSStateName(),
1548 (*pos)->isResuming() ? "RESUMED " : "",
1549 (*pos)->isSlowSync() ? "SLOW" : "normal",
1550 SyncModeDescriptions[(*pos)->getSyncMode()],
1551 (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1554 // thread might end here, so stop profiling
1556 } // TSyncAgent::ClientMessageEnded
1559 // get credentials/username to authenticate with remote party, NULL if none
1560 SmlCredPtr_t TSyncAgent::newCredentialsForRemote(void)
1563 // generate cretentials from username/password
1564 PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA,("Authenticating with server as user '%s'", fServerUser.c_str()));
1565 PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA+DBG_EXOTIC,("- using nonce '%s'", fRemoteNonce.c_str()));
1566 // NOTE: can be NULL when fServerRequestedAuth is auth_none
1567 return newCredentials(
1568 fServerUser.c_str(),
1569 fServerPassword.c_str()
1573 // already authorized, no auth needed
1576 } // TSyncAgent::newCredentialsForRemote
1580 TSyncClientBase *TSyncAgent::getClientBase(void)
1582 return static_cast<TSyncClientBase *>(getSyncAppBase());
1583 } // TSyncAgent::getClientBase
1586 // retry older protocol, returns false if no older protocol to try
1587 bool TSyncAgent::retryOlderProtocol(bool aSameVersionRetry, bool aOldMessageInBuffer)
1589 if (fIncomingState==psta_idle) {
1590 // if we have not started a session yet and not using oldest protocol already,
1591 // we want to retry with next older SyncML version
1592 if (aSameVersionRetry) {
1593 // just retry same version
1594 PDEBUGPRINTFX(DBG_PROTO,("Retrying session start with %s",SyncMLVerProtoNames[fSyncMLVersion]));
1596 else if (fSyncMLVersion>getSessionConfig()->fMinSyncMLVersionSupported) {
1598 fSyncMLVersion=(TSyncMLVersions)(((uInt16)fSyncMLVersion)-1);
1599 PDEBUGPRINTFX(DBG_PROTO,("Server does not support our SyncML version, trying with %s",SyncMLVerProtoNames[fSyncMLVersion]));
1606 retryClientSessionStart(aOldMessageInBuffer);
1609 // session already started or no older protocol to try
1611 } // TSyncAgent::retryOlderProtocol
1614 // prepares client session such that it will do a retry to start a session
1615 // (but keeping already received auth/nonce/syncML-Version state)
1616 void TSyncAgent::retryClientSessionStart(bool aOldMessageInBuffer)
1618 TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
1621 PDEBUGPRINTFX(DBG_HOT,("=================> Retrying Client Session Start"));
1622 bool newSessionForAuthRetry = configP->fNewSessionForAuthRetry;
1623 bool noRespURIForAuthRetry = configP->fNoRespURIForAuthRetry;
1624 // check if we should use modified behaviour (smart retries)
1625 if (configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) {
1626 if (newSessionForAuthRetry) {
1627 // if we had new session for retry, switch to in-session retry now
1628 newSessionForAuthRetry = false;
1629 noRespURIForAuthRetry = false;
1632 // if we had in-session retry, try new session retry now
1633 newSessionForAuthRetry = true;
1634 noRespURIForAuthRetry = true;
1636 PDEBUGPRINTFX(DBG_PROTO,("Smart retry with modified behaviour: newSessionForAuthRetry=%d, noRespURIForAuthRetry=%d",newSessionForAuthRetry,noRespURIForAuthRetry));
1639 if (newSessionForAuthRetry) {
1641 // - must apparently be disabled for SCTS 3.1.2 and possibly Mightyphone
1642 // - must be enabled e.g for for Magically Server
1643 // Create new session ID
1644 StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)++fClientSessionNo);
1645 // restart message counting at 1
1648 // we must terminate the block here when we reset fIncomingMsgID, as NextMessage
1649 // only closes the incoming block when fIncomingMsgID>0
1650 PDEBUGENDBLOCK("SyncML_Incoming");
1652 if (noRespURIForAuthRetry) {
1654 // - must apparently be switched on for Starfish.
1655 // - must apparently be switched off for SCTS 3.1.2.
1656 // make sure we send next msg to the original URL
1657 fRespondURI=fRemoteURI;
1659 // - make sure status for SyncHdr will not be generated!
1660 forgetHeaderWaitCommands();
1661 // check if we have already started next outgoing message
1662 if (!fOutgoingStarted) {
1663 if (aOldMessageInBuffer) {
1664 // make sure we start with a fresh output buffer
1665 // Note: This usually only occur when we are not currently parsing
1666 // part of the buffer. If we are parsing, the remaining incoming
1667 // message gets cleared as well.
1668 getClientBase()->clrUnreadSmlBufferdata();
1670 // start a new message
1674 if (aOldMessageInBuffer) {
1675 PDEBUGPRINTFX(DBG_ERROR,("Warning - restarting session with old message in output buffer"));
1678 // - make sure subsequent commands (most probably statuses for Alerts)
1679 // don't get processed
1680 AbortCommandProcessing(0); // silently discard all further commands
1681 // - make sure possible processing errors do not abort the session
1682 fIgnoreMsgErrs = true;
1683 } // TSyncAgent::retryClientSessionStart
1686 #endif // SYSYNC_CLIENT
1688 #ifdef SYSYNC_SERVER
1690 // undefine these only for tests. Introduced to find problem with T68i
1691 #define RESPURI_ONLY_WHEN_NEEDED
1693 // create a RespURI string. If none needed, return NULL
1694 SmlPcdataPtr_t TSyncAgent::newResponseURIForRemote(void)
1696 // do it in a transport-independent way, therefore let dispatcher do it
1697 string respURI; // empty string
1699 getSyncAppBase()->generateRespURI(
1700 respURI, // remains unaffected if no RespURI could be calculated
1701 fInitialLocalURI.c_str(), // initial URI used by remote to send first message
1702 fLocalSessionID.c_str() // server generated unique session ID
1704 // Omit RespURI if local URI as seen by client is identical
1705 if (getServerConfig()->fRespURIOnlyWhenDifferent) {
1706 // create RespURI only if different from original URI
1707 if (respURI==fLocalURI) {
1709 DEBUGPRINTFX(DBG_SESSION,(
1710 "Generated RespURI and sourceLocURI are equal (%s)-> RespURI omitted",
1716 // Note: returns NULL if respURI is empty string
1717 return newPCDataOptString(respURI.c_str());
1718 } // newResponseURIForRemote
1721 // called after successful decoding of an incoming message
1722 bool TSyncAgent::ServerMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
1724 // message not authorized by default
1725 fMessageAuthorized=false;
1727 // Get information from SyncHdr which is needed for answers
1728 // - session ID to be used for responses
1729 fSynchdrSessionID=smlPCDataToCharP(aContentP->sessionID);
1730 // - local URI (as seen by remote client)
1731 fLocalURI=smlSrcTargLocURIToCharP(aContentP->target);
1732 fLocalName=smlSrcTargLocNameToCharP(aContentP->target);
1733 // - also remember URI to which first message was sent
1734 // %%% note: incoming ID is not a criteria, because it might be >1 due to
1735 // client retrying something which it thinks is for the same session
1736 //if (fIncomingMsgID==1) {
1737 if (fOutgoingMsgID==0) {
1738 // this is the first message, remember first URI used to contact server
1739 // (or set preconfigured string from <externalurl>)
1740 if (getServerConfig()->fExternalURL.empty())
1741 fInitialLocalURI=fLocalURI; // use what client sends to us
1743 fInitialLocalURI=getServerConfig()->fExternalURL; // use preconfigured URL
1744 // Many clients, including SCTS send the second login attempt with a MsgID>1,
1745 // and depending on how they handle RespURI, they might get a new session for that
1746 // -> so, just handle the case that a new session does not start with MsgID=1
1747 if (fIncomingMsgID>1) {
1748 PDEBUGPRINTFX(DBG_ERROR,(
1749 "New session gets first message with MsgID=%ld (should be 1). Might be due to retries, adjusting OutgoingID as well",
1750 (long)fIncomingMsgID
1752 fOutgoingMsgID=fIncomingMsgID-1; // to make it match what client expects
1756 fRemoteURI=smlSrcTargLocURIToCharP(aContentP->source);
1757 fRemoteName=smlSrcTargLocNameToCharP(aContentP->source);
1758 // - RespURI (remote URI to respond to, if different from source)
1759 fRespondURI.erase();
1760 if (aContentP->respURI) {
1761 fRespondURI=smlPCDataToCharP(aContentP->respURI);
1762 DEBUGPRINTFX(DBG_PROTO,("RespURI specified = '%s'",fRespondURI.c_str()));
1764 if (fRespondURI==fRemoteURI) fRespondURI.erase(); // if specified but equal to remote: act as if not specified
1765 // More checking if header was ok
1767 // bad header, only do what is needed to get a status back to client
1768 fSessionAuthorized=false;
1769 fIncomingState=psta_init;
1770 fOutgoingState=psta_init;
1771 fNewOutgoingPackage=true;
1772 // issue header to make sure status can be sent back to client
1774 issueHeader(false); // issue header, do not prevent responses
1777 // check busy (or expired) case
1779 #ifdef APP_CAN_EXPIRE
1780 if (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK) {
1781 aStatusCommand.setStatusCode(511); // server failure (expired)
1782 aStatusCommand.addItemString("License expired or invalid");
1783 PDEBUGPRINTFX(DBG_ERROR,("License expired or invalid - Please contact Synthesis AG to obtain license"));
1788 aStatusCommand.setStatusCode(101); // busy
1790 issueHeader(false); // issue header, do not prevent responses
1791 AbortSession(0,true); // silently discard rest of commands
1792 return false; // header not ok
1794 // now check what state we are in
1795 if (fIncomingState==psta_idle) {
1797 // - session-wide authorization not yet there
1798 fSessionAuthorized=false;
1800 // - session has started, we are processing first incoming
1801 // package and generating first outgoing package
1802 // (init, possibly changed to combined init/sync by <sync> in this package)
1803 fIncomingState=psta_init;
1804 fOutgoingState=psta_init;
1805 fNewOutgoingPackage=true;
1807 // authorization check
1808 if (fIncomingState>=psta_init) {
1809 // now check authorization
1810 if (!fSessionAuthorized) {
1811 // started, but not yet permanently authorized
1812 fMessageAuthorized=checkCredentials(
1813 smlSrcTargLocNameToCharP(aContentP->source), // user name in clear text according to SyncML 1.0.1
1814 aContentP->cred, // actual credentials
1817 // NOTE: aStatusCommand has now the appropriate status and chal (set by checkCredentials())
1818 // if credentials do not match, stop processing commands (but stay with the session)
1819 if (!fMessageAuthorized) {
1820 AbortCommandProcessing(aStatusCommand.getStatusCode());
1821 PDEBUGPRINTFX(DBG_PROTO,("Authorization failed with status %hd, stop command processing",aStatusCommand.getStatusCode()));
1823 // now determine if authorization is permanent or not
1824 if (fMessageAuthorized) {
1825 fAuthFailures=0; // reset count
1826 if (messageAuthRequired()) {
1827 // each message needs autorisation again (or no auth at all)
1828 // - 200 ok, next message needs authorization again (or again: none)
1829 fSessionAuthorized=false; // no permanent authorization
1830 aStatusCommand.setStatusCode(200);
1831 // - add challenge for next auth (different nonce)
1832 aStatusCommand.setChallenge(newSessionChallenge());
1833 PDEBUGPRINTFX(DBG_PROTO,("Authorization ok, but required again for subsequent messages: 200 + chal"));
1836 // entire session is authorized
1837 fSessionAuthorized=true; // permanent authorization
1838 // - 212 authentication accepted (or 200 if none is reqired at all)
1839 aStatusCommand.setStatusCode(requestedAuthType()==auth_none ? 200 : 212);
1840 // - add challenge for next auth (in next session, but as we support carry
1841 // forward via using sessionID, we need to send one here as well)
1842 aStatusCommand.setChallenge(newSessionChallenge());
1843 PDEBUGPRINTFX(DBG_PROTO,("Authorization accepted: 212"));
1846 } // authorisation check
1848 // already authorized from previous message
1849 PDEBUGPRINTFX(DBG_PROTO,("Authorization ok from previous request: 200"));
1850 fMessageAuthorized=true;
1852 // Start response message AFTER auth check, to allow issueHeader
1853 // to check auth state and customize the header accordingly (no
1854 // RespURI for failed auth for example)
1856 issueHeader(false); // issue header, do not prevent responses
1858 } // if started at least
1860 // return startmessage status
1863 if (PDEBUGMASK & DBG_SESSION) {
1864 PDEBUGPRINTFX(DBG_SESSION,(
1865 "---> MessageStarted, Message %sauthorized, incoming state='%s', outgoing state='%s'",
1866 fMessageAuthorized ? "" : "NOT ",
1867 PackageStateNames[fIncomingState],
1868 PackageStateNames[fOutgoingState]
1870 TLocalDataStorePContainer::iterator pos;
1871 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1872 // Show state of local datastores
1873 PDEBUGPRINTFX(DBG_SESSION,(
1874 "Local Datastore '%s': State=%s, %s%s sync, %s%s",
1876 (*pos)->getDSStateName(),
1877 (*pos)->isResuming() ? "RESUMED " : "",
1878 (*pos)->fSlowSync ? "SLOW" : "normal",
1879 SyncModeDescriptions[(*pos)->fSyncMode],
1880 (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1885 // final check for too many auth failures
1886 if (!fMessageAuthorized) {
1887 #ifdef NO_NONCE_OLD_BEAHVIOUR
1888 AbortSession(aStatusCommand.getStatusCode(),true); // local error
1889 // avoid special treatment of non-authorized message, we have aborted, this is enough
1890 fMessageAuthorized=true;
1892 // Unsuccessful auth, count this
1894 PDEBUGPRINTFX(DBG_ERROR,(
1895 "Authorization failed %hd. time, (any reason), sending status %hd",
1897 aStatusCommand.getStatusCode()
1899 // - abort session after too many auth failures
1900 if (fAuthFailures>=MAX_AUTH_ATTEMPTS) {
1901 PDEBUGPRINTFX(DBG_ERROR,("Too many (>=%hd) failures, aborting session",MAX_AUTH_ATTEMPTS));
1902 AbortSession(400,true);
1906 // returns false on BAD header (but true on wrong/bad/missing cred)
1908 } // TSyncAgent::ServerMessageStarted
1911 void TSyncAgent::ServerMessageEnded(bool aIncomingFinal)
1914 TPackageStates newoutgoingstate,newincomingstate;
1915 TLocalDataStorePContainer::iterator pos;
1916 bool allFromClientOnly=false;
1918 // Incoming message ends here - what is following are commands initiated by the server
1919 // not directly related to a incoming command.
1920 PDEBUGENDBLOCK("SyncML_Incoming");
1921 // assume that outgoing package is NOT finished, so outgoing state does not change
1922 newoutgoingstate=fOutgoingState;
1923 // new incoming state depends on whether this message is final or not
1924 if ((aIncomingFinal || (fIncomingState==psta_supplement)) && fMessageAuthorized) {
1925 // Note: in supplement state, incoming final is not relevant (may or may not be present, there is
1926 // no next phase anyway
1927 // find out if this is a shortened session (no map phase) due to
1928 // from-client-only in all datastores
1929 if (!fCompleteFromClientOnly) {
1930 allFromClientOnly=true;
1931 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1933 if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
1934 allFromClientOnly=false;
1939 // determine what package comes next
1940 switch (fIncomingState) {
1942 newincomingstate=psta_sync;
1945 case psta_initsync :
1946 // end of sync phase means end of session if all datastores are in from-client-only mode
1947 if (allFromClientOnly) {
1948 PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode: don't expect map phase from client"));
1949 newincomingstate=psta_supplement;
1952 newincomingstate=psta_map;
1956 case psta_supplement : // supplement state does not exit automatically
1957 // after map, possibly some supplement status/alert 222 messages are needed from client
1958 newincomingstate=psta_supplement;
1961 // by default, back to idle
1962 newincomingstate=psta_idle;
1967 // not final or not authorized: no change in state
1968 newincomingstate=fIncomingState;
1970 // show status before processing
1971 PDEBUGPRINTFX(DBG_SESSION,(
1972 "---> MessageEnded starts : old incoming state='%s', old outgoing state='%s', %sNeedToAnswer",
1973 PackageStateNames[fIncomingState],
1974 PackageStateNames[fOutgoingState],
1975 fNeedToAnswer ? "" : "NO "
1979 // actual aborting has already taken place
1980 PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
1981 newoutgoingstate=psta_idle;
1982 newincomingstate=psta_idle;
1985 else if (isSuspending()) {
1986 // only flagged for suspend - but datastores are not yet aborted, do it now
1987 AbortSession(514,true,getAbortReasonStatus());
1988 PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'suspended' -> MessageEnded ends package and session"));
1989 newoutgoingstate=psta_idle;
1990 newincomingstate=psta_idle;
1993 else if (!fMessageAuthorized) {
1994 // not authorized messages will just be ignored, no matter if final or not,
1995 // so outgoing will NEVER be final on non-authorized messages
1996 // %%% before 1.0.4.9, this was fInProgress=true
1997 // DEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore and DONT end package, session continues"));
1998 // fInProgress=true;
1999 PDEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore msg and terminate session"));
2003 // determine if session continues living or not
2004 // - if in other than idle state, session will continue
2006 (newincomingstate!=psta_idle) || // if not idle, we'll continue
2007 !fMessageAuthorized; // if not authorized, we'll continue as well (retrying auth)
2008 // Check if we need to send an Alert 222 to get more messages of this package
2009 if (!aIncomingFinal) {
2010 // not end of incoming package
2011 #ifndef ALWAYS_CONTINUE222
2015 #ifdef COMBINE_SYNCANDMAP
2016 // %%% make sure session gets to an end in case combined sync/map was used
2017 if (fMapSeen && fIncomingState==psta_map && fOutgoingState==psta_map) {
2018 DEBUGPRINTFX(DBG_HOT,("********** Incoming, non-final message in (combined)map state needs no answer -> force end of outgoing package"));
2019 newoutgoingstate=psta_idle;
2024 // detected 222-loop on init here: when we have nothing to answer in init
2025 // and nothing is alerted -> break session
2026 // %%% not sure if this is always ok
2027 if (fIncomingState<=psta_init) {
2028 PDEBUGPRINTFX(DBG_ERROR,("############## Looks like if we were looping in an init-repeat loop -> force final"));
2030 fOutgoingState=psta_idle;
2033 // not final, and nothing to answer otherwise: create alert-Command to request more info
2034 TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
2035 // %%% not clear from spec what has to be in item for 222 alert code
2036 // but there MUST be an Item for the Alert command according to SyncML TK
2037 // - we just put local and remote URIs here
2038 SmlItemPtr_t itemP = newItem();
2039 itemP->target = newLocation(fRemoteURI.c_str());
2040 itemP->source = newLocation(fLocalURI.c_str());
2041 alertCmdP->addItem(itemP);
2042 ISSUE_COMMAND_ROOT(this,alertCmdP);
2048 // end of package, finish processing package
2049 if (fIncomingState==psta_init) {
2050 // - try to load devinf from cache (only if we don't have both datastores and type info already)
2051 if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
2052 SmlDevInfDevInfPtr_t devinfP;
2053 TStatusCommand dummystatus(this);
2054 if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
2055 // we have cached devinf, analyze it now
2056 localstatus sta = analyzeRemoteDevInf(devinfP);
2057 PDEBUGPRINTFX(DBG_ERROR,("devInf from Cache could not be analyzed: error=%hd",sta));
2060 // - if no DevInf for remote datastores cached or received yet,
2061 // issue GET for it now
2062 if (!fRemoteDataStoresKnown) {
2063 // if we know datastores here, but not types, this means that remote does not have
2064 // CTCap, so it makes no sense to issue a GET again.
2065 #ifndef NO_DEVINF_GET
2066 // end of initialisation package, but datastores not known yet
2067 // (=no DevInf Put received) --> ask for devinf now
2068 PDEBUGPRINTFX(DBG_REMOTEINFO,("No DevInf received or cached, request DevInf using GET command"));
2069 TGetCommand *getcommandP = new TGetCommand(this);
2070 getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
2071 string devinftype=SYNCML_DEVINF_META_TYPE;
2072 addEncoding(devinftype);
2073 getcommandP->setMeta(newMetaType(devinftype.c_str()));
2074 ISSUE_COMMAND_ROOT(this,getcommandP);
2079 // make sure syncing local datastores get informed of end-of-<Sync>-message
2080 if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
2081 // end of an incoming message of the Sync Package
2082 // - let all local datastores know, this is now the time to generate
2083 // <sync> commands, if needed
2084 // Note: if there are SyncEnd commands delayed, this means that this is
2085 // not yet the time to start <sync> commands. Instead, when all
2086 // queued SyncEnd commands are executed later, engEndOfSyncFromRemote()
2087 // will be called with the endOfAllSyncCommands flag true instead
2089 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2090 (*pos)->engEndOfSyncFromRemote(aIncomingFinal && !delayedSyncEndsPending());
2093 // Detect outgoing package state transitions
2095 if (fOutgoingState==psta_init && newincomingstate>psta_init) {
2096 // new outgoing state is sync.
2097 // Note: In combined init&sync mode, sync command received in init state
2098 // will set outgoing state from init to init-sync while processing message,
2099 // so no transition needs to be detected here
2100 newoutgoingstate=psta_sync;
2104 (fOutgoingState==psta_sync || fOutgoingState==psta_initsync) && // outgoing is sync..
2105 (newincomingstate>=psta_initsync) && // ..and incoming has finished sync
2106 !allFromClientOnly // ..and this is not a session with all datastores doing from-client-only
2108 // outgoing message belongs to Sync package
2109 // - ask all local datastores if they are finished with sync command generation
2111 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2112 alldone = alldone && (*pos)->isSyncDone();
2115 // outgoing state changes to map (or supplement if all datastores are from-client-only
2116 PDEBUGPRINTFX(DBG_HOT,("All datastores are done with generating <Sync>"));
2117 newoutgoingstate=psta_map;
2118 #ifdef COMBINE_SYNCANDMAP
2119 // %%% it seems as if 9210 needs combined Sync/Map package and
2121 // prevent FINAL to be sent at end of message
2122 DEBUGPRINTFX(DBG_HOT,("********** Combining outgoing sync and map-response packages into one"));
2123 fOutgoingState=psta_map;
2128 // - map (or from-client-only sync) to idle
2130 (fOutgoingState==psta_map && newincomingstate==psta_supplement) ||
2131 (allFromClientOnly && (fOutgoingState==psta_sync || fOutgoingState==psta_initsync))
2133 // we are going back to idle now
2134 newoutgoingstate=psta_idle;
2135 // session ends if it doesn't need to continue for session-level reasons
2136 if (!sessionMustContinue()) {
2137 PDEBUGPRINTFX(DBG_HOT,("Session completed, now let datastores terminate all sync operations"));
2138 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2139 // finished with Map: end of sync
2140 (*pos)->engFinishDataStoreSync(); // successful
2144 // let custom PUTs which may transmit some session statistics etc. happen now
2145 issueCustomEndPut();
2148 // - if no need to answer (e.g. nothing to send back except OK status for SyncHdr),
2149 // session is over now (as well)
2150 if (!fNeedToAnswer) fInProgress=false;
2152 // Now finish outgoing message
2153 #ifdef DONT_FINAL_BAD_AUTH_ATTEMPTS
2154 // - PREVENT final flag after failed auth attempts
2156 fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
2157 !fMessageAuthorized || serverBusy() // busy or unauthorized prevent final flag at any rate
2160 // - DO set final flag after failed auth attempts
2162 !fMessageAuthorized || fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
2163 serverBusy() // busy prevents final flag at any rate
2167 // outgoing state HAS changed
2168 fOutgoingState=newoutgoingstate;
2170 // Now update incoming state
2171 fIncomingState=newincomingstate;
2173 PDEBUGPRINTFX(DBG_HOT,(
2174 "---> MessageEnded finishes : new incoming state='%s', new outgoing state='%s', %sNeedToAnswer",
2175 PackageStateNames[fIncomingState],
2176 PackageStateNames[fOutgoingState],
2177 fNeedToAnswer ? "" : "NO "
2179 // let all local datastores know that message has ended
2180 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2182 (*pos)->engEndOfMessage();
2183 // Show state of local datastores
2184 PDEBUGPRINTFX(DBG_HOT,(
2185 "Local Datastore '%s': State=%s, %s%s sync, %s%s",
2187 (*pos)->getDSStateName(),
2188 (*pos)->isResuming() ? "RESUMED " : "",
2189 (*pos)->fSlowSync ? "SLOW" : "normal",
2190 SyncModeDescriptions[(*pos)->fSyncMode],
2191 (*pos)->fServerAlerted ? ", Server-Alerted" : ""
2194 // End of outgoing message
2195 PDEBUGPRINTFX(DBG_HOT,(
2196 "=================> Finished generating outgoing message #%ld, request=%ld",
2197 (long)fOutgoingMsgID,
2198 (long)getSyncAppBase()->requestCount()
2200 PDEBUGENDBLOCK("SyncML_Outgoing");
2201 } // TSyncAgent::ServerMessageEnded
2204 void TSyncAgent::RequestEnded(bool &aHasData)
2206 // to make sure, finish any unfinished message
2207 FinishMessage(true); // final allowed, as this is an out-of-normal-order case anyway
2208 // if we need to answer, we have data
2209 // - SyncML specs 1.0.1 says that server must always respond, even if message
2210 // contains of a Status for the SyncHdr only
2212 // %%% first drafts of 1.0.1 said that SyncHdr Status only messages must not be sent...
2213 // aHasData=fNeedToAnswer; // %%%
2215 // now let all datastores know that request processing ends here (so they might
2216 // prepare for a thread switch)
2217 // terminate sync with all datastores
2218 TLocalDataStorePContainer::iterator pos;
2219 for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2220 // now let them know that request has ended
2221 (*pos)->engRequestEnded();
2223 } // TSyncAgent::RequestEnded
2226 // Called at end of Request, returns true if session must be deleted
2227 // returns flag if data to be returned. If response URI was specified
2228 // different, it is returned in aRespURI, otherwise aRespURI is empty.
2229 bool TSyncAgent::EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes)
2231 // count incoming data
2232 fIncomingBytes+=aReqBytes;
2233 // let client or server do what is needed
2234 if (fMessageRetried) {
2235 // Message processing cancelled
2236 CancelMessageProcessing();
2238 // - but count bytes
2239 fOutgoingBytes+=fBufferedAnswerSize;
2240 PDEBUGPRINTFX(DBG_HOT,(
2241 "========= Finished retried request with re-sending buffered answer (session %sin progress), incoming bytes=%ld, outgoing bytes=%ld",
2242 fInProgress ? "" : "NOT ",
2244 (long)fBufferedAnswerSize
2246 aHasData=false; // we do not have data in the sml instance (but we have/had some in the retry re-send buffer)
2250 RequestEnded(aHasData);
2252 fOutgoingBytes+=getOutgoingMessageSize();
2253 PDEBUGPRINTFX(DBG_HOT,(
2254 "========= Finished request (session %sin progress), processing time=%ld msec, incoming bytes=%ld, outgoing bytes=%ld",
2255 fInProgress ? "" : "NOT ",
2256 (long)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) * nanosecondsPerLinearTime / 1000000),
2258 (long)getOutgoingMessageSize()
2260 // return RespURI (is empty if none specified or equal to message source URI)
2261 aRespURI = fRespondURI;
2264 // terminate datastores here already in case we are not in progress any more
2265 // here. If any of the datastores are in progress at this point, this is a
2266 // protocol violation, and therefore we return a 400.
2267 // Note: resetting the session later will also call TerminateDatastores, but then
2268 // with a 408 (which is misleading when the session ends here due to protocol
2270 TerminateDatastores(400);
2272 //%%% moved to happen before end of SyncML_Outgoing
2273 //PDEBUGENDBLOCK("SyncML_Incoming");
2274 if (fRequestMinTime>0) {
2275 // make sure we spent enough time with this request, if not, artificially extend time
2276 // - get number of seconds already spent
2278 (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
2279 // - delay if needed
2280 if (t<fRequestMinTime) {
2281 PDEBUGPRINTFX(DBG_HOT,(
2282 "requestmintime is set to %ld seconds, we have spent only %ld seconds so far -> sleeping %ld seconds",
2283 (long)fRequestMinTime,
2285 (long)fRequestMinTime-t
2287 CONSOLEPRINTF((" ...delaying response by %ld seconds because requestmintime is set to %ld",fRequestMinTime,fRequestMinTime-t));
2288 sleepLineartime((lineartime_t)(fRequestMinTime-t)*secondToLinearTimeFactor);
2291 // thread might end here, so stop profiling
2294 // we are not the main thread any longer
2295 getDbgLogger()->DebugThreadOutputDone();
2297 // return true if session is not in progress any more
2298 return(!fInProgress);
2299 } // TSyncAgent::EndRequest
2302 // buffer answer in the session's buffer if transport allows it
2303 Ret_t TSyncAgent::bufferAnswer(MemPtr_t aAnswer, MemSize_t aAnswerSize)
2305 // get rid of previous buffered answer
2306 if (fBufferedAnswer)
2307 delete[] fBufferedAnswer;
2308 fBufferedAnswer=NULL;
2309 fBufferedAnswerSize=0;
2310 // save new answer (if not empty)
2311 if (aAnswer && aAnswerSize) {
2313 fBufferedAnswer = new unsigned char[aAnswerSize];
2315 if (!fBufferedAnswer) return SML_ERR_NOT_ENOUGH_SPACE;
2316 memcpy(fBufferedAnswer,aAnswer,aAnswerSize);
2318 fBufferedAnswerSize=aAnswerSize;
2321 } // TSyncAgent::bufferAnswer
2324 // get buffered answer from the session's buffer if there is any
2325 void TSyncAgent::getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize)
2327 aAnswer=fBufferedAnswer;
2328 aAnswerSize=fBufferedAnswerSize;
2329 PDEBUGPRINTFX(DBG_HOT,(
2330 "Buffered answer read from session: %ld bytes",
2333 } // TSyncAgent::getBufferedAnswer
2336 // returns remaining time for request processing [seconds]
2337 sInt32 TSyncAgent::RemainingRequestTime(void)
2340 // clients don't process requests, so there's no limit
2341 return 0x7FFFFFFF; // "infinite"
2344 // if no request timeout specified, use session timeout
2345 sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout;
2346 // calculate number of remaining seconds
2349 0x7FFFFFFF : // "infinite"
2350 t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
2352 } // TSyncAgent::RemainingRequestTime
2358 // process a Map command in context of server session
2359 bool TSyncAgent::processMapCommand(
2360 SmlMapPtr_t aMapCommandP, // the map command contents
2361 TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
2362 bool &aQueueForLater
2365 bool allok=false; // assume not ok
2368 // remember that this session has seen a map command already
2370 // Detecting a map command in supplement incomin state indicates a
2371 // client like funambol that send to many <final/> in pre-map phases
2372 // (such as in 222-Alert messages). So we reset the session state back
2373 // to incoming/outgoing map to correct this client bug
2374 if (fIncomingState==psta_supplement) {
2375 // back to map phase, as client apparently IS still in map phase, despite too many
2377 PDEBUGPRINTFX(DBG_ERROR,(
2378 "Warning: detected <Map> command after end of Map phase - buggy client sent too many <final/>. Re-entering map phase to compensate"
2380 fIncomingState=psta_map;
2381 fOutgoingState=psta_map;
2384 // - get relative URI of requested database
2385 const char *targetdburi = smlSrcTargLocURIToCharP(aMapCommandP->target);
2386 TLocalEngineDS *datastoreP = findLocalDataStoreByURI(targetdburi);
2388 // no such local datastore
2389 aStatusCommand.setStatusCode(404); // not found
2392 // local datastore found
2393 // - maps can be processed when we are at least ready for early (chached by client from previous session) maps
2394 if (datastoreP->testState(dssta_syncmodestable)) {
2395 // datastore is ready
2396 PDEBUGBLOCKFMT(("ProcessMap", "Processing items from Map command", "datastore=%s", targetdburi));
2397 allok=true; // assume all ok
2398 SmlMapItemListPtr_t nextnode = aMapCommandP->mapItemList;
2400 POINTERTEST(nextnode->mapItem,("MapItemList node w/o MapItem"));
2401 PDEBUGPRINTFX(DBG_HOT,(
2402 "Mapping remoteID='%s' to localID='%s'",
2403 smlSrcTargLocURIToCharP(nextnode->mapItem->source),
2404 smlSrcTargLocURIToCharP(nextnode->mapItem->target)
2406 sta = datastoreP->engProcessMap(
2407 #ifdef DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS
2408 smlSrcTargLocURIToCharP(nextnode->mapItem->source),
2410 relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->source)),
2412 relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->target))
2414 if (sta!=LOCERR_OK) {
2415 PDEBUGPRINTFX(DBG_ERROR,(" Mapping FAILED!"));
2416 aStatusCommand.setStatusCode(sta);
2421 nextnode=nextnode->next;
2422 } // while more mapitems
2423 // terminate Map command
2424 allok=datastoreP->MapFinishAsServer(allok,aStatusCommand);
2425 PDEBUGENDBLOCK("ProcessMap");
2428 // we must queue the command for later execution
2429 aQueueForLater=true;
2430 allok=true; // ok for now, we'll re-execute this later
2434 } // TSyncAgent::processMapCommand
2437 // get next nonce string top be sent to remote party for subsequent MD5 auth
2438 void TSyncAgent::getNextNonce(const char *aDeviceID, string &aNextNonce)
2441 if (getServerConfig()->fAutoNonce) {
2442 // generate nonce out of source ref and session ID
2443 // This scheme can provide nonce carrying forward between
2444 // sessions by initializing lastNonce with the srcRef/sessionid-1
2445 // assuming client to use nonce from last session.
2447 // use current day as nonce varying number
2448 sid = time(NULL) / 3600 / 24;
2449 generateNonce(fLastNonce,aDeviceID,sid);
2452 // get constant nonce (if empty, this is NO nonce)
2453 fLastNonce=getServerConfig()->fConstantNonce;
2456 DEBUGPRINTFX(DBG_PROTO,("getNextNonce: created nonce='%s'",fLastNonce.c_str()));
2457 aNextNonce=fLastNonce;
2458 } // TSyncAgent::getNextNonce
2461 // - get nonce string for specified deviceID
2462 void TSyncAgent::getAuthNonce(const char *aDeviceID, string &aAuthNonce)
2464 // if no device ID, use session default nonce
2466 TSyncSession::getAuthNonce(aDeviceID,fLastNonce);
2469 // Basic nonce mechanism needing no per-device storage:
2470 // - we have no stored last nonce, but we can re-create nonce used
2471 // for last session with this device by the used algorithm
2472 if (getServerConfig()->fAutoNonce) {
2473 if (fLastNonce.empty()) {
2474 // none available, produce new one
2476 // use current day as nonce varying number
2477 sid = time(NULL) / 3600 / 24;
2478 generateNonce(fLastNonce,aDeviceID,sid);
2482 // return constant nonce
2483 fLastNonce=getServerConfig()->fConstantNonce;
2486 DEBUGPRINTFX(DBG_PROTO,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str()));
2487 aAuthNonce=fLastNonce;
2488 } // TSyncAgent::getAuthNonce
2492 // info about server status
2493 bool TSyncAgent::serverBusy(void)
2495 // return flag (which might have been set by some connection
2496 // limit code in sessiondispatch).
2497 // When app is expired, all server sessions are busy anyway
2498 #ifdef APP_CAN_EXPIRE
2499 return fSessionIsBusy || (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK);
2501 return fSessionIsBusy;
2503 } // TSyncAgent::serverBusy
2507 TAgentConfig *TSyncAgent::getServerConfig(void)
2510 GET_CASTED_PTR(scP,TAgentConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TAgentConfig","ss1"));
2512 } // TSyncAgent::getServerConfig
2515 #endif // SYSYNC_SERVER
2518 // info about requested auth type
2519 TAuthTypes TSyncAgent::requestedAuthType(void)
2522 #ifdef SYSYNC_SERVER
2523 return getServerConfig()->fRequestedAuth;
2527 return auth_none; // client does not require auth
2529 } // TSyncAgent::requestedAuthType
2532 // check if auth type is allowed
2533 bool TSyncAgent::isAuthTypeAllowed(TAuthTypes aAuthType)
2536 #ifdef SYSYNC_SERVER
2537 return aAuthType>=getServerConfig()->fRequiredAuth;
2541 return true; // client accepts any auth
2543 } // TSyncAgent::isAuthTypeAllowed
2546 // called when incoming SyncHdr fails to execute
2547 bool TSyncAgent::syncHdrFailure(bool aTryAgain)
2550 // do not try to re-execute the header, just let message processing fail;
2551 // this will cause the client's main loop to try using an older protocol
2555 #ifdef SYSYNC_SERVER
2557 // not already retried executing
2558 // special case: header failed to execute, this means that session must be reset
2559 // - Reset session (aborts all DB transactions etc.)
2561 PDEBUGPRINTFX(DBG_ERROR,("Trying to recover SyncHdr failure: =========== Session restarted ====================="));
2562 // - now all session infos are gone except this command which is owned by
2563 // this function alone. Execute it again.
2567 // special special case: header failed to execute the second time
2568 DEBUGPRINTFX(DBG_ERROR,("Fatal internal problem, SyncHdr execution failed twice"));
2569 aTryAgain=false; // just to make sure
2570 SYSYNC_THROW(TSyncException("SyncHdr fatal execution problem"));
2575 } // TSyncAgent::syncHdrFailure
2578 // handle status received for SyncHdr, returns false if not handled
2579 bool TSyncAgent::handleHeaderStatus(TStatusCommand *aStatusCmdP)
2582 #ifdef SYSYNC_CLIENT
2583 TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
2586 SmlMetInfMetInfPtr_t chalmetaP=NULL;
2589 // first evaluate possible challenge in header status
2590 chalP = aStatusCmdP->getStatusElement()->chal;
2592 chalmetaP = smlPCDataToMetInfP(chalP->meta);
2596 if (!chalmetaP->type) AbortSession(401,true); // missing auth, but no type
2597 txt = smlPCDataToCharP(chalmetaP->type);
2598 PDEBUGPRINTFX(DBG_PROTO,("Remote requests auth type='%s'",txt));
2599 if (StrToEnum(authTypeSyncMLNames,numAuthTypes,ty,txt))
2600 fRemoteRequestedAuth=(TAuthTypes)ty;
2602 AbortSession(406,true); // unknown auth type, not supported
2603 goto donewithstatus;
2605 // - get auth format
2606 if (!smlPCDataToFormat(chalmetaP->format, fRemoteRequestedAuthEnc)) {
2607 AbortSession(406,true); // unknown auth format, not supported
2608 goto donewithstatus;
2611 if (chalmetaP->nextnonce) {
2614 uInt8 *nonce = b64::decode(smlPCDataToCharP(chalmetaP->nextnonce), 0, &l);
2615 fRemoteNonce.assign((char *)nonce,l);
2619 PDEBUGPRINTFX(DBG_PROTO,(
2620 "Next Cred will have type='%s' and format='%s' and use nonce='%s'",
2621 authTypeNames[fRemoteRequestedAuth],
2622 encodingFmtNames[fRemoteRequestedAuthEnc],
2623 fRemoteNonce.c_str()
2626 /* %%% do not save here already, we don't know if SyncML version is ok
2627 moved to those status code cases below that signal
2628 // let descendant possibly save auth params
2632 // now evaluate status code
2633 switch (aStatusCmdP->getStatusCode()) {
2636 AbortSession(101,false);
2638 case 212: // authentication accepted for entire session
2639 fNeedAuth=false; // no need for further auth
2640 PDEBUGPRINTFX(DBG_PROTO,("Remote accepted authentication for entire session"));
2641 case 200: // authentication accepted for this message
2642 // if this is the first authorized message we get an OK for the synchdr, this is
2643 // also the first incoming message that is really processed as init message
2644 if (fIncomingState==psta_idle && fMessageAuthorized) {
2645 // first incoming is expected to be same as first outgoing (init or initsync)
2646 fIncomingState=fOutgoingState;
2647 PDEBUGPRINTFX(DBG_PROTO,("Authenticated successfully with remote server"));
2650 PDEBUGPRINTFX(DBG_PROTO,("Authentication with server ok for this message"));
2652 // let descendant possibly save auth params
2655 case 501: // handle a "command not implemented" for the SyncHdr like 513 (indication that server does not like our header)
2656 case 400: // ..and 400 as well (sync4j case, as it seems)
2657 case 513: // bad protocol version
2658 case 505: // bad DTD version (NextHaus/DeskNow case)
2659 // try with next lower protocol
2660 PDEBUGENDBLOCK("processStatus"); // done processing status
2661 if (!retryOlderProtocol()) {
2662 // no older SyncML protocol we can try --> abort
2663 AbortSession(513,false); // server does not know any of our SyncML versions
2666 case 401: // bad authentication
2667 // Bad authorisation
2668 if (fAuthRetries==0)
2669 // if first attempt is rejected with "bad", we conclude that the
2670 // last attempt was carrying auth data and was not a attempt to get challenge
2671 // from server. Therefore we count this as two tries (one get chal, one really failing)
2674 fAuthRetries++; // just count attempt to auth
2675 /* %%% no longer required, is tested below at authfail:
2676 if (fAuthRetries>MAX_AUTH_RETRIES) {
2677 AbortSession(401,false); // abort session, too many retries
2681 // Treat no nonce like empty nonce to make sure that a server (like SySync old versions...)
2682 // that does not send a nonce at all does not get auth with some old, invalid nonce string included.
2683 if (chalmetaP && chalmetaP->nextnonce==NULL) fRemoteNonce.erase();
2684 // otherwise treat like 407
2686 case 407: // authentication required
2687 // new since 2.0.4.6: count this as well (normally this happens once when sending
2688 // no auth to the server to force it to send us auth chal first).
2691 PDEBUGPRINTFX(DBG_ERROR,("Authentication failed (status=%hd) with remote server",aStatusCmdP->getStatusCode()));
2692 // Auth fail after we have received a valid response for the init message indicates protocol messed up
2693 if (fIncomingState!=psta_idle) {
2694 AbortSession(400,true); // error in protocol handling from remote
2697 // Check if smart retries (with modified in-session vs out-of-session behaviour) are enabled
2698 if (!configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) {
2699 fAuthRetries = MAX_SMART_AUTH_RETRIES+1; // skip additional smart retries
2701 // Missing or bad authorisation, evaluate chal
2702 if (!chalmetaP || fAuthRetries>MAX_SMART_AUTH_RETRIES) {
2705 PDEBUGPRINTFX(DBG_ERROR,("Bad auth but no challenge in response status -> can't work - no retry"));
2708 AbortSession(aStatusCmdP->getStatusCode(),false); // retries exhausted or no retry possible (no chal) -> stop session
2711 // let descendant possibly save auth params
2713 // modify session for re-start
2714 PDEBUGENDBLOCK("processStatus"); // done processing status
2715 retryClientSessionStart(false); // no previously sent message in the buffer
2718 handled=false; // could not handle status
2721 // Anyway, reception of status for header enables generation of next message header
2722 // (plus already generated commands such as status for response header)
2723 if (!fMsgNoResp && !isAborted()) {
2724 // issue header now if not already issued above
2725 if (!fOutgoingStarted) {
2726 // interrupt status processing block here as issueHeader will do a start-of-message PDEBUGBLOCK
2727 PDEBUGENDBLOCK("processStatus");
2729 PDEBUGBLOCKDESC("processStatus","finishing processing incoming SyncHdr Status");
2732 // return handled status
2734 #endif // SYSYNC_SERVER
2738 return inherited::handleHeaderStatus(aStatusCmdP);
2740 } // TSyncAgent::handleHeaderStatus
2743 // - start sync group (called in client or server roles)
2744 bool TSyncAgent::processSyncStart(
2745 SmlSyncPtr_t aSyncP, // the Sync element
2746 TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
2747 bool &aQueueForLater // will be set if command must be queued for later (re-)execution
2751 #ifdef SYSYNC_CLIENT
2752 if (fIncomingState!=psta_sync && fIncomingState!=psta_initsync) {
2753 aStatusCommand.setStatusCode(403); // forbidden in this context
2754 PDEBUGPRINTFX(DBG_ERROR,("Sync command not allowed outside of sync phase (-> 403)"));
2755 AbortSession(400,true);
2758 // just find appropriate database, must be already initialized for sync!
2759 // determine local database to sync with (target)
2760 TLocalEngineDS *datastoreP = findLocalDataStoreByURI(smlSrcTargLocURIToCharP(aSyncP->target));
2762 // no such local datastore
2763 PDEBUGPRINTFX(DBG_ERROR,("Sync command for unknown DS locURI '%s' (-> 404)",smlSrcTargLocURIToCharP(aSyncP->target)));
2764 aStatusCommand.setStatusCode(404); // not found
2768 // save the pointer, will e.g. be used to route subsequent server commands
2769 fLocalSyncDatastoreP=datastoreP;
2770 // let local datastore know
2771 return fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
2774 #endif // SYSYNC_CLIENT
2777 #ifdef SYSYNC_SERVER
2778 // Init datastores for sync
2779 localstatus sta = initSync(
2780 smlSrcTargLocURIToCharP(aSyncP->target), // local datastore
2781 smlSrcTargLocURIToCharP(aSyncP->source) // remote datastore
2783 if (sta!=LOCERR_OK) {
2784 aStatusCommand.setStatusCode(sta);
2787 // let local datastore prepare for sync as server
2788 // - let local process sync command
2789 bool ok=fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
2790 // Note: ok means that the sync command is addressing existing datastores. However,
2791 // it does not mean that the actual processing is already executed; aQueueForLater
2793 // if ok and not queued: update package states
2795 if (fIncomingState==psta_init || fIncomingState==psta_initsync) {
2796 // detected sync command in init package -> this is combined init/sync
2798 if (fIncomingState==psta_init)
2799 DEBUGPRINTFX(DBG_HOT,("<Sync> started init package -> switching to combined init/sync"));
2801 // - set new incoming state
2802 fIncomingState=psta_initsync;
2803 // - also update outgoing state, if it is in init package
2804 if (fOutgoingState==psta_init)
2805 fOutgoingState=psta_initsync;
2807 else if (fCmdIncomingState!=psta_sync) {
2808 DEBUGPRINTFX(DBG_ERROR,(
2809 "<Sync> found in wrong incoming package state '%s' -> aborting session",
2810 PackageStateNames[fCmdIncomingState]
2812 aStatusCommand.setStatusCode(403); // forbidden
2813 fLocalSyncDatastoreP->engAbortDataStoreSync(403,true); // abort, local problem
2817 // - show sync start
2818 DEBUGPRINTFX(DBG_HOT,(
2819 "<Sync> started, cmd-incoming state='%s', incoming state='%s', outgoing state='%s'",
2820 PackageStateNames[fCmdIncomingState],
2821 PackageStateNames[fIncomingState],
2822 PackageStateNames[fOutgoingState]
2827 #endif // SYSYNC_SERVER
2829 } // TSyncAgent::processSyncStart
2834 #ifdef ENGINEINTERFACE_SUPPORT
2836 // Support for EngineModule common interface
2837 // =========================================
2840 /// @brief Get new session key to access details of this session
2841 appPointer TSyncAgent::newSessionKey(TEngineInterface *aEngineInterfaceP)
2843 return new TAgentParamsKey(aEngineInterfaceP,this);
2844 } // TSyncAgent::newSessionKey
2847 #ifdef ENGINE_LIBRARY
2849 TSyError TSyncAgent::SessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
2852 #ifdef SYSYNC_CLIENT
2853 return ClientSessionStep(aStepCmd,aInfoP);
2854 #endif // SYSYNC_CLIENT
2857 #ifdef SYSYNC_SERVER
2858 return ServerSessionStep(aStepCmd,aInfoP);
2859 #endif // SYSYNC_SERVER
2861 } // TSyncAgent::SessionStep
2863 #endif // ENGINE_LIBRARY
2866 #ifdef SYSYNC_SERVER
2868 // Server implementation
2869 // ---------------------
2871 #ifndef ENGINE_LIBRARY
2873 // dummy server engine support to allow AsKey from plugins
2875 #warning "using ENGINEINTERFACE_SUPPORT in old-style appbase-rooted environment. Should be converted to real engine usage later"
2877 // Engine factory function for non-Library case
2878 ENGINE_IF_CLASS *newServerEngine(void)
2880 // For real engine based targets, newServerEngine must create a target-specific derivate
2881 // of the server engine, which then has a suitable newSyncAppBase() method to create the
2882 // appBase. For old-style environment, a generic TServerEngineInterface is ok, as this
2883 // in turn calls the global newSyncAppBase() which then returns the appropriate
2884 // target specific appBase. Here we just return a dummy server engine base.
2885 return new TDummyServerEngineInterface;
2886 } // newServerEngine
2888 /// @brief returns a new application base.
2889 TSyncAppBase *TDummyServerEngineInterface::newSyncAppBase(void)
2891 // For not really engine based targets, the appbase factory function is
2892 // a global routine (for real engine targets, it is a true virtual of
2893 // the engineInterface, implemented in the target's leaf engineInterface derivate.
2894 // - for now, use the global appBase creator routine
2895 return sysync::newSyncAppBase(); // use global factory function
2896 } // TDummyServerEngineInterface::newSyncAppBase
2900 // Real server engine support
2902 /// @brief Executes next step of the session
2903 /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
2904 /// - tells caller to send or receive data or end the session etc.
2905 /// - instructs engine to abort or time out the session etc.
2906 /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
2907 /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
2908 TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
2910 uInt16 stepCmdIn = aStepCmd;
2911 localstatus sta = LOCERR_WRONGUSAGE;
2913 // init default response
2914 aStepCmd = STEPCMD_ERROR; // error
2916 aInfoP->eventtype=PEV_NOP;
2923 // if session is already aborted, no more steps are required
2925 fServerEngineState = ses_done; // we are done
2928 // handle pre-processed step command according to current engine state
2929 switch (fServerEngineState) {
2933 // session done, nothing happens any more
2934 aStepCmd = STEPCMD_DONE;
2938 // Waiting for SyncML request data
2940 switch (stepCmdIn) {
2941 case STEPCMD_GOTDATA : {
2942 // got data, check content type
2943 MemPtr_t data = NULL;
2944 smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &fRequestSize); // get request size
2945 SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, fRequestSize);
2946 if (getEncoding()==SML_UNDEF) {
2947 // no encoding known so far - use what we found from looking at data
2948 PDEBUGPRINTFX(DBG_ERROR,(
2949 "Incoming data had no or invalid content type, Determined encoding by looking at data: %s",
2950 SyncMLEncodingNames[enc]
2954 else if (getEncoding()!=enc) {
2955 // already known encoding does not match actual encoding
2956 PDEBUGPRINTFX(DBG_ERROR,(
2957 "Warning: Incoming data encoding mismatch: expected=%s, found=%s",
2958 SyncMLEncodingNames[getEncoding()],
2959 SyncMLEncodingNames[enc]
2962 if (getEncoding()==SML_UNDEF) {
2963 // if session encoding is still unknown at this point, reject data as non-SyncML
2964 PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML"));
2965 sta = LOCERR_BADCONTENT; // bad content type
2966 aStepCmd = STEPCMD_ERROR;
2967 // Note: we do not abort the session here - app could have a retry strategy and re-enter
2968 // this step with better data
2971 // content type ok - switch to processing mode
2972 fServerEngineState = ses_processing;
2973 aStepCmd = STEPCMD_OK;
2977 } // switch stepCmdIn for ses_needdata
2980 // Waiting until SyncML answer data is sent
2981 // (only when session needs to continue, otherwise we are in ses_done)
2983 switch (stepCmdIn) {
2984 case STEPCMD_SENTDATA :
2985 // sent data, now wait for next request
2986 fServerEngineState = ses_needdata;
2987 aStepCmd = STEPCMD_NEEDDATA;
2990 } // switch stepCmdIn for ses_dataready
2994 // Ready for generation steps
2995 case ses_generating:
2996 switch (stepCmdIn) {
2998 sta = ServerGeneratingStep(aStepCmd,aInfoP);
3000 } // switch stepCmdIn for ses_generating
3003 // Ready for processing steps
3004 case ses_processing:
3005 switch (stepCmdIn) {
3007 sta = ServerProcessingStep(aStepCmd,aInfoP);
3009 } // switch stepCmdIn for ses_processing
3012 case numServerEngineStates:
3016 } // switch fServerEngineState
3020 } // TSyncAgent::ServerSessionStep
3025 // Step that processes SyncML request data
3026 TSyError TSyncAgent::ServerProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3028 localstatus sta = LOCERR_WRONGUSAGE;
3029 InstanceID_t myInstance = getSmlWorkspaceID();
3032 // now process next command
3033 PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)"));
3035 MemPtr_t data = NULL;
3037 smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3043 if (rc==SML_ERR_CONTINUE) {
3044 // processed ok, but message not completely processed yet
3045 // - engine state remains as is
3046 aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step
3049 else if (rc==SML_ERR_OK) {
3050 // message completely processed
3051 // - switch engine state to generating answer message (if any)
3052 aStepCmd = STEPCMD_OK;
3053 fServerEngineState = ses_generating;
3056 else if (rc==LOCERR_RETRYMSG) {
3057 // server has detected that this message is a retry - report this to the app such that app can
3058 // first discard the instance buffer (consume everything in it)
3059 if (smlLockReadBuffer(myInstance,&data,&datasize)==SML_ERR_OK)
3060 smlUnlockReadBuffer(myInstance,datasize);
3061 // indicate that transport must resend the previous response
3062 PDEBUGPRINTFX(DBG_ERROR,(
3063 "Incoming message was identified as a retry - report STEPCMD_RESENDDATA - caller must resent last response"
3065 aStepCmd = STEPCMD_RESENDDATA;
3066 fServerEngineState = ses_dataready;
3069 // processing failed
3070 PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc));
3071 // dump the message that failed to process
3073 if (data) DumpSyncMLBuffer(data,datasize,false,rc);
3075 // abort the session (causing proper error events to be generated and reported back)
3076 AbortSession(LOCERR_PROCESSMSG, true);
3077 // session is now done
3078 fServerEngineState = ses_done;
3079 // step by itself is ok - let app continue stepping (to restart session or complete abort)
3080 aStepCmd = STEPCMD_OK;
3085 } // TSyncAgent::ServerProcessingStep
3089 // Step that generates (rest of) SyncML answer data at end of request
3090 TSyError TSyncAgent::ServerGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3096 done = EndRequest(hasdata, respURI, fRequestSize);
3097 // check different exit points
3099 // there is data to be sent
3100 aStepCmd = STEPCMD_SENDDATA;
3101 fServerEngineState = ses_dataready;
3104 // no more data to send
3105 aStepCmd = STEPCMD_OK; // need one more step to finish
3107 // in any case, if done, all susequent steps will return STEPCMD_DONE
3111 // subsequent steps will all return STEPCMD_DONE
3112 fServerEngineState = ses_done;
3117 // finished generating outgoing message
3118 // - make sure read pointer is set (advanced in case incoming
3119 // message had trailing garbage) to beginning of generated
3120 // answer. With incoming message being clean SyncML without
3121 // garbage, this call is not needed, however with garbage
3122 // it is important because otherwise outgoing message
3123 // would have that garbage inserted before actual message
3125 smlReadOutgoingAgain(getSmlWorkspaceID());
3129 } // TSyncAgent::ServerGeneratingStep
3131 #endif // ENGINE_LIBRARY
3133 #endif // SYSYNC_SERVER
3136 #ifdef SYSYNC_CLIENT
3138 #ifdef ENGINE_LIBRARY
3140 // Client implementation
3141 // ---------------------
3143 /// @brief Executes next step of the session
3144 /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
3145 /// - tells caller to send or receive data or end the session etc.
3146 /// - instructs engine to suspend or abort the session etc.
3147 /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
3148 /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
3149 TSyError TSyncAgent::ClientSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3151 uInt16 stepCmdIn = aStepCmd;
3152 localstatus sta = LOCERR_WRONGUSAGE;
3154 // init default response
3155 aStepCmd = STEPCMD_ERROR; // error
3157 aInfoP->eventtype=PEV_NOP;
3164 // if session is already aborted, no more steps are required
3166 fClientEngineState = ces_done; // we are done
3169 // handle pre-processed step command according to current engine state
3170 switch (fClientEngineState) {
3174 // session done, nothing happens any more
3175 aStepCmd = STEPCMD_DONE;
3181 // in idle, we can only start a session
3182 switch (stepCmdIn) {
3183 case STEPCMD_CLIENTSTART:
3184 case STEPCMD_CLIENTAUTOSTART:
3185 // initialize a new session
3186 sta = InitializeSession(fProfileSelectorInternal,stepCmdIn==STEPCMD_CLIENTAUTOSTART);
3187 if (sta!=LOCERR_OK) break;
3188 // engine is now ready, start generating first request
3189 fClientEngineState = ces_generating;
3190 // ok with no status
3191 aStepCmd = STEPCMD_OK;
3193 } // switch stepCmdIn for ces_idle
3197 // Ready for generation steps
3198 case ces_generating: {
3199 switch (stepCmdIn) {
3201 sta = ClientGeneratingStep(aStepCmd,aInfoP);
3203 } // switch stepCmdIn for ces_generating
3207 // Ready for processing steps
3208 case ces_processing: {
3209 switch (stepCmdIn) {
3211 sta = ClientProcessingStep(aStepCmd,aInfoP);
3213 } // switch stepCmdIn for ces_processing
3217 // Waiting for SyncML data
3218 case ces_needdata: {
3219 switch (stepCmdIn) {
3220 case STEPCMD_GOTDATA : {
3221 // got data, now start processing it
3222 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_recvend,NULL,0,0,0);
3223 // check content type now
3224 MemPtr_t data = NULL;
3226 smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3227 // check content type
3228 SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, datasize);
3229 if (enc!=getEncoding()) {
3230 PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML"));
3231 sta = LOCERR_BADCONTENT; // bad content type
3233 if (data) DumpSyncMLBuffer(data,datasize,false,SML_ERR_UNSPECIFIC);
3235 // abort the session (causing proper error events to be generated and reported back)
3236 AbortSession(sta, true);
3237 // session is now done
3238 fClientEngineState = ces_done;
3239 aStepCmd = STEPCMD_ERROR;
3242 // content type ok - switch to processing mode
3243 fIgnoreMsgErrs=false; // do not ignore errors by default
3244 fClientEngineState = ces_processing;
3245 aStepCmd = STEPCMD_OK;
3249 case STEPCMD_RESENDDATA :
3250 // instead of having received new data, the network layer has found it needs to re-send the data.
3251 // performing the STEPCMD_RESENDDATA just generates a new send start event, but otherwise no engine action
3252 fClientEngineState = ces_resending;
3253 aStepCmd = STEPCMD_RESENDDATA; // return the same step command, to differentiate it from STEPCMD_SENDDATA
3254 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0);
3257 } // switch stepCmdIn for ces_needdata
3261 // Waiting until SyncML data is sent
3263 case ces_resending: {
3264 switch (stepCmdIn) {
3265 case STEPCMD_SENTDATA :
3266 // allowed in dataready or resending state
3267 // sent (or re-sent) data, now request answer data
3268 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendend,NULL,0,0,0);
3269 fClientEngineState = ces_needdata;
3270 aStepCmd = STEPCMD_NEEDDATA;
3273 } // switch stepCmdIn for ces_dataready
3277 case numClientEngineStates: {
3282 } // switch fClientEngineState
3286 } // TSyncAgent::ClientSessionStep
3290 // Step that generates SyncML data
3291 TSyError TSyncAgent::ClientGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3293 localstatus sta = LOCERR_WRONGUSAGE;
3296 //%%% at this time, generate next message in one step
3297 sta = NextMessage(done);
3299 // done with session, with or without error
3300 fClientEngineState = ces_done; // blocks any further activity with the session
3301 aStepCmd = STEPCMD_DONE;
3302 // terminate session to provoke all end-of-session progress events
3305 else if (sta==LOCERR_OK) {
3306 // finished generating outgoing message
3307 // - make sure read pointer is set (advanced in case incoming
3308 // message had trailing garbage) to beginning of generated
3309 // answer. With incoming message being clean SyncML without
3310 // garbage, this call is not needed, however with garbage
3311 // it is important because otherwise outgoing message
3312 // would have that garbage inserted before actual message
3314 smlReadOutgoingAgain(getSmlWorkspaceID());
3315 // next is sending request to server
3316 fClientEngineState = ces_dataready;
3317 aStepCmd = STEPCMD_SENDDATA;
3318 OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0);
3322 } // TSyncAgent::ClientGeneratingStep
3326 // Step that processes SyncML data
3327 TSyError TSyncAgent::ClientProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3329 InstanceID_t myInstance = getSmlWorkspaceID();
3331 localstatus sta = LOCERR_WRONGUSAGE;
3333 // now process next command
3334 PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)"));
3336 MemPtr_t data = NULL;
3338 smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3344 if (rc==SML_ERR_CONTINUE) {
3345 // processed ok, but message not completely processed yet
3346 // - engine state remains as is
3347 aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step
3350 else if (rc==SML_ERR_OK) {
3351 // message completely processed
3352 // - switch engine state to generating next message (if any)
3353 aStepCmd = STEPCMD_OK;
3354 fClientEngineState = ces_generating;
3358 // processing failed
3359 PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc));
3360 // dump the message that failed to process
3362 if (data) DumpSyncMLBuffer(data,datasize,false,rc);
3364 if (!fIgnoreMsgErrs) {
3365 // abort the session (causing proper error events to be generated and reported back)
3366 AbortSession(LOCERR_PROCESSMSG, true);
3367 // session is now done
3368 fClientEngineState = ces_done;
3371 // we must ignore errors e.g. because of session restart and go back to generate next message
3372 fClientEngineState = ces_generating;
3374 // anyway, step by itself is ok - let app continue stepping (to restart session or complete abort)
3375 aStepCmd = STEPCMD_OK;
3378 // now check if this is a session restart
3379 if (sta==LOCERR_OK && isStarting()) {
3380 // this is still the beginning of a session, which means
3381 // that we are restarting the session and caller should close
3382 // possibly open communication with the server before sending the next message
3383 aStepCmd = STEPCMD_RESTART;
3387 } // TSyncAgent::ClientProcessingStep
3389 #endif // ENGINE_LIBRARY
3391 #endif // SYSYNC_CLIENT
3395 // Session runtime settings key
3396 // ---------------------------
3399 TAgentParamsKey::TAgentParamsKey(TEngineInterface *aEngineInterfaceP, TSyncAgent *aAgentP) :
3400 inherited(aEngineInterfaceP,aAgentP),
3403 } // TAgentParamsKey::TAgentParamsKey
3407 // - read local session ID
3408 static TSyError readLocalSessionID(
3409 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3410 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3413 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3414 return TStructFieldsKey::returnString(
3415 mykeyP->fAgentP->getLocalSessionID(),
3416 aBuffer,aBufSize,aValSize
3418 } // readLocalSessionID
3421 // - read initial local URI
3422 static TSyError readInitialLocalURI(
3423 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3424 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3427 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3428 return TStructFieldsKey::returnString(
3429 mykeyP->fAgentP->getInitialLocalURI(),
3430 aBuffer,aBufSize,aValSize
3432 } // readInitialLocalURI
3435 // - read abort status
3436 static TSyError readAbortStatus(
3437 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3438 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3441 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3442 return TStructFieldsKey::returnInt(
3443 mykeyP->fAgentP->getAbortReasonStatus(),
3445 aBuffer,aBufSize,aValSize
3447 } // readAbortStatus
3451 // - write abort status, which means aborting a session
3452 TSyError writeAbortStatus(
3453 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3454 cAppPointer aBuffer, memSize aValSize
3457 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3458 // abort the session
3459 TSyError sta = *((TSyError *)aBuffer);
3460 mykeyP->fAgentP->AbortSession(sta, true);
3462 } // writeAbortStatus
3466 // - read content type string
3467 static TSyError readContentType(
3468 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3469 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3472 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3473 string contentType = SYNCML_MIME_TYPE;
3474 mykeyP->fAgentP->addEncoding(contentType);
3475 return TStructFieldsKey::returnString(
3476 contentType.c_str(),
3477 aBuffer,aBufSize,aValSize
3479 } // readContentType
3482 // - write content type string
3483 static TSyError writeContentType(
3484 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3485 cAppPointer aBuffer, memSize aValSize
3488 string contentType((cAppCharP)aBuffer,aValSize);
3489 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3490 mykeyP->fAgentP->setEncoding(TSyncAppBase::encodingFromContentType(contentType.c_str()));
3492 } // writeContentType
3495 // - read connection URL
3496 static TSyError readConnectURI(
3497 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3498 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3501 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3502 return TStructFieldsKey::returnString(
3503 mykeyP->fAgentP->getSendURI(),
3504 aBuffer,aBufSize,aValSize
3509 // - read host part of connection URL
3510 static TSyError readConnectHost(
3511 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3512 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3515 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3517 splitURL(mykeyP->fAgentP->getSendURI(),NULL,&host,NULL,NULL,NULL);
3518 return TStructFieldsKey::returnString(
3520 aBuffer,aBufSize,aValSize
3522 } // readConnectHost
3525 // - read document part of connection URL
3526 static TSyError readConnectDoc(
3527 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3528 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3531 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3533 splitURL(mykeyP->fAgentP->getSendURI(),NULL,NULL,&doc,NULL,NULL);
3534 return TStructFieldsKey::returnString(
3536 aBuffer,aBufSize,aValSize
3541 // - time when session was last used
3542 static TSyError readLastUsed(
3543 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3544 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3547 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3549 return TStructFieldsKey::returnLineartime(mykeyP->fAgentP->getSessionLastUsed(), aBuffer, aBufSize, aValSize);
3553 // - server only: check session timeout
3554 static TSyError readTimedOut(
3555 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3556 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3559 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3560 // check if session has timed out
3561 bool timedout = mykeyP->fAgentP->getSessionLastUsed()+mykeyP->fAgentP->getSessionConfig()->getSessionTimeout() < mykeyP->fAgentP->getSystemNowAs(TCTX_UTC);
3563 return TStructFieldsKey::returnInt(timedout, sizeof(bool), aBuffer, aBufSize, aValSize);
3567 #ifdef SYSYNC_SERVER
3569 // - server only: read respURI enable flag
3570 static TSyError readSendRespURI(
3571 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3572 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3575 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3576 return TStructFieldsKey::returnInt(mykeyP->fAgentP->fUseRespURI, sizeof(bool), aBuffer, aBufSize, aValSize);
3577 } // readSendRespURI
3580 // - write respURI enable flag
3581 static TSyError writeSendRespURI(
3582 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3583 cAppPointer aBuffer, memSize aValSize
3586 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3587 mykeyP->fAgentP->fUseRespURI = *((uInt8P)aBuffer);
3589 } // writeSendRespURI
3592 #endif // SYSYNC_SERVER
3595 #ifdef SYSYNC_CLIENT
3597 // - write (volatile, write-only) password for running this session
3598 // (for cases where we don't want to rely on binfile storage for sensitive password data)
3599 TSyError writeSessionPassword(
3600 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3601 cAppPointer aBuffer, memSize aValSize
3604 TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3605 mykeyP->fAgentP->setServerPassword((cAppCharP)aBuffer, aValSize);
3607 } // writeSessionPassword
3610 #ifdef ENGINE_LIBRARY
3611 // - read display alert
3612 static TSyError readDisplayAlert(
3613 TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3614 appPointer aBuffer, memSize aBufSize, memSize &aValSize
3617 TClientEngineInterface *clientEngineP =
3618 static_cast<TClientEngineInterface *>(aStructFieldsKeyP->getEngineInterface());
3619 return TStructFieldsKey::returnString(
3620 clientEngineP->fAlertMessage.c_str(),
3621 aBuffer,aBufSize,aValSize
3623 } // readDisplayAlert
3626 #endif // SYSYNC_CLIENT
3629 // accessor table for server session key
3630 static const TStructFieldInfo ServerParamFieldInfos[] =
3632 // valName, valType, writable, fieldOffs, valSiz
3633 { "localSessionID", VALTYPE_TEXT, false, 0, 0, &readLocalSessionID, NULL },
3634 { "initialLocalURI", VALTYPE_TEXT, false, 0, 0, &readInitialLocalURI, NULL },
3635 { "abortStatus", VALTYPE_INT16, true, 0, 0, &readAbortStatus, &writeAbortStatus },
3636 { "contenttype", VALTYPE_TEXT, true, 0, 0, &readContentType, &writeContentType },
3637 { "connectURI", VALTYPE_TEXT, false, 0, 0, &readConnectURI, NULL },
3638 { "connectHost", VALTYPE_TEXT, false, 0, 0, &readConnectHost, NULL },
3639 { "connectDoc", VALTYPE_TEXT, false, 0, 0, &readConnectDoc, NULL },
3640 { "timedout", VALTYPE_INT8, false, 0, 0, &readTimedOut, NULL },
3641 { "lastused", VALTYPE_TIME64, false, 0, 0, &readLastUsed, NULL },
3642 #ifdef SYSYNC_SERVER
3643 { "sendrespuri", VALTYPE_INT8, true, 0, 0, &readSendRespURI, &writeSendRespURI },
3645 #ifdef SYSYNC_CLIENT
3646 { "sessionPassword", VALTYPE_TEXT, true, 0, 0, NULL, &writeSessionPassword },
3647 #ifdef ENGINE_LIBRARY
3648 { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL },
3653 // get table describing the fields in the struct
3654 const TStructFieldInfo *TAgentParamsKey::getFieldsTable(void)
3656 return ServerParamFieldInfos;
3657 } // TAgentParamsKey::getFieldsTable
3659 sInt32 TAgentParamsKey::numFields(void)
3661 return sizeof(ServerParamFieldInfos)/sizeof(TStructFieldInfo);
3662 } // TAgentParamsKey::numFields
3664 // get actual struct base address
3665 uInt8P TAgentParamsKey::getStructAddr(void)
3667 // prepared for accessing fields in server session object
3668 return (uInt8P)fAgentP;
3669 } // TAgentParamsKey::getStructAddr
3672 // open subkey by name (not by path!)
3673 TSyError TAgentParamsKey::OpenSubKeyByName(
3674 TSettingsKeyImpl *&aSettingsKeyP,
3675 cAppCharP aName, stringSize aNameSize,
3678 #ifdef DBAPI_TUNNEL_SUPPORT
3679 if (strucmp(aName,"tunnel",aNameSize)==0) {
3680 // get tunnel datastore pointer
3681 TLocalEngineDS *ds = fAgentP->getTunnelDS();
3682 if (!ds) return LOCERR_WRONGUSAGE;
3683 // opens current session's tunnel key
3684 aSettingsKeyP = ds->newTunnelKey(fEngineInterfaceP);
3688 return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
3691 } // TAgentParamsKey::OpenSubKeyByName
3694 #endif // ENGINEINTERFACE_SUPPORT
3696 } // namespace sysync