/* * File: syncagent.cpp * * Author: Lukas Zeller (luz@synthesis.ch) * * TSyncAgent: Provides functionality to run client or server * sessions. * Unifies former TSyncClient and TSyncServer * * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch) * * 2009-09-30 : luz : created from syncclient.cpp and syncserver.cpp * */ // includes #include "prefix_file.h" #include "sysync.h" #include "syncagent.h" #include "syncappbase.h" #ifdef HARD_CODED_SERVER_URI #include "syserial.h" #endif // includes that can't be in .h due to circular references #ifdef SYSYNC_SERVER #include "syncsessiondispatch.h" #endif #ifdef SYSYNC_CLIENT #include "syncclientbase.h" #endif #ifdef SYSYNC_TOOL #include #endif namespace sysync { #ifdef SYSYNC_TOOL // Support for SySync Diagnostic Tool // ================================== // test login into database int testLogin(int argc, const char *argv[]) { if (argc<0) { // help requested CONSOLEPRINTF((" login [ ] []")); CONSOLEPRINTF((" test login to database with syncml user/password and optional deviceid")); return EXIT_SUCCESS; } TSyncSession *sessionP = NULL; const char *username = NULL; const char *password = NULL; const char *deviceid = "sysytool_test"; // check for argument if (argc<2) { // no user/password, test anonymous login if (argc>0) deviceid = argv[0]; } else { // login with user/password username = argv[0]; password = argv[1]; if (argc>2) { // explicit device ID deviceid = argv[2]; } } // get session to work with sessionP = static_cast(getSyncAppBase())->getSySyToolSession(); bool authok = false; // try login if (username) { // real login with user and password authok = sessionP->SessionLogin(username, password, sectyp_clearpass, deviceid); } else { // anonymous - do a "login" with empty credentials authok = sessionP->SessionLogin("anonymous", NULL, sectyp_anonymous, deviceid); } if (authok) { CONSOLEPRINTF(("+++++ Successfully authorized")); } else { CONSOLEPRINTF(("----- Authorisation failed")); } return authok; } // testLogin // convert user data into internal format and back int convertData(int argc, const char *argv[]) { if (argc<0) { // help requested CONSOLEPRINTF((" convert [] []")); CONSOLEPRINTF((" Convert data to internal format of specified datastore and back")); return EXIT_SUCCESS; } TSyncSession *sessionP = NULL; const char *datastore = NULL; const char *rawfilename = NULL; const char *inputtype = NULL; const char *outputtype = NULL; // check for argument if (argc<2) { CONSOLEPRINTF(("required datatype name and raw file name arguments")); return EXIT_FAILURE; } datastore = argv[0]; rawfilename = argv[1]; if (argc>=3) { // third arg is explicit input type inputtype=argv[2]; } outputtype=inputtype; // default to input type if (argc>=4) { // fourth arg is explicit output type outputtype=argv[3]; } // get session to work with sessionP = static_cast(getSyncAppBase())->getSySyToolSession(); // configure session sessionP->fRemoteCanHandleUTC = true; // run generator and parser in UTC enabled mode // switch mimimal debugging on sessionP->getDbgLogger()->setMask(sessionP->getDbgLogger()->getMask() | (DBG_PARSE+DBG_GEN)); // find datastore TLocalEngineDS *datastoreP = sessionP->findLocalDataStore(datastore); TSyncItemType *inputtypeP = NULL; TSyncItemType *outputtypeP = NULL; if (!datastoreP) { CONSOLEPRINTF(("datastore type '%s' not found",datastore)); return EXIT_FAILURE; } // find input type if (inputtype) { // search in datastore inputtypeP=datastoreP->getReceiveType(inputtype,NULL); } else { // use preferred rx type inputtypeP=datastoreP->getPreferredRxItemType(); } if (!inputtypeP) { CONSOLEPRINTF(("input type not found")); return EXIT_FAILURE; } // find output type if (outputtype) { // search in datastore outputtypeP=datastoreP->getSendType(outputtype,NULL); } else { // use preferred rx type outputtypeP=datastoreP->getPreferredTxItemType(); } if (!outputtypeP) { CONSOLEPRINTF(("output type not found")); return EXIT_FAILURE; } // prepare type usage if (inputtypeP==outputtypeP) inputtypeP->initDataTypeUse(datastoreP, true, true); else { inputtypeP->initDataTypeUse(datastoreP, false, true); outputtypeP->initDataTypeUse(datastoreP, true, false); } // now open file and read data item FILE *infile; size_t insize=0; uInt8 *databuffer; infile = fopen(rawfilename,"rb"); if (!infile) { CONSOLEPRINTF(("Cannot open input file '%s' (%d)",rawfilename,errno)); return EXIT_FAILURE; } // - get size of file fseek(infile,0,SEEK_END); insize=ftell(infile); fseek(infile,0,SEEK_SET); // - create buffer of appropriate size databuffer = new uInt8[insize]; if (!databuffer) { CONSOLEPRINTF(("Not enough memory to read input file '%s' (%d)",rawfilename,errno)); return EXIT_FAILURE; } // - read data if (fread(databuffer,1,insize,infile)data=newPCDataStringX(databuffer,true,insize); delete[] databuffer; // create and fill a Sync item TSyncItem *syncitemP = inputtypeP->newSyncItem( smlitemP, // SyncML toolkit item Data to be converted into SyncItem sop_replace, // the operation to be performed with this item fmt_chr, // assume default (char) format inputtypeP, // target myself datastoreP, // local datastore statusCmd // status command that might be modified in case of error ); // forget SyncML version smlFreeItemPtr(smlitemP); if (!syncitemP) { CONSOLEPRINTF(("Error converting input file to internal format (SyncML status code=%hd)",statusCmd.getStatusCode())); return EXIT_FAILURE; } CONSOLEPRINTF(("\nNow copying item and convert back to transport format\n")); // make new for output type TSyncItem *outsyncitemP = outputtypeP->newSyncItem( outputtypeP, // target myself datastoreP // local datastore ); // copy data outsyncitemP->replaceDataFrom(*syncitemP); delete syncitemP; // convert back smlitemP=outputtypeP->newSmlItem( outsyncitemP, // the syncitem to be represented as SyncML datastoreP // local datastore ); if (!syncitemP) { CONSOLEPRINTF(("Could not convert back item data")); return EXIT_FAILURE; } // forget converted back item smlFreeItemPtr(smlitemP); return EXIT_SUCCESS; } // convertData #endif // SYSYNC_TOOL #ifdef PRECONFIGURED_SYNCREQUESTS // Implementation of TSyncReqConfig // ================================ // config for databases to sync with TSyncReqConfig::TSyncReqConfig(TLocalDSConfig *aLocalDSCfg, TConfigElement *aParentElement) : TConfigElement("syncrequest",aParentElement), fLocalDSConfig(aLocalDSCfg) { clear(); } // TSyncReqConfig::TSyncReqConfig TSyncReqConfig::~TSyncReqConfig() { // nop so far } // TSyncReqConfig::~TSyncReqConfig // init defaults void TSyncReqConfig::clear(void) { // init defaults // - local client datatstore subselection path or CGI (such as "test" in "contact/test") fLocalPathExtension.erase(); // - remote server DB layer auth fDBUser.erase(); fDBPassword.erase(); // - remote server datastore path fServerDBPath.erase(); // - sync mode fSyncMode=smo_twoway; fSlowSync=false; // default to non-slow // - DS 1.2 filtering parameters fRecordFilterQuery.erase(); fFilterInclusive=false; // clear inherited inherited::clear(); } // TSyncReqConfig::clear // config element parsing bool TSyncReqConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) { // checking the elements if (strucmp(aElementName,"localpathextension")==0) expectString(fLocalPathExtension); else if (strucmp(aElementName,"dbuser")==0) expectString(fDBUser); else if (strucmp(aElementName,"dbpassword")==0) expectString(fDBPassword); else if (strucmp(aElementName,"dbpath")==0) expectString(fServerDBPath); else if (strucmp(aElementName,"syncmode")==0) expectEnum(sizeof(fSyncMode),&fSyncMode,SyncModeNames,numSyncModes); else if (strucmp(aElementName,"slowsync")==0) expectBool(fSlowSync); else if (strucmp(aElementName,"recordfilter")==0) expectString(fRecordFilterQuery); else if (strucmp(aElementName,"filterinclusive")==0) expectBool(fFilterInclusive); // - none known here else return inherited::localStartElement(aElementName,aAttributes,aLine); // ok return true; } // TSyncReqConfig::localStartElement // resolve void TSyncReqConfig::localResolve(bool aLastPass) { if (aLastPass) { // check for required settings // %%% tbd } // resolve inherited inherited::localResolve(aLastPass); } // TSyncReqConfig::localResolve // create appropriate type of local datastore from config and init sync parameters TLocalEngineDS *TSyncReqConfig::initNewLocalDataStore(TSyncSession *aSessionP) { // - create appropriate type of localdsP TLocalEngineDS *localdsP = fLocalDSConfig->newLocalDataStore(aSessionP); // - set parameters localdsP->dsSetClientSyncParams( fSyncMode, fSlowSync, fServerDBPath.c_str(), fDBUser.c_str(), fDBPassword.c_str(), fLocalPathExtension.c_str(), fRecordFilterQuery.c_str(), fFilterInclusive ); return localdsP; } // TSyncReqConfig::initNewLocalDataStore #endif // PRECONFIGURED_SYNCREQUESTS // Implementation of TAgentConfig // ============================== TAgentConfig::TAgentConfig(const char* aName, TConfigElement *aParentElement) : inherited(aName,aParentElement) { clear(); } // TAgentConfig::TAgentConfig TAgentConfig::~TAgentConfig() { clear(); } // TAgentConfig::~TAgentConfig // init defaults void TAgentConfig::clear(void) { // clear inherited inherited::clear(); // Note: we always clear both client and server fields - even if we'll only use one set later #ifdef SYSYNC_CLIENT // init client auth defaults (note that these MUST correspond with the defaults set by loadRemoteParams() !!! fAssumedServerAuth=auth_none; // start with no auth fAssumedServerAuthEnc=fmt_chr; // start with char encoding fAssumedNonce.erase(); // start with no nonce // auth retry options (mainly for stupid servers like SCTS) #ifdef SCTS_COMPATIBILITY_HACKS fNewSessionForAuthRetry=false; fNoRespURIForAuthRetry=false; #else fNewSessionForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7 fNoRespURIForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7 #endif fSmartAuthRetry=true; // try to be smart and try different auth retry (different from fNewSessionForAuthRetry/fNoRespURIForAuthRetry) if first attempts fail // other defaults fPutDevInfAtSlowSync=true; // smartner server needs it, and it does not harm so we have it on by default #ifndef NO_LOCAL_DBLOGIN fLocalDBUser.erase(); fLocalDBPassword.erase(); fNoLocalDBLogin=false; #endif #ifdef PRECONFIGURED_SYNCREQUESTS fEncoding=SML_XML; // default to more readable XML fServerUser.erase(); fServerPassword.erase(); fServerURI.erase(); fTransportUser.erase(); fTransportPassword.erase(); fSocksHost.erase(); fProxyHost.erase(); fProxyUser.erase(); fProxyPassword.erase(); // remove sync db specifications TSyncReqList::iterator pos; for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++) delete *pos; fSyncRequests.clear(); #endif // clear inherited inherited::clear(); // modify timeout after inherited sets it fSessionTimeout=DEFAULT_CLIENTSESSIONTIMEOUT; // SyncML version support fAssumedServerVersion=MAX_SYNCML_VERSION; // try with highest version we support fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION; // support what we request (overrides session default) #endif #ifdef SYSYNC_SERVER // init server defaults fRequestedAuth = auth_md5; fRequiredAuth = auth_md5; fAutoNonce = true; fConstantNonce.erase(); fExternalURL.erase(); fMaxGUIDSizeSent = 32; // reasonable size, but prevent braindamaged Exchange-size IDs to be sent fUseRespURI = true; fRespURIOnlyWhenDifferent = true; // modify timeout after inherited sets it fSessionTimeout=DEFAULT_SERVERSESSIONTIMEOUT; #endif } // TAgentConfig::clear #ifndef HARDCODED_CONFIG // config element parsing bool TAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine) { if (IS_CLIENT) { // check the client elements #ifdef SYSYNC_CLIENT // - defaults for starting a session if (strucmp(aElementName,"defaultsyncmlversion")==0) expectEnum(sizeof(fAssumedServerVersion),&fAssumedServerVersion,SyncMLVersionNames,numSyncMLVersions); else if (strucmp(aElementName,"defaultauth")==0) expectEnum(sizeof(fAssumedServerAuth),&fAssumedServerAuth,authTypeNames,numAuthTypes); else if (strucmp(aElementName,"defaultauthencoding")==0) expectEnum(sizeof(fAssumedServerAuthEnc),&fAssumedServerAuthEnc,encodingFmtSyncMLNames,numFmtTypes); else if (strucmp(aElementName,"defaultauthnonce")==0) expectString(fAssumedNonce); else if (strucmp(aElementName,"newsessionforretry")==0) expectBool(fNewSessionForAuthRetry); else if (strucmp(aElementName,"originaluriforretry")==0) expectBool(fNoRespURIForAuthRetry); else if (strucmp(aElementName,"smartauthretry")==0) expectBool(fSmartAuthRetry); // - other options else if (strucmp(aElementName,"putdevinfatslowsync")==0) expectBool(fPutDevInfAtSlowSync); else if (strucmp(aElementName,"fakedeviceid")==0) expectString(fFakeDeviceID); else #ifndef NO_LOCAL_DBLOGIN if (strucmp(aElementName,"localdbuser")==0) expectString(fLocalDBUser); else if (strucmp(aElementName,"localdbpassword")==0) expectString(fLocalDBPassword); else if (strucmp(aElementName,"nolocaldblogin")==0) expectBool(fNoLocalDBLogin); else #endif // serverURL is always available to allow define fixed URL in config that can't be overridden in profiles if (strucmp(aElementName,"serverurl")==0) expectString(fServerURI); else #ifdef PRECONFIGURED_SYNCREQUESTS if (strucmp(aElementName,"syncmlencoding")==0) expectEnum(sizeof(fEncoding),&fEncoding,SyncMLEncodingNames,numSyncMLEncodings); else if (strucmp(aElementName,"serveruser")==0) expectString(fServerUser); else if (strucmp(aElementName,"serverpassword")==0) expectString(fServerPassword); else if (strucmp(aElementName,"sockshost")==0) expectString(fSocksHost); else if (strucmp(aElementName,"proxyhost")==0) expectString(fProxyHost); else if (strucmp(aElementName,"proxyuser")==0) expectString(fProxyUser); else if (strucmp(aElementName,"proxypassword")==0) expectString(fProxyPassword); else if (strucmp(aElementName,"transportuser")==0) expectString(fTransportUser); else if (strucmp(aElementName,"transportpassword")==0) expectString(fTransportPassword); // - Sync DB specification else if (strucmp(aElementName,"syncrequest")==0) { // definition of a new datastore const char* nam = getAttr(aAttributes,"datastore"); if (!nam) { ReportError(true,"syncrequest missing 'datastore' attribute"); } else { // search datastore TLocalDSConfig *localDSCfgP = getLocalDS(nam); if (!localDSCfgP) return fail("unknown local datastore '%s' specified",nam); // create new syncDB config linked to that datastore TSyncReqConfig *syncreqcfgP = new TSyncReqConfig(localDSCfgP,this); // - save in list fSyncRequests.push_back(syncreqcfgP); // - let element handle parsing expectChildParsing(*syncreqcfgP); } } else #endif // - none known here return inherited::localStartElement(aElementName,aAttributes,aLine); #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER // check the server elements if (strucmp(aElementName,"requestedauth")==0) expectEnum(sizeof(fRequestedAuth),&fRequestedAuth,authTypeNames,numAuthTypes); else if (strucmp(aElementName,"requiredauth")==0) expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes); // here to maintain compatibility with old pre 1.0.5.3 config files else if (strucmp(aElementName,"reqiredauth")==0) expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes); else if (strucmp(aElementName,"autononce")==0) expectBool(fAutoNonce); else if (strucmp(aElementName,"constantnonce")==0) expectString(fConstantNonce); else if (strucmp(aElementName,"externalurl")==0) expectString(fExternalURL); else if (strucmp(aElementName,"maxguidsizesent")==0) expectUInt16(fMaxGUIDSizeSent); else if (strucmp(aElementName,"sendrespuri")==0) expectBool(fUseRespURI); else if (strucmp(aElementName,"respurionlywhendifferent")==0) expectBool(fRespURIOnlyWhenDifferent); // - none known here else return inherited::localStartElement(aElementName,aAttributes,aLine); #endif // SYSYNC_SERVER } // ok return true; } // TAgentConfig::localStartElement #endif // HARDCODED_CONFIG // resolve void TAgentConfig::localResolve(bool aLastPass) { if (aLastPass) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT #ifdef PRECONFIGURED_SYNCREQUESTS // - resolve requests TSyncReqList::iterator pos; for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++) (*pos)->Resolve(aLastPass); #endif #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER if (!fAutoNonce && fConstantNonce.empty()) ReportError(false,"Warning: 'constantnonce' should be defined when 'autononce' is not set"); #endif // SYSYNC_SERVER } } // resolve inherited inherited::localResolve(aLastPass); } // TAgentConfig::localResolve // Implementation of TSyncAgent // ============================= // constructor TSyncAgent::TSyncAgent( TSyncAppBase *aAppBaseP, TSyncSessionHandle *aSessionHandleP, const char *aSessionID // a session ID ) : TSyncSession(aAppBaseP,aSessionID) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT #ifdef HARD_CODED_SERVER_URI fNoCRCPrefixLen=0; #endif #ifdef ENGINE_LIBRARY // engine fClientEngineState = ces_idle; #endif // reset session now to get correct initial state InternalResetSession(); // restart with session numbering at 1 (incremented before use) fClientSessionNo=0; #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER // init answer buffer fBufferedAnswer = NULL; fBufferedAnswerSize = 0; // reset data counts fIncomingBytes = 0; fOutgoingBytes = 0; #ifdef ENGINE_LIBRARY // engine fServerEngineState = ses_needdata; fRequestSize = 0; #endif // init own stuff InternalResetSession(); // save session handle fSessionHandleP = aSessionHandleP; // link to handle // get config defaults TAgentConfig *configP = static_cast(aAppBaseP->getRootConfig()->fAgentConfigP); fUseRespURI = configP->fUseRespURI; // create all locally available datastores from config TLocalDSList::iterator pos; for (pos=configP->fDatastores.begin(); pos!=configP->fDatastores.end(); pos++) { // create the datastore addLocalDataStore(*pos); } #endif // SYSYNC_SERVER } } // TSyncAgent::TSyncAgent // destructor TSyncAgent::~TSyncAgent() { if (IS_CLIENT) { // make sure everything is terminated BEFORE destruction of hierarchy begins TerminateSession(); } else { #ifdef SYSYNC_SERVER // forget any buffered answers bufferAnswer(NULL,0); // reset session InternalResetSession(); // show session data transfer PDEBUGPRINTFX(DBG_HOT,( "Session data transfer statistics: incoming bytes=%ld, outgoing bytes=%ld", (long)fIncomingBytes, (long)fOutgoingBytes )); // DO NOT remove session from dispatcher here, // this is the task of the dispatcher itself! CONSOLEPRINTF(("Terminated SyncML session (server id=%s)\n",getLocalSessionID())); // show end of session in global level POBJDEBUGPRINTFX(getSyncAppBase(),DBG_HOT,( "TSyncAgent::~TSyncAgent: Deleted SyncML session (local session id=%s)", getLocalSessionID() )); #endif // SYSYNC_SERVER } } // TSyncAgent::~TSyncAgent // Terminate session void TSyncAgent::TerminateSession() { #ifdef SYSYNC_CLIENT if (IS_CLIENT && !fTerminated) { InternalResetSession(); #ifdef ENGINE_LIBRARY // switch state to done to prevent any further activity via SessionStep() fClientEngineState = ces_done; #endif } #endif // SYSYNC_CLIENT inherited::TerminateSession(); } // TSyncAgent::TerminateSession void TSyncAgent::InternalResetSession(void) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT // use remote URI as specified to start a session fRespondURI = fRemoteURI; #ifdef HARD_CODED_SERVER_URI #if defined(CUSTOM_URI_SUFFIX) && !defined(HARD_CODED_SERVER_URI_LEN) #error "HARD_CODED_SERVER_URI_LEN must be defined when using CUSTOM_URI_SUFFIX" #endif #ifdef HARD_CODED_SERVER_URI_LEN // only part of URL must match (max HARD_CODED_SERVER_URI_LEN chars will be added, less if URI is shorter) fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false, HARD_CODED_SERVER_URI_LEN); #else // entire URL (except prefix) must match fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false); #endif #endif // set SyncML version // Note: will be overridden with call to loadRemoteParams() fSyncMLVersion = syncml_vers_unknown; // unknown // will be cleared to suppress automatic use of DS 1.2 SINCE/BEFORE filters // (e.g. for date range in func_SetDaysRange()) fServerHasSINCEBEFORE = true; // no outgoing alert 222 sent so far fOutgoingAlert222Count = 0; #endif } else { #ifdef SYSYNC_SERVER // %%% remove this as soon as Server is 1.1 compliant //fSyncMLVersion=syncml_vers_1_0; // only accepts 1.0 for now %%%% #endif } } // TSyncAgent::InternalResetSession // Virtual version void TSyncAgent::ResetSession(void) { // let ancestor do its stuff TSyncSession::ResetSession(); // do my own stuff (and probably modify settings of ancestor!) InternalResetSession(); } // TSyncAgent::ResetSession bool TSyncAgent::MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT return ClientMessageStarted(aContentP,aStatusCommand,aBad); #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER return ServerMessageStarted(aContentP,aStatusCommand,aBad); #endif // SYSYNC_SERVER } } void TSyncAgent::MessageEnded(bool aIncomingFinal) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT ClientMessageEnded(aIncomingFinal); #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER ServerMessageEnded(aIncomingFinal); #endif // SYSYNC_SERVER } } // starting with engine version 2.0.8.7 a client's device ID (in devinf) is no longer // a constant string, but the device's unique ID string TSyncAgent::getDeviceID(void) { if (fLocalURI.empty()) { if (IS_SERVER) return SYSYNC_SERVER_DEVID; // return default ID else return SYSYNC_CLIENT_DEVID; // return default ID } else return fLocalURI; } // TSyncAgent::getDeviceID // ask syncappbase for device type string TSyncAgent::getDeviceType(void) { // taken from configuration or default for engine type (client/server) return getSyncAppBase()->getDevTyp(); } // TSyncAgent::getDeviceType #ifdef SYSYNC_CLIENT // initialize the client session and link it with the SML toolkit localstatus TSyncAgent::InitializeSession(uInt32 aProfileSelector, bool aAutoSyncSession) { localstatus sta; // Select profile now (before creating instance, as encoding is dependent on profile) sta=SelectProfile(aProfileSelector, aAutoSyncSession); if (sta) return sta; // Start a SyncML toolkit instance now and set the encoding from config InstanceID_t myInstance; if (!getSyncAppBase()->newSmlInstance( getEncoding(), getRootConfig()->fLocalMaxMsgSize * 2, // twice the message size myInstance )) { return LOCERR_SMLFATAL; } // let toolkit know the session pointer if (getSyncAppBase()->setSmlInstanceUserData(myInstance,this)!=SML_ERR_OK) // toolkit must know session (as userData) return LOCERR_SMLFATAL; // remember the instance myself setSmlWorkspaceID(myInstance); // session must know toolkit workspace // done return LOCERR_OK; } // TSyncAgent::InitializeSession // select a profile (returns false if profile not found) // Note: base class just tries to retrieve information from // config localstatus TSyncAgent::SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession) { #ifndef PRECONFIGURED_SYNCREQUESTS // no profile settings in config -> error return LOCERR_NOCFG; #else // get profile settings from config TAgentConfig *configP = static_cast(getRootConfig()->fAgentConfigP); // - get encoding fEncoding=configP->fEncoding; // SyncML encoding // - set server access details fRemoteURI=configP->fServerURI; // Remote URI = Server URI fTransportUser=configP->fTransportUser; // transport layer user (e.g. HTTP auth) fTransportPassword=configP->fTransportPassword; // transport layer password (e.g. HTTP auth) fServerUser=configP->fServerUser; // Server layer authentification user name fServerPassword=configP->fServerPassword; // Server layer authentification password #ifndef NO_LOCAL_DBLOGIN fLocalDBUser=configP->fLocalDBUser; // Local DB authentification user name (empty if local DB is single user) fNoLocalDBLogin=configP->fNoLocalDBLogin; // if set, no local DB auth takes place, but fLocalDBUser is used as userkey (depending on DB implementation) fLocalDBPassword=configP->fLocalDBPassword; // Local DB authentification password #endif fProxyHost=configP->fProxyHost; // Proxy host fSocksHost=configP->fSocksHost; // Socks host fProxyUser=configP->fProxyUser; fProxyPassword=configP->fProxyPassword; // Reset session after profile change // and also remove any datastores we might have ResetAndRemoveDatastores(); // if tunnel, that's all for now if (aProfileSelector==TUNNEL_PROFILE_ID) return LOCERR_OK; // - create and init datastores needed for this session from config // Note: probably config has no sync requests, but they are created later // programmatically TSyncReqList::iterator pos; for (pos=configP->fSyncRequests.begin(); pos!=configP->fSyncRequests.end(); pos++) { // create and init the datastore fLocalDataStores.push_back( (*pos)->initNewLocalDataStore(this) ); } // create "new" session ID (derivates will do this better) fClientSessionNo++; return LOCERR_OK; #endif } // TSyncAgent::SelectProfile // make sure we are logged in to local datastore localstatus TSyncAgent::LocalLogin(void) { #ifndef NO_LOCAL_DBLOGIN if (!fNoLocalDBLogin && !fLocalDBUser.empty()) { // check authorisation (login to correct user) in local DB if (!SessionLogin(fLocalDBUser.c_str(),fLocalDBPassword.c_str(),sectyp_clearpass,fRemoteURI.c_str())) { return localError(401); // done & error } } #endif return LOCERR_OK; } // TSyncAgent::LocalLogin // process message in the instance buffer localstatus TSyncAgent::processAnswer(void) { InstanceID_t myInstance = getSmlWorkspaceID(); Ret_t err; // now process data DEBUGPRINTF(("===> now calling smlProcessData")); #ifdef SYDEBUG MemPtr_t data = NULL; MemSize_t datasize; smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize); #endif fIgnoreMsgErrs=false; err=smlProcessData( myInstance, SML_ALL_COMMANDS ); if (err) { // dump the message that failed to process #ifdef SYDEBUG if (data) DumpSyncMLBuffer(data,datasize,false,err); #endif if (!fIgnoreMsgErrs) { PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)err)); // other problem or already using SyncML 1.0 --> error return LOCERR_PROCESSMSG; } } // now check if this is a session restart if (isStarting()) { // this is still the beginning of a session return LOCERR_SESSIONRST; } return LOCERR_OK; } // TSyncAgentBase::processAnswer // let session produce (or finish producing) next message into // SML workspace // - returns aDone if no answer needs to be sent (=end of session) // - returns 0 if successful // - returns SyncML status code if unsucessfully aborted session localstatus TSyncAgent::NextMessage(bool &aDone) { TLocalDataStorePContainer::iterator pos; TSyError status; TP_START(fTPInfo,TP_general); // could be new thread // default to not continuing aDone=true; #ifdef PROGRESS_EVENTS // check for user suspend if (!getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) { SuspendSession(LOCERR_USERSUSPEND); } #endif // done if session was aborted by last received commands if (isAborted()) return getAbortReasonStatus(); // done & error // check package state if (fOutgoingState==psta_idle) { // if suspended here, we'll just stop - nothing has happened yet if (isSuspending()) { AbortSession(fAbortReasonStatus,true); return getAbortReasonStatus(); } // start of an entirely new client session #ifdef HARD_CODED_SERVER_URI // extra check to limit hacking if (fServerURICRC != SERVER_URI_CRC) { // someone has tried to change the URI DEBUGPRINTFX(DBG_ERROR,("hardcoded Server URI CRC mismatch")); return LOCERR_LIMITED; // user will not know what this means, but we will } #endif // - check if we have client requests if (fLocalDataStores.size()<1) { PDEBUGPRINTFX(DBG_ERROR,("No datastores defined to sync with")); return LOCERR_NOCFG; } // %%% later, we could probably load cached info about // server requested auth, devinf etc. here // use remote URI as specified to start a session fRespondURI=fRemoteURI; // get default params for sending first message to remote // Note: may include remote flag settings that influence creation of my own ID below, that's why we do it now here loadRemoteParams(); // get info about my own URI, whatever that is #ifndef HARDCODED_CONFIG if (!static_cast(getRootConfig()->fAgentConfigP)->fFakeDeviceID.empty()) { // return fake Device ID if we have one defined in the config file (useful for testing) fLocalURI = static_cast(getRootConfig()->fAgentConfigP)->fFakeDeviceID; } else #endif { if (!getLocalDeviceID(fLocalURI) || devidWithUserHash()) { // Device ID is not really unique, make a hash including user name to make it pseudo-unique // create MD5 hash from non-unique ID and user name // Note: when compiled with GUARANTEED_UNIQUE_DEVICID, devidWithUserHash() is always false. md5::SYSYNC_MD5_CTX context; uInt8 digest[16]; // for MD5 digest md5::Init (&context); // - add what we got for ID md5::Update (&context, (uInt8 *)fLocalURI.c_str(), fLocalURI.size()); // - add user name, if any if (fLocalURI.size()>0) md5::Update (&context, (uInt8 *)fServerUser.c_str(), fServerUser.size()); // - done md5::Final (digest, &context); // now make hex string of that fLocalURI = devidWithUserHash() ? 'x' : 'X'; // start with X to document this special case (lowercase = forced by remoteFlag) for (int n=0; n<16; n++) { AppendHexByte(fLocalURI,digest[n]); } } } // get my own name (if any) getPlatformString(pfs_device_name,fLocalName); // override some of these if not set by loadRemoteParams() if (fSyncMLVersion==syncml_vers_unknown) fSyncMLVersion=static_cast(getRootConfig()->fAgentConfigP)->fAssumedServerVersion; if (fRemoteRequestedAuth==auth_none) fRemoteRequestedAuth=static_cast(getRootConfig()->fAgentConfigP)->fAssumedServerAuth; if (fRemoteRequestedAuthEnc==fmt_chr) fRemoteRequestedAuthEnc=static_cast(getRootConfig()->fAgentConfigP)->fAssumedServerAuthEnc; if (fRemoteNonce.empty()) fRemoteNonce=static_cast(getRootConfig()->fAgentConfigP)->fAssumedNonce; // we are not yet authenticated for the entire session fNeedAuth=true; // now ready for init fOutgoingState=psta_init; // %%%% could also set psta_initsync for combined init/sync fIncomingState=psta_idle; // remains idle until first answer SyncHdr with OK status is received fInProgress=true; // assume in progress // set session ID string StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)fClientSessionNo); // now we have a session id, can now display debug stuff #ifdef SYDEBUG string t; StringObjTimestamp(t,getSystemNowAs(TCTX_SYSTEM)); PDEBUGPRINTFX(DBG_HOT,("\n[%s] =================> Starting new client session",t.c_str())); #endif // - make sure we are logged into the local database (if needed) status=LocalLogin(); if (status!=LOCERR_OK) return status; // create header for first message no noResp issueHeader(false); } else { // check for proper end of session (caused by MessageEnded analysis) if (!fInProgress) { // give an opportunity to let make outgoing message end and flush xml end message FinishMessage(true, false); // end sync in all datastores (save anchors etc.) PDEBUGPRINTFX(DBG_PROTO,("Successful end of session -> calling engFinishDataStoreSync() for datastores now")); for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) (*pos)->engFinishDataStoreSync(); // successful end PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0")); return LOCERR_OK; // done & ok } } // check expired case #ifdef APP_CAN_EXPIRE if (getClientBase()->fAppExpiryStatus!=LOCERR_OK) { PDEBUGPRINTFX(DBG_ERROR,("Evaluation Version expired - Please contact Synthesis AG for release version")); return getClientBase()->fAppExpiryStatus; // payment required, done & error } #endif if (fOutgoingState==psta_init || fOutgoingState==psta_initsync) { // - if suspended in init, nothing substantial has happened already, so just exit if (isSuspending() && fOutgoingState==psta_init) { AbortSession(fAbortReasonStatus,true); return getAbortReasonStatus(); } // - prepare Alert(s) for databases to sync bool anyfirstsyncs=false; bool anyslowsyncs=false; TLocalEngineDS *localDS; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // prepare alert localDS = *pos; status=localDS->engPrepareClientSyncAlert(NULL); // not as superdatastore if (status!=LOCERR_OK) { // local database error return localError(status); // not found } if (localDS->fFirstTimeSync) anyfirstsyncs=true; if (localDS->fSlowSync) anyslowsyncs=true; } // send devinf in Put command right away with init message if either... // - mustSendDevInf() returns true signalling an external condition that suggests sending devInf (like changed config) // - any datastore is doing first time sync // - fPutDevInfAtSlowSync is true and any datastore is doing slow sync if ( mustSendDevInf() || anyfirstsyncs || (anyslowsyncs && static_cast(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync) ) { TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this); issueRootPtr(putcmdP); } // try to load devinf from cache (only if we don't know it already) if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) { SmlDevInfDevInfPtr_t devinfP; if (loadRemoteDevInf(getRemoteURI(),devinfP)) { // we have cached devinf, analyze it now analyzeRemoteDevInf(devinfP); } } // GET the server's info if server didn't send it and we haven't cached at least the datastores if (!fRemoteDataStoresKnown) { // if we know datastores here, but not types, this means that remote does not have // CTCap, so it makes no sense to issue a GET again. #ifndef NO_DEVINF_GET PDEBUGPRINTFX(DBG_REMOTEINFO,("Nothing known about server, request DevInf using GET command")); TGetCommand *getcommandP = new TGetCommand(this); getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]); string devinftype=SYNCML_DEVINF_META_TYPE; addEncoding(devinftype); getcommandP->setMeta(newMetaType(devinftype.c_str())); ISSUE_COMMAND_ROOT(this,getcommandP); #endif } // - create Alert(s) for databases to sync for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // create alert for non-subdatastores localDS = *pos; if (!localDS->isSubDatastore()) { TAlertCommand *alertcmdP; status=localDS->engGenerateClientSyncAlert(alertcmdP); if (status!=0) { // local database error return status; // not found } ///%%%% unneeded (probably got here by copy&paste accidentally): if (localDS->fFirstTimeSync) anyfirstsyncs=true; // issue alert issueRootPtr(alertcmdP); } } // append sync phase if we have combined init/sync if (fOutgoingState==psta_initsync) fOutgoingState=psta_sync; } // process sync/syncop/map generating phases after init if (!isSuspending()) { // normal, session continues if (fOutgoingState==psta_sync) { // hold back sync until server has finished first package (init or initsync) if (fIncomingState==psta_sync || fIncomingState==psta_initsync) { // start sync for alerted datastores for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // Note: some datastores might be aborted due to unsuccessful alert. if ((*pos)->isActive()) { // prepare engine for sync (%%% new routine in 3.2.0.3, summarizing engInitForSyncOps() and // switching to dssta_dataaccessstarted, i.e. loading sync set), but do in only once if (!((*pos)->testState(dssta_syncsetready))) { // not yet started status = (*pos)->engInitForClientSync(); if (status!=LOCERR_OK) { // failed AbortSession(status,true); return getAbortReasonStatus(); } } // start or continue (which is largely nop, as continuing works via unfinished sync command) // generating sync items (*pos)->engClientStartOfSyncMessage(); } } } } else if (fOutgoingState==psta_map) { // hold back map until server has started sync at least (incominstate >=psta_sync) // NOTE: This is according to the specs, which says that client can begin // with Map/update status package BEFORE sync package from server is // completely received. // NOTE: Starfish server expects this and generates a few 222 alerts // if we wait here, but then goes to map as well // (so (fIncomingState==psta_map)-version works as well here! // %%%% other version: wait until server has started map phase as well // %%%% if (fIncomingState==psta_map) { if (fIncomingState>=psta_sync) { // start map for synced datastores for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // Note: some datastores might be aborted due to unsuccessful alert. if ((*pos)->isActive()) { // now call datastore to generate map command if not already done (*pos)->engClientStartOfMapMessage(fIncomingState just generate a Alert 222 and wait for server to complete PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Client finished so far, but needs to wait in supplement outgoing state until server finishes phase")); } else if (fOutgoingState!=psta_init) { // NOTE: can be psta_init because "if" begins again after psta_init checking // to allow psta_init appending psta_sync for combined init/sync // no known state return 9999; // %%%%% } } // if not suspended // security only: exit here if session got aborted in between if (isAborted()) return getAbortReasonStatus(); // done & error if (!fInProgress) return 9999; // that's fine with us // now, we know that we will (most probably) send a message, so default for aDone is false from now on aDone=false; bool outgoingfinal; // check for suspend if (isSuspending()) { // make sure we send a Suspend Alert TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)224); // - we just put local and remote URIs here SmlItemPtr_t itemP = newItem(); itemP->target = newLocation(fRemoteURI.c_str()); itemP->source = newLocation(fLocalURI.c_str()); alertCmdP->addItem(itemP); ISSUE_COMMAND_ROOT(this,alertCmdP); // outgoing message is final, regardless of any session state outgoingfinal=true; MarkSuspendAlertSent(true); } else { // Determine if package can be final and if we need an 222 Alert // NOTE: if any commands were interruped or not sent due to outgoing message // size limits, FinishMessage() will prevent final anyway, so no // separate checking for enOfSync or endOfMap is needed. // - can finalize message when server has at least started answering current package // OR if this is the first message (probably repeatedly) sent outgoingfinal = fIncomingState >= fOutgoingState || fIncomingState==psta_idle; if (outgoingfinal) { // allow early success here in case of nothing to respond, and nothing pending // StarFish server does need this... if (!fNeedToAnswer) { if (hasPendingCommands()) { // we have pending commands, cannot be final message outgoingfinal=false; } else { // no pending commands -> we're done now PDEBUGPRINTFX(DBG_PROTO,("Early end of session (nothing to send to server any more) -> calling engFinishDataStoreSync() for datastores now")); // - end sync in all datastores (save anchors etc.) for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) (*pos)->engFinishDataStoreSync(); // successful end PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0")); // done & ok aDone=true; return LOCERR_OK; } } } } /* A dummy alert indicates this is a message with only alert222 request*/ bool dummyAlert = false; if (!outgoingfinal) { // - send Alert 222 if we need to continue package but have nothing to send // (or ALWAYS_CONTINUE222 defined) #ifndef ALWAYS_CONTINUE222 if (!fNeedToAnswer) #endif { /* End-less loop detection * Some servers will never end and triggers client sends * ALERT222 forever. Detect this scenario and abort the session if * detected. * It is still valid for the server to use ALERT222 to "keep-alive" the * connection. * Therefore the loop detection criteria is: * - Nothing to send except the 222 Alert (!fNeedToAnswer) * - 5 adjacent 222 alerts within 20 seconds * - no status for an actual sync op command received (fOutgoingAlert222Count will be reset by those) * because a server sending pending status in small chunks could also trigger the detector otherwise */ if (!fNeedToAnswer) { dummyAlert = true; if (fOutgoingAlert222Count++ == 0) { // start of 222 loop detection time fLastOutgoingAlert222 = getSystemNowAs(TCTX_UTC); } else if (fOutgoingAlert222Count > 5) { lineartime_t curTime = getSystemNowAs(TCTX_UTC); if (curTime - fLastOutgoingAlert222 < 20*secondToLinearTimeFactor) { PDEBUGPRINTFX(DBG_ERROR,( "Warning: More than 5 consecutive Alert 222 within 20 seconds- " "looks like endless loop, abort session" )); AbortSession(400, false); return getAbortReasonStatus(); } else { fOutgoingAlert222Count = 0; } } } // not final, and nothing to answer otherwise: create alert-Command to request more info TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222); // %%% not clear from spec what has to be in item for 222 alert code // but there MUST be an Item for the Alert command according to SyncML TK // - we just put local and remote URIs here SmlItemPtr_t itemP = newItem(); itemP->target = newLocation(fRemoteURI.c_str()); itemP->source = newLocation(fLocalURI.c_str()); alertCmdP->addItem(itemP); ISSUE_COMMAND_ROOT(this,alertCmdP); } } // We send a response with no dummy alert, so reset the alert detector if (!dummyAlert) { fOutgoingAlert222Count = 0; } // send custom end-of session puts if (!isSuspending() && outgoingfinal && fOutgoingState==psta_map) { // End of outgoing map package; let custom PUTs which may transmit some session statistics etc. happen now issueCustomEndPut(); } // message complete, now finish it FinishMessage( outgoingfinal, // allowed if possible false // final not prevented ); // Note, now fNewOutgoingPackage is set (by FinishMessage()) // if next message will be responded to with a new package // debug info #ifdef SYDEBUG if (PDEBUGMASK & DBG_SESSION) { PDEBUGPRINTFX(DBG_SESSION,( "---> NextMessage, outgoing state='%s', incoming state='%s'", PackageStateNames[fOutgoingState], PackageStateNames[fIncomingState] )); for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // Show state of local datastores PDEBUGPRINTFX(DBG_SESSION,( "Local Datastore '%s': %sState=%s, %s%s sync, %s%s", (*pos)->getName(), (*pos)->isAborted() ? "ABORTED - " : "", (*pos)->getDSStateName(), (*pos)->isResuming() ? "RESUMED " : "", (*pos)->fSlowSync ? "SLOW" : "normal", SyncModeDescriptions[(*pos)->fSyncMode], (*pos)->fServerAlerted ? ", Server-Alerted" : "" )); } } PDEBUGENDBLOCK("SyncML_Outgoing"); if (getLastIncomingMsgID()>0) { // we have already received an incoming message, so we have started an "SyncML_Incoming" blocks sometime PDEBUGENDBLOCK("SyncML_Incoming"); // terminate debug block of previous incoming message as well } #endif // ok return LOCERR_OK; // ok } // TSyncAgent::NextMessage // called after successful decoding of an incoming message bool TSyncAgent::ClientMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad) { // message not authorized by default fMessageAuthorized=false; // Check information from SyncHdr if ( aBad || (!(fSynchdrSessionID==smlPCDataToCharP(aContentP->sessionID))) || (!(fLocalURI==smlSrcTargLocURIToCharP(aContentP->target))) ) { // bad response PDEBUGPRINTFX(DBG_ERROR,( "Bad SyncHeader from Server. Syntax %s, SessionID (rcvd/correct) = '%s' / '%s', LocalURI (rcvd/correct) = '%s' / '%s'", aBad ? "ok" : "BAD", smlPCDataToCharP(aContentP->sessionID), fSynchdrSessionID.c_str(), smlSrcTargLocURIToCharP(aContentP->target), fLocalURI.c_str() )); aStatusCommand.setStatusCode(400); // bad response/request AbortSession(400,true); return false; } // check for suspend: if we are suspended at this point, this means that we have sent the Suspend Alert already // in the previous message (due to user suspend request), so we can now terminate the session if (isSuspending() && isSuspendAlertSent()) { AbortSession(514,true,LOCERR_USERSUSPEND); return false; } // - RespURI (remote URI to respond to) if (aContentP->respURI) { fRespondURI=smlPCDataToCharP(aContentP->respURI); DEBUGPRINTFX(DBG_PROTO,("RespURI set to = '%s'",fRespondURI.c_str())); } // authorization check // Note: next message will be started not before status for last one // has been processed. Commands issued before will automatically // be queued by issuePtr() // %%% none for now fSessionAuthorized=true; fMessageAuthorized=true; // returns false on BAD header (but true on wrong/bad/missing cred) return true; } // TSyncAgent::ClientMessageStarted // determines new package states and sets fInProgress void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) { TLocalDataStorePContainer::iterator pos; // show status before processing PDEBUGPRINTFX(DBG_SESSION,( "MessageEnded starts : old outgoing state='%s', old incoming state='%s', %sNeedToAnswer", PackageStateNames[fOutgoingState], PackageStateNames[fIncomingState], fNeedToAnswer ? "" : "NO " )); bool allFromClientOnly=false; // process exceptions if (fAborted) { PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session")); fOutgoingState=psta_idle; fIncomingState=psta_idle; fInProgress=false; } // if aborted else if (!fMessageAuthorized) { // not authorized messages will just be ignored, so // nothing changes in states // %%% this will probably not really work, as we would need to repeat the last // message in this (unlikely) case that fMessageAuthorized is not set for // a non-first message (first message case is handled in handleHeaderStatus) DEBUGPRINTFX(DBG_ERROR,("***** received Message not authorized, ignore and DONT end package")); fInProgress=true; } else { fInProgress=true; // assume we need to continue // Note: the map phase will not take place, if all datastores are in // send-to-server-only mode and we are not in non-conformant old // synthesis-compatible fCompleteFromClientOnly mode. if (!fCompleteFromClientOnly) { // let all local datastores know that message has ended allFromClientOnly=true; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // check sync modes if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { allFromClientOnly=false; break; } } } // new outgoing state is determined by the incomingState of this message // (which is the answer to the message of the previous outgoing package) if (fNewOutgoingPackage && fIncomingState!=psta_idle) { // last message sent was an end-of-package, so next will be a new package if (fIncomingState==psta_init) { // server has responded (or is still responding) to our finished init, // so client enters sync state now (but holds back sync until server // has finished init) fOutgoingState=psta_sync; } else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) { // server has started (or already finished) sending statuses for our // or its own // client can enter map state (but holds back maps until server // has finished sync/initsync). In case of allFromClientOnly, we skip the map phase // but only if there is no need to answer. // Otherwise, this is most probably an old (pre 2.9.8.2) Synthesis server that has // sent an empty (and the status for it has set fNeedToAnswer), so we still // go to map phase. if (allFromClientOnly && !fNeedToAnswer) { fOutgoingState=psta_supplement; // all datastores are from-client-only, skip map phase PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode, and no need to answer: skip map phase")); } else { fOutgoingState=psta_map; // Some datastores do from-server-only or twoway, so we need a map phase allFromClientOnly=false; // do not skip map phase } } else { // map is finished as well, we might need extra packages just to // finish getting results for map commands fOutgoingState=psta_supplement; } } // New incoming state is simply derived from the incoming state of // this message if (aIncomingFinal && fIncomingState!=psta_idle) { if (fIncomingState==psta_init) { // switch to sync fIncomingState=psta_sync; } else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) { // check what to do if (allFromClientOnly) { // no need to answer and allFromClientOnly -> this is the end of the session fIncomingState=psta_supplement; fInProgress=false; // normally, at end of map answer, we are done } else { fIncomingState=psta_map; } } else { // end of a map phase - end of session (if no fNeedToAnswer) fIncomingState=psta_supplement; // this only ALLOWS ending the session, but it will continue as long // as more than OK for SyncHdr (fNeedToAnswer) must be sent fInProgress=false; // normally, at end of map answer, we are done } } // continue anyway as long as we need to answer if (fNeedToAnswer) fInProgress=true; } // show states PDEBUGPRINTFX(DBG_HOT,( "MessageEnded finishes : new outgoing state='%s', new incoming state='%s', %sNeedToAnswer", PackageStateNames[fOutgoingState], PackageStateNames[fIncomingState], fNeedToAnswer ? "" : "NO " )); // let all local datastores know that message has ended for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // let them know (*pos)->engEndOfMessage(); // Show state of local datastores PDEBUGPRINTFX(DBG_HOT,( "Local Datastore '%s': %sState=%s, %s%s sync, %s%s", (*pos)->getName(), (*pos)->isAborted() ? "ABORTED - " : "", (*pos)->getDSStateName(), (*pos)->isResuming() ? "RESUMED " : "", (*pos)->isSlowSync() ? "SLOW" : "normal", SyncModeDescriptions[(*pos)->getSyncMode()], (*pos)->fServerAlerted ? ", Server-Alerted" : "" )); } // thread might end here, so stop profiling TP_STOP(fTPInfo); } // TSyncAgent::ClientMessageEnded // get credentials/username to authenticate with remote party, NULL if none SmlCredPtr_t TSyncAgent::newCredentialsForRemote(void) { if (fNeedAuth) { // generate cretentials from username/password PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA,("Authenticating with server as user '%s'", fServerUser.c_str())); PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA+DBG_EXOTIC,("- using nonce '%s'", fRemoteNonce.c_str())); // NOTE: can be NULL when fServerRequestedAuth is auth_none return newCredentials( fServerUser.c_str(), fServerPassword.c_str() ); } else { // already authorized, no auth needed return NULL; } } // TSyncAgent::newCredentialsForRemote // get client base TSyncClientBase *TSyncAgent::getClientBase(void) { return static_cast(getSyncAppBase()); } // TSyncAgent::getClientBase // retry older protocol, returns false if no older protocol to try bool TSyncAgent::retryOlderProtocol(bool aSameVersionRetry, bool aOldMessageInBuffer) { if (fIncomingState==psta_idle) { // if we have not started a session yet and not using oldest protocol already, // we want to retry with next older SyncML version if (aSameVersionRetry) { // just retry same version PDEBUGPRINTFX(DBG_PROTO,("Retrying session start with %s",SyncMLVerProtoNames[fSyncMLVersion])); } else if (fSyncMLVersion>getSessionConfig()->fMinSyncMLVersionSupported) { // next lower fSyncMLVersion=(TSyncMLVersions)(((uInt16)fSyncMLVersion)-1); PDEBUGPRINTFX(DBG_PROTO,("Server does not support our SyncML version, trying with %s",SyncMLVerProtoNames[fSyncMLVersion])); } else { // cannot retry return false; } // retry retryClientSessionStart(aOldMessageInBuffer); return true; } // session already started or no older protocol to try return false; } // TSyncAgent::retryOlderProtocol // prepares client session such that it will do a retry to start a session // (but keeping already received auth/nonce/syncML-Version state) void TSyncAgent::retryClientSessionStart(bool aOldMessageInBuffer) { TAgentConfig *configP = static_cast(getRootConfig()->fAgentConfigP); // now restarting PDEBUGPRINTFX(DBG_HOT,("=================> Retrying Client Session Start")); bool newSessionForAuthRetry = configP->fNewSessionForAuthRetry; bool noRespURIForAuthRetry = configP->fNoRespURIForAuthRetry; // check if we should use modified behaviour (smart retries) if (configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) { if (newSessionForAuthRetry) { // if we had new session for retry, switch to in-session retry now newSessionForAuthRetry = false; noRespURIForAuthRetry = false; } else { // if we had in-session retry, try new session retry now newSessionForAuthRetry = true; noRespURIForAuthRetry = true; } PDEBUGPRINTFX(DBG_PROTO,("Smart retry with modified behaviour: newSessionForAuthRetry=%d, noRespURIForAuthRetry=%d",newSessionForAuthRetry,noRespURIForAuthRetry)); } // now retry if (newSessionForAuthRetry) { // Notes: // - must apparently be disabled for SCTS 3.1.2 and possibly Mightyphone // - must be enabled e.g for for Magically Server // Create new session ID StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)++fClientSessionNo); // restart message counting at 1 fIncomingMsgID=0; fOutgoingMsgID=0; // we must terminate the block here when we reset fIncomingMsgID, as NextMessage // only closes the incoming block when fIncomingMsgID>0 PDEBUGENDBLOCK("SyncML_Incoming"); } if (noRespURIForAuthRetry) { // Notes: // - must apparently be switched on for Starfish. // - must apparently be switched off for SCTS 3.1.2. // make sure we send next msg to the original URL fRespondURI=fRemoteURI; } // - make sure status for SyncHdr will not be generated! forgetHeaderWaitCommands(); // check if we have already started next outgoing message if (!fOutgoingStarted) { if (aOldMessageInBuffer) { // make sure we start with a fresh output buffer // Note: This usually only occur when we are not currently parsing // part of the buffer. If we are parsing, the remaining incoming // message gets cleared as well. getClientBase()->clrUnreadSmlBufferdata(); } // start a new message issueHeader(false); } else { if (aOldMessageInBuffer) { PDEBUGPRINTFX(DBG_ERROR,("Warning - restarting session with old message in output buffer")); } } // - make sure subsequent commands (most probably statuses for Alerts) // don't get processed AbortCommandProcessing(0); // silently discard all further commands // - make sure possible processing errors do not abort the session fIgnoreMsgErrs = true; } // TSyncAgent::retryClientSessionStart #endif // SYSYNC_CLIENT #ifdef SYSYNC_SERVER // undefine these only for tests. Introduced to find problem with T68i #define RESPURI_ONLY_WHEN_NEEDED // create a RespURI string. If none needed, return NULL SmlPcdataPtr_t TSyncAgent::newResponseURIForRemote(void) { // do it in a transport-independent way, therefore let dispatcher do it string respURI; // empty string if (fUseRespURI) { getSyncAppBase()->generateRespURI( respURI, // remains unaffected if no RespURI could be calculated fInitialLocalURI.c_str(), // initial URI used by remote to send first message fLocalSessionID.c_str() // server generated unique session ID ); // Omit RespURI if local URI as seen by client is identical if (getServerConfig()->fRespURIOnlyWhenDifferent) { // create RespURI only if different from original URI if (respURI==fLocalURI) { respURI.erase(); DEBUGPRINTFX(DBG_SESSION,( "Generated RespURI and sourceLocURI are equal (%s)-> RespURI omitted", fLocalURI.c_str() )); } } } // Note: returns NULL if respURI is empty string return newPCDataOptString(respURI.c_str()); } // newResponseURIForRemote // called after successful decoding of an incoming message bool TSyncAgent::ServerMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad) { // message not authorized by default fMessageAuthorized=false; // Get information from SyncHdr which is needed for answers // - session ID to be used for responses fSynchdrSessionID=smlPCDataToCharP(aContentP->sessionID); // - local URI (as seen by remote client) fLocalURI=smlSrcTargLocURIToCharP(aContentP->target); fLocalName=smlSrcTargLocNameToCharP(aContentP->target); // - also remember URI to which first message was sent // %%% note: incoming ID is not a criteria, because it might be >1 due to // client retrying something which it thinks is for the same session //if (fIncomingMsgID==1) { if (fOutgoingMsgID==0) { // this is the first message, remember first URI used to contact server // (or set preconfigured string from ) if (getServerConfig()->fExternalURL.empty()) fInitialLocalURI=fLocalURI; // use what client sends to us else fInitialLocalURI=getServerConfig()->fExternalURL; // use preconfigured URL // Many clients, including SCTS send the second login attempt with a MsgID>1, // and depending on how they handle RespURI, they might get a new session for that // -> so, just handle the case that a new session does not start with MsgID=1 if (fIncomingMsgID>1) { PDEBUGPRINTFX(DBG_ERROR,( "New session gets first message with MsgID=%ld (should be 1). Might be due to retries, adjusting OutgoingID as well", (long)fIncomingMsgID )); fOutgoingMsgID=fIncomingMsgID-1; // to make it match what client expects } } // - remote URI fRemoteURI=smlSrcTargLocURIToCharP(aContentP->source); fRemoteName=smlSrcTargLocNameToCharP(aContentP->source); // - RespURI (remote URI to respond to, if different from source) fRespondURI.erase(); if (aContentP->respURI) { fRespondURI=smlPCDataToCharP(aContentP->respURI); DEBUGPRINTFX(DBG_PROTO,("RespURI specified = '%s'",fRespondURI.c_str())); } if (fRespondURI==fRemoteURI) fRespondURI.erase(); // if specified but equal to remote: act as if not specified // More checking if header was ok if (aBad) { // bad header, only do what is needed to get a status back to client fSessionAuthorized=false; fIncomingState=psta_init; fOutgoingState=psta_init; fNewOutgoingPackage=true; // issue header to make sure status can be sent back to client if (!fMsgNoResp) issueHeader(false); // issue header, do not prevent responses } else { // check busy (or expired) case if (serverBusy()) { #ifdef APP_CAN_EXPIRE if (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK) { aStatusCommand.setStatusCode(511); // server failure (expired) aStatusCommand.addItemString("License expired or invalid"); PDEBUGPRINTFX(DBG_ERROR,("License expired or invalid - Please contact Synthesis AG to obtain license")); } else #endif { aStatusCommand.setStatusCode(101); // busy } issueHeader(false); // issue header, do not prevent responses AbortSession(0,true); // silently discard rest of commands return false; // header not ok } // now check what state we are in if (fIncomingState==psta_idle) { // Initialize // - session-wide authorization not yet there fSessionAuthorized=false; fMapSeen=false; // - session has started, we are processing first incoming // package and generating first outgoing package // (init, possibly changed to combined init/sync by in this package) fIncomingState=psta_init; fOutgoingState=psta_init; fNewOutgoingPackage=true; } // authorization check if (fIncomingState>=psta_init) { // now check authorization if (!fSessionAuthorized) { // started, but not yet permanently authorized fMessageAuthorized=checkCredentials( smlSrcTargLocNameToCharP(aContentP->source), // user name in clear text according to SyncML 1.0.1 aContentP->cred, // actual credentials aStatusCommand ); // NOTE: aStatusCommand has now the appropriate status and chal (set by checkCredentials()) // if credentials do not match, stop processing commands (but stay with the session) if (!fMessageAuthorized) { AbortCommandProcessing(aStatusCommand.getStatusCode()); PDEBUGPRINTFX(DBG_PROTO,("Authorization failed with status %hd, stop command processing",aStatusCommand.getStatusCode())); } // now determine if authorization is permanent or not if (fMessageAuthorized) { fAuthFailures=0; // reset count if (messageAuthRequired()) { // each message needs autorisation again (or no auth at all) // - 200 ok, next message needs authorization again (or again: none) fSessionAuthorized=false; // no permanent authorization aStatusCommand.setStatusCode(200); // - add challenge for next auth (different nonce) aStatusCommand.setChallenge(newSessionChallenge()); PDEBUGPRINTFX(DBG_PROTO,("Authorization ok, but required again for subsequent messages: 200 + chal")); } else { // entire session is authorized fSessionAuthorized=true; // permanent authorization // - 212 authentication accepted (or 200 if none is reqired at all) aStatusCommand.setStatusCode(requestedAuthType()==auth_none ? 200 : 212); // - add challenge for next auth (in next session, but as we support carry // forward via using sessionID, we need to send one here as well) aStatusCommand.setChallenge(newSessionChallenge()); PDEBUGPRINTFX(DBG_PROTO,("Authorization accepted: 212")); } } } // authorisation check else { // already authorized from previous message PDEBUGPRINTFX(DBG_PROTO,("Authorization ok from previous request: 200")); fMessageAuthorized=true; } // Start response message AFTER auth check, to allow issueHeader // to check auth state and customize the header accordingly (no // RespURI for failed auth for example) if (!fMsgNoResp) { issueHeader(false); // issue header, do not prevent responses } } // if started at least } // if not aBad // return startmessage status // debug info #ifdef SYDEBUG if (PDEBUGMASK & DBG_SESSION) { PDEBUGPRINTFX(DBG_SESSION,( "---> MessageStarted, Message %sauthorized, incoming state='%s', outgoing state='%s'", fMessageAuthorized ? "" : "NOT ", PackageStateNames[fIncomingState], PackageStateNames[fOutgoingState] )); TLocalDataStorePContainer::iterator pos; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // Show state of local datastores PDEBUGPRINTFX(DBG_SESSION,( "Local Datastore '%s': State=%s, %s%s sync, %s%s", (*pos)->getName(), (*pos)->getDSStateName(), (*pos)->isResuming() ? "RESUMED " : "", (*pos)->fSlowSync ? "SLOW" : "normal", SyncModeDescriptions[(*pos)->fSyncMode], (*pos)->fServerAlerted ? ", Server-Alerted" : "" )); } } #endif // final check for too many auth failures if (!fMessageAuthorized) { #ifdef NO_NONCE_OLD_BEAHVIOUR AbortSession(aStatusCommand.getStatusCode(),true); // local error // avoid special treatment of non-authorized message, we have aborted, this is enough fMessageAuthorized=true; #else // Unsuccessful auth, count this fAuthFailures++; PDEBUGPRINTFX(DBG_ERROR,( "Authorization failed %hd. time, (any reason), sending status %hd", fAuthFailures, aStatusCommand.getStatusCode() )); // - abort session after too many auth failures if (fAuthFailures>=MAX_AUTH_ATTEMPTS) { PDEBUGPRINTFX(DBG_ERROR,("Too many (>=%hd) failures, aborting session",MAX_AUTH_ATTEMPTS)); AbortSession(400,true); } #endif } // returns false on BAD header (but true on wrong/bad/missing cred) return true; } // TSyncAgent::ServerMessageStarted void TSyncAgent::ServerMessageEnded(bool aIncomingFinal) { bool alldone; TPackageStates newoutgoingstate,newincomingstate; TLocalDataStorePContainer::iterator pos; bool allFromClientOnly=false; // Incoming message ends here - what is following are commands initiated by the server // not directly related to a incoming command. PDEBUGENDBLOCK("SyncML_Incoming"); // assume that outgoing package is NOT finished, so outgoing state does not change newoutgoingstate=fOutgoingState; // new incoming state depends on whether this message is final or not if ((aIncomingFinal || (fIncomingState==psta_supplement)) && fMessageAuthorized) { // Note: in supplement state, incoming final is not relevant (may or may not be present, there is // no next phase anyway // find out if this is a shortened session (no map phase) due to // from-client-only in all datastores if (!fCompleteFromClientOnly) { allFromClientOnly=true; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // check sync modes if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { allFromClientOnly=false; break; } } } // determine what package comes next switch (fIncomingState) { case psta_init : newincomingstate=psta_sync; break; case psta_sync : case psta_initsync : // end of sync phase means end of session if all datastores are in from-client-only mode if (allFromClientOnly) { PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode: don't expect map phase from client")); newincomingstate=psta_supplement; } else { newincomingstate=psta_map; } break; case psta_map : case psta_supplement : // supplement state does not exit automatically // after map, possibly some supplement status/alert 222 messages are needed from client newincomingstate=psta_supplement; break; default: // by default, back to idle newincomingstate=psta_idle; break; } // switch } else { // not final or not authorized: no change in state newincomingstate=fIncomingState; } // show status before processing PDEBUGPRINTFX(DBG_SESSION,( "---> MessageEnded starts : old incoming state='%s', old outgoing state='%s', %sNeedToAnswer", PackageStateNames[fIncomingState], PackageStateNames[fOutgoingState], fNeedToAnswer ? "" : "NO " )); // process if (isAborted()) { // actual aborting has already taken place PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session")); newoutgoingstate=psta_idle; newincomingstate=psta_idle; fInProgress=false; } // if aborted else if (isSuspending()) { // only flagged for suspend - but datastores are not yet aborted, do it now AbortSession(514,true,getAbortReasonStatus()); PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'suspended' -> MessageEnded ends package and session")); newoutgoingstate=psta_idle; newincomingstate=psta_idle; fInProgress=false; } else if (!fMessageAuthorized) { // not authorized messages will just be ignored, no matter if final or not, // so outgoing will NEVER be final on non-authorized messages // %%% before 1.0.4.9, this was fInProgress=true // DEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore and DONT end package, session continues")); // fInProgress=true; PDEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore msg and terminate session")); fInProgress=false; } else { // determine if session continues living or not // - if in other than idle state, session will continue fInProgress = (newincomingstate!=psta_idle) || // if not idle, we'll continue !fMessageAuthorized; // if not authorized, we'll continue as well (retrying auth) // Check if we need to send an Alert 222 to get more messages of this package if (!aIncomingFinal) { // not end of incoming package #ifndef ALWAYS_CONTINUE222 if (!fNeedToAnswer) #endif { #ifdef COMBINE_SYNCANDMAP // %%% make sure session gets to an end in case combined sync/map was used if (fMapSeen && fIncomingState==psta_map && fOutgoingState==psta_map) { DEBUGPRINTFX(DBG_HOT,("********** Incoming, non-final message in (combined)map state needs no answer -> force end of outgoing package")); newoutgoingstate=psta_idle; } else #endif { // detected 222-loop on init here: when we have nothing to answer in init // and nothing is alerted -> break session // %%% not sure if this is always ok if (fIncomingState<=psta_init) { PDEBUGPRINTFX(DBG_ERROR,("############## Looks like if we were looping in an init-repeat loop -> force final")); fInProgress=false; fOutgoingState=psta_idle; } else { // not final, and nothing to answer otherwise: create alert-Command to request more info TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222); // %%% not clear from spec what has to be in item for 222 alert code // but there MUST be an Item for the Alert command according to SyncML TK // - we just put local and remote URIs here SmlItemPtr_t itemP = newItem(); itemP->target = newLocation(fRemoteURI.c_str()); itemP->source = newLocation(fLocalURI.c_str()); alertCmdP->addItem(itemP); ISSUE_COMMAND_ROOT(this,alertCmdP); } } } } else { // end of package, finish processing package if (fIncomingState==psta_init) { // - try to load devinf from cache (only if we don't have both datastores and type info already) if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) { SmlDevInfDevInfPtr_t devinfP; TStatusCommand dummystatus(this); if (loadRemoteDevInf(getRemoteURI(),devinfP)) { // we have cached devinf, analyze it now localstatus sta = analyzeRemoteDevInf(devinfP); PDEBUGPRINTFX(DBG_ERROR,("devInf from Cache could not be analyzed: error=%hd",sta)); } } // - if no DevInf for remote datastores cached or received yet, // issue GET for it now if (!fRemoteDataStoresKnown) { // if we know datastores here, but not types, this means that remote does not have // CTCap, so it makes no sense to issue a GET again. #ifndef NO_DEVINF_GET // end of initialisation package, but datastores not known yet // (=no DevInf Put received) --> ask for devinf now PDEBUGPRINTFX(DBG_REMOTEINFO,("No DevInf received or cached, request DevInf using GET command")); TGetCommand *getcommandP = new TGetCommand(this); getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]); string devinftype=SYNCML_DEVINF_META_TYPE; addEncoding(devinftype); getcommandP->setMeta(newMetaType(devinftype.c_str())); ISSUE_COMMAND_ROOT(this,getcommandP); #endif } } } // make sure syncing local datastores get informed of end-of--message if (fIncomingState==psta_sync || fIncomingState==psta_initsync) { // end of an incoming message of the Sync Package // - let all local datastores know, this is now the time to generate // commands, if needed // Note: if there are SyncEnd commands delayed, this means that this is // not yet the time to start commands. Instead, when all // queued SyncEnd commands are executed later, engEndOfSyncFromRemote() // will be called with the endOfAllSyncCommands flag true instead // of now. for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { (*pos)->engEndOfSyncFromRemote(aIncomingFinal && !delayedSyncEndsPending()); } } // Detect outgoing package state transitions // - init to sync if (fOutgoingState==psta_init && newincomingstate>psta_init) { // new outgoing state is sync. // Note: In combined init&sync mode, sync command received in init state // will set outgoing state from init to init-sync while processing message, // so no transition needs to be detected here newoutgoingstate=psta_sync; } // - sync to map else if ( (fOutgoingState==psta_sync || fOutgoingState==psta_initsync) && // outgoing is sync.. (newincomingstate>=psta_initsync) && // ..and incoming has finished sync !allFromClientOnly // ..and this is not a session with all datastores doing from-client-only ) { // outgoing message belongs to Sync package // - ask all local datastores if they are finished with sync command generation alldone=true; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { alldone = alldone && (*pos)->isSyncDone(); } if (alldone) { // outgoing state changes to map (or supplement if all datastores are from-client-only PDEBUGPRINTFX(DBG_HOT,("All datastores are done with generating ")); newoutgoingstate=psta_map; #ifdef COMBINE_SYNCANDMAP // %%% it seems as if 9210 needs combined Sync/Map package and if (fMapSeen) { // prevent FINAL to be sent at end of message DEBUGPRINTFX(DBG_HOT,("********** Combining outgoing sync and map-response packages into one")); fOutgoingState=psta_map; } #endif } } // - map (or from-client-only sync) to idle else if ( (fOutgoingState==psta_map && newincomingstate==psta_supplement) || (allFromClientOnly && (fOutgoingState==psta_sync || fOutgoingState==psta_initsync)) ) { // we are going back to idle now newoutgoingstate=psta_idle; // session ends if it doesn't need to continue for session-level reasons if (!sessionMustContinue()) { PDEBUGPRINTFX(DBG_HOT,("Session completed, now let datastores terminate all sync operations")); for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // finished with Map: end of sync (*pos)->engFinishDataStoreSync(); // successful } // session ends now fInProgress=false; // let custom PUTs which may transmit some session statistics etc. happen now issueCustomEndPut(); } } // - if no need to answer (e.g. nothing to send back except OK status for SyncHdr), // session is over now (as well) if (!fNeedToAnswer) fInProgress=false; } // else // Now finish outgoing message #ifdef DONT_FINAL_BAD_AUTH_ATTEMPTS // - PREVENT final flag after failed auth attempts if(FinishMessage( fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle !fMessageAuthorized || serverBusy() // busy or unauthorized prevent final flag at any rate )) #else // - DO set final flag after failed auth attempts if(FinishMessage( !fMessageAuthorized || fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle serverBusy() // busy prevents final flag at any rate )) #endif { // outgoing state HAS changed fOutgoingState=newoutgoingstate; } // Now update incoming state fIncomingState=newincomingstate; // show states PDEBUGPRINTFX(DBG_HOT,( "---> MessageEnded finishes : new incoming state='%s', new outgoing state='%s', %sNeedToAnswer", PackageStateNames[fIncomingState], PackageStateNames[fOutgoingState], fNeedToAnswer ? "" : "NO " )); // let all local datastores know that message has ended for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // let them know (*pos)->engEndOfMessage(); // Show state of local datastores PDEBUGPRINTFX(DBG_HOT,( "Local Datastore '%s': State=%s, %s%s sync, %s%s", (*pos)->getName(), (*pos)->getDSStateName(), (*pos)->isResuming() ? "RESUMED " : "", (*pos)->fSlowSync ? "SLOW" : "normal", SyncModeDescriptions[(*pos)->fSyncMode], (*pos)->fServerAlerted ? ", Server-Alerted" : "" )); } // End of outgoing message PDEBUGPRINTFX(DBG_HOT,( "=================> Finished generating outgoing message #%ld, request=%ld", (long)fOutgoingMsgID, (long)getSyncAppBase()->requestCount() )); PDEBUGENDBLOCK("SyncML_Outgoing"); } // TSyncAgent::ServerMessageEnded void TSyncAgent::RequestEnded(bool &aHasData) { // to make sure, finish any unfinished message FinishMessage(true); // final allowed, as this is an out-of-normal-order case anyway // if we need to answer, we have data // - SyncML specs 1.0.1 says that server must always respond, even if message // contains of a Status for the SyncHdr only aHasData=true; // %%% first drafts of 1.0.1 said that SyncHdr Status only messages must not be sent... // aHasData=fNeedToAnswer; // %%% // now let all datastores know that request processing ends here (so they might // prepare for a thread switch) // terminate sync with all datastores TLocalDataStorePContainer::iterator pos; for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { // now let them know that request has ended (*pos)->engRequestEnded(); } } // TSyncAgent::RequestEnded // Called at end of Request, returns true if session must be deleted // returns flag if data to be returned. If response URI was specified // different, it is returned in aRespURI, otherwise aRespURI is empty. bool TSyncAgent::EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes) { // count incoming data fIncomingBytes+=aReqBytes; // let client or server do what is needed if (fMessageRetried) { // Message processing cancelled CancelMessageProcessing(); // Nothing happened // - but count bytes fOutgoingBytes+=fBufferedAnswerSize; PDEBUGPRINTFX(DBG_HOT,( "========= Finished retried request with re-sending buffered answer (session %sin progress), incoming bytes=%ld, outgoing bytes=%ld", fInProgress ? "" : "NOT ", (long)aReqBytes, (long)fBufferedAnswerSize )); aHasData=false; // we do not have data in the sml instance (but we have/had some in the retry re-send buffer) } else { // end request RequestEnded(aHasData); // count bytes fOutgoingBytes+=getOutgoingMessageSize(); PDEBUGPRINTFX(DBG_HOT,( "========= Finished request (session %sin progress), processing time=%ld msec, incoming bytes=%ld, outgoing bytes=%ld", fInProgress ? "" : "NOT ", (long)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) * nanosecondsPerLinearTime / 1000000), (long)aReqBytes, (long)getOutgoingMessageSize() )); // return RespURI (is empty if none specified or equal to message source URI) aRespURI = fRespondURI; } if (!fInProgress) { // terminate datastores here already in case we are not in progress any more // here. If any of the datastores are in progress at this point, this is a // protocol violation, and therefore we return a 400. // Note: resetting the session later will also call TerminateDatastores, but then // with a 408 (which is misleading when the session ends here due to protocol // problem. TerminateDatastores(400); } //%%% moved to happen before end of SyncML_Outgoing //PDEBUGENDBLOCK("SyncML_Incoming"); if (fRequestMinTime>0) { // make sure we spent enough time with this request, if not, artificially extend time // - get number of seconds already spent sInt32 t = (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor); // - delay if needed if (t sleeping %ld seconds", (long)fRequestMinTime, (long)t, (long)fRequestMinTime-t )); CONSOLEPRINTF((" ...delaying response by %ld seconds because requestmintime is set to %ld",fRequestMinTime,fRequestMinTime-t)); sleepLineartime((lineartime_t)(fRequestMinTime-t)*secondToLinearTimeFactor); } } // thread might end here, so stop profiling TP_STOP(fTPInfo); #ifdef SYDEBUG // we are not the main thread any longer getDbgLogger()->DebugThreadOutputDone(); #endif // return true if session is not in progress any more return(!fInProgress); } // TSyncAgent::EndRequest // buffer answer in the session's buffer if transport allows it Ret_t TSyncAgent::bufferAnswer(MemPtr_t aAnswer, MemSize_t aAnswerSize) { // get rid of previous buffered answer if (fBufferedAnswer) delete[] fBufferedAnswer; fBufferedAnswer=NULL; fBufferedAnswerSize=0; // save new answer (if not empty) if (aAnswer && aAnswerSize) { // allocate buffer fBufferedAnswer = new unsigned char[aAnswerSize]; // copy data if (!fBufferedAnswer) return SML_ERR_NOT_ENOUGH_SPACE; memcpy(fBufferedAnswer,aAnswer,aAnswerSize); // save size fBufferedAnswerSize=aAnswerSize; } return SML_ERR_OK; } // TSyncAgent::bufferAnswer // get buffered answer from the session's buffer if there is any void TSyncAgent::getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize) { aAnswer=fBufferedAnswer; aAnswerSize=fBufferedAnswerSize; PDEBUGPRINTFX(DBG_HOT,( "Buffered answer read from session: %ld bytes", fBufferedAnswerSize )); } // TSyncAgent::getBufferedAnswer // returns remaining time for request processing [seconds] sInt32 TSyncAgent::RemainingRequestTime(void) { // if no request timeout specified, use session timeout sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout; // calculate number of remaining seconds return t==0 ? 0x7FFFFFFF : // "infinite" t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor); } // TSyncAgent::RemainingRequestTime // process a Map command in context of server session bool TSyncAgent::processMapCommand( SmlMapPtr_t aMapCommandP, // the map command contents TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors bool &aQueueForLater ) { bool allok=false; // assume not ok localstatus sta; // remember that this session has seen a map command already fMapSeen=true; // Detecting a map command in supplement incomin state indicates a // client like funambol that send to many in pre-map phases // (such as in 222-Alert messages). So we reset the session state back // to incoming/outgoing map to correct this client bug if (fIncomingState==psta_supplement) { // back to map phase, as client apparently IS still in map phase, despite too many // sent PDEBUGPRINTFX(DBG_ERROR,( "Warning: detected command after end of Map phase - buggy client sent too many . Re-entering map phase to compensate" )); fIncomingState=psta_map; fOutgoingState=psta_map; } // find database(s) // - get relative URI of requested database const char *targetdburi = smlSrcTargLocURIToCharP(aMapCommandP->target); TLocalEngineDS *datastoreP = findLocalDataStoreByURI(targetdburi); if (!datastoreP) { // no such local datastore aStatusCommand.setStatusCode(404); // not found } else { // local datastore found // - maps can be processed when we are at least ready for early (chached by client from previous session) maps if (datastoreP->testState(dssta_syncmodestable)) { // datastore is ready PDEBUGBLOCKFMT(("ProcessMap", "Processing items from Map command", "datastore=%s", targetdburi)); allok=true; // assume all ok SmlMapItemListPtr_t nextnode = aMapCommandP->mapItemList; while (nextnode) { POINTERTEST(nextnode->mapItem,("MapItemList node w/o MapItem")); PDEBUGPRINTFX(DBG_HOT,( "Mapping remoteID='%s' to localID='%s'", smlSrcTargLocURIToCharP(nextnode->mapItem->source), smlSrcTargLocURIToCharP(nextnode->mapItem->target) )); sta = datastoreP->engProcessMap( #ifdef DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS smlSrcTargLocURIToCharP(nextnode->mapItem->source), #else relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->source)), #endif relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->target)) ); if (sta!=LOCERR_OK) { PDEBUGPRINTFX(DBG_ERROR,(" Mapping FAILED!")); aStatusCommand.setStatusCode(sta); allok=false; break; } // next mapitem nextnode=nextnode->next; } // while more mapitems // terminate Map command allok=datastoreP->MapFinishAsServer(allok,aStatusCommand); PDEBUGENDBLOCK("ProcessMap"); } else { // we must queue the command for later execution aQueueForLater=true; allok=true; // ok for now, we'll re-execute this later } } // database found return allok; } // TSyncAgent::processMapCommand // get next nonce string top be sent to remote party for subsequent MD5 auth void TSyncAgent::getNextNonce(const char *aDeviceID, string &aNextNonce) { fLastNonce.erase(); if (getServerConfig()->fAutoNonce) { // generate nonce out of source ref and session ID // This scheme can provide nonce carrying forward between // sessions by initializing lastNonce with the srcRef/sessionid-1 // assuming client to use nonce from last session. sInt32 sid; // use current day as nonce varying number sid = time(NULL) / 3600 / 24; generateNonce(fLastNonce,aDeviceID,sid); } else { // get constant nonce (if empty, this is NO nonce) fLastNonce=getServerConfig()->fConstantNonce; } // return new nonce DEBUGPRINTFX(DBG_PROTO,("getNextNonce: created nonce='%s'",fLastNonce.c_str())); aNextNonce=fLastNonce; } // TSyncAgent::getNextNonce // - get nonce string for specified deviceID void TSyncAgent::getAuthNonce(const char *aDeviceID, string &aAuthNonce) { // if no device ID, use session default nonce if (!aDeviceID) { TSyncSession::getAuthNonce(aDeviceID,fLastNonce); } else { // Basic nonce mechanism needing no per-device storage: // - we have no stored last nonce, but we can re-create nonce used // for last session with this device by the used algorithm if (getServerConfig()->fAutoNonce) { if (fLastNonce.empty()) { // none available, produce new one sInt32 sid; // use current day as nonce varying number sid = time(NULL) / 3600 / 24; generateNonce(fLastNonce,aDeviceID,sid); } } else { // return constant nonce fLastNonce=getServerConfig()->fConstantNonce; } } DEBUGPRINTFX(DBG_PROTO,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str())); aAuthNonce=fLastNonce; } // TSyncAgent::getAuthNonce // info about server status bool TSyncAgent::serverBusy(void) { // return flag (which might have been set by some connection // limit code in sessiondispatch). // When app is expired, all server sessions are busy anyway #ifdef APP_CAN_EXPIRE return fSessionIsBusy || (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK); #else return fSessionIsBusy; #endif } // TSyncAgent::serverBusy // access to config TAgentConfig *TSyncAgent::getServerConfig(void) { TAgentConfig *scP; GET_CASTED_PTR(scP,TAgentConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TAgentConfig","ss1")); return scP; } // TSyncAgent::getServerConfig #endif // SYSYNC_SERVER // info about requested auth type TAuthTypes TSyncAgent::requestedAuthType(void) { if (IS_SERVER) { #ifdef SYSYNC_SERVER return getServerConfig()->fRequestedAuth; #endif } else { return auth_none; // client does not require auth } } // TSyncAgent::requestedAuthType // check if auth type is allowed bool TSyncAgent::isAuthTypeAllowed(TAuthTypes aAuthType) { if (IS_SERVER) { #ifdef SYSYNC_SERVER return aAuthType>=getServerConfig()->fRequiredAuth; #endif } else { return true; // client accepts any auth } } // TSyncAgent::isAuthTypeAllowed // called when incoming SyncHdr fails to execute bool TSyncAgent::syncHdrFailure(bool aTryAgain) { if (IS_CLIENT) { // do not try to re-execute the header, just let message processing fail; // this will cause the client's main loop to try using an older protocol return false; } else { #ifdef SYSYNC_SERVER if (!aTryAgain) { // not already retried executing // special case: header failed to execute, this means that session must be reset // - Reset session (aborts all DB transactions etc.) ResetSession(); PDEBUGPRINTFX(DBG_ERROR,("Trying to recover SyncHdr failure: =========== Session restarted =====================")); // - now all session infos are gone except this command which is owned by // this function alone. Execute it again. aTryAgain=true; } else { // special special case: header failed to execute the second time DEBUGPRINTFX(DBG_ERROR,("Fatal internal problem, SyncHdr execution failed twice")); aTryAgain=false; // just to make sure SYSYNC_THROW(TSyncException("SyncHdr fatal execution problem")); } return aTryAgain; #endif } } // TSyncAgent::syncHdrFailure // handle status received for SyncHdr, returns false if not handled bool TSyncAgent::handleHeaderStatus(TStatusCommand *aStatusCmdP) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT TAgentConfig *configP = static_cast(getRootConfig()->fAgentConfigP); bool handled=true; const char *txt; SmlMetInfMetInfPtr_t chalmetaP=NULL; SmlChalPtr_t chalP; // first evaluate possible challenge in header status chalP = aStatusCmdP->getStatusElement()->chal; if (chalP) { chalmetaP = smlPCDataToMetInfP(chalP->meta); if (chalmetaP) { sInt16 ty; // - get auth type if (!chalmetaP->type) AbortSession(401,true); // missing auth, but no type txt = smlPCDataToCharP(chalmetaP->type); PDEBUGPRINTFX(DBG_PROTO,("Remote requests auth type='%s'",txt)); if (StrToEnum(authTypeSyncMLNames,numAuthTypes,ty,txt)) fRemoteRequestedAuth=(TAuthTypes)ty; else { AbortSession(406,true); // unknown auth type, not supported goto donewithstatus; } // - get auth format if (!smlPCDataToFormat(chalmetaP->format, fRemoteRequestedAuthEnc)) { AbortSession(406,true); // unknown auth format, not supported goto donewithstatus; } // - get next nonce if (chalmetaP->nextnonce) { // decode B64 uInt32 l; uInt8 *nonce = b64::decode(smlPCDataToCharP(chalmetaP->nextnonce), 0, &l); fRemoteNonce.assign((char *)nonce,l); sysync_free(nonce); } // - show PDEBUGPRINTFX(DBG_PROTO,( "Next Cred will have type='%s' and format='%s' and use nonce='%s'", authTypeNames[fRemoteRequestedAuth], encodingFmtNames[fRemoteRequestedAuthEnc], fRemoteNonce.c_str() )); } /* %%% do not save here already, we don't know if SyncML version is ok moved to those status code cases below that signal // let descendant possibly save auth params saveRemoteParams(); */ } // now evaluate status code switch (aStatusCmdP->getStatusCode()) { case 101: // Busy // Abort AbortSession(101,false); break; case 212: // authentication accepted for entire session fNeedAuth=false; // no need for further auth PDEBUGPRINTFX(DBG_PROTO,("Remote accepted authentication for entire session")); case 200: // authentication accepted for this message // if this is the first authorized message we get an OK for the synchdr, this is // also the first incoming message that is really processed as init message if (fIncomingState==psta_idle && fMessageAuthorized) { // first incoming is expected to be same as first outgoing (init or initsync) fIncomingState=fOutgoingState; PDEBUGPRINTFX(DBG_PROTO,("Authenticated successfully with remote server")); } else { PDEBUGPRINTFX(DBG_PROTO,("Authentication with server ok for this message")); } // let descendant possibly save auth params saveRemoteParams(); break; case 501: // handle a "command not implemented" for the SyncHdr like 513 (indication that server does not like our header) case 400: // ..and 400 as well (sync4j case, as it seems) case 513: // bad protocol version case 505: // bad DTD version (NextHaus/DeskNow case) // try with next lower protocol PDEBUGENDBLOCK("processStatus"); // done processing status if (!retryOlderProtocol()) { // no older SyncML protocol we can try --> abort AbortSession(513,false); // server does not know any of our SyncML versions } break; case 401: // bad authentication // Bad authorisation if (fAuthRetries==0) // if first attempt is rejected with "bad", we conclude that the // last attempt was carrying auth data and was not a attempt to get challenge // from server. Therefore we count this as two tries (one get chal, one really failing) fAuthRetries=2; else fAuthRetries++; // just count attempt to auth /* %%% no longer required, is tested below at authfail: if (fAuthRetries>MAX_AUTH_RETRIES) { AbortSession(401,false); // abort session, too many retries break; } */ // Treat no nonce like empty nonce to make sure that a server (like SySync old versions...) // that does not send a nonce at all does not get auth with some old, invalid nonce string included. if (chalmetaP && chalmetaP->nextnonce==NULL) fRemoteNonce.erase(); // otherwise treat like 407 goto authfail; case 407: // authentication required // new since 2.0.4.6: count this as well (normally this happens once when sending // no auth to the server to force it to send us auth chal first). fAuthRetries++; authfail: PDEBUGPRINTFX(DBG_ERROR,("Authentication failed (status=%hd) with remote server",aStatusCmdP->getStatusCode())); // Auth fail after we have received a valid response for the init message indicates protocol messed up if (fIncomingState!=psta_idle) { AbortSession(400,true); // error in protocol handling from remote break; } // Check if smart retries (with modified in-session vs out-of-session behaviour) are enabled if (!configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) { fAuthRetries = MAX_SMART_AUTH_RETRIES+1; // skip additional smart retries } // Missing or bad authorisation, evaluate chal if (!chalmetaP || fAuthRetries>MAX_SMART_AUTH_RETRIES) { #ifdef SYDEBUG if (!chalmetaP) { PDEBUGPRINTFX(DBG_ERROR,("Bad auth but no challenge in response status -> can't work - no retry")); } #endif AbortSession(aStatusCmdP->getStatusCode(),false); // retries exhausted or no retry possible (no chal) -> stop session break; } // let descendant possibly save auth params saveRemoteParams(); // modify session for re-start PDEBUGENDBLOCK("processStatus"); // done processing status retryClientSessionStart(false); // no previously sent message in the buffer break; default: handled=false; // could not handle status } // switch donewithstatus: // Anyway, reception of status for header enables generation of next message header // (plus already generated commands such as status for response header) if (!fMsgNoResp && !isAborted()) { // issue header now if not already issued above if (!fOutgoingStarted) { // interrupt status processing block here as issueHeader will do a start-of-message PDEBUGBLOCK PDEBUGENDBLOCK("processStatus"); issueHeader(false); PDEBUGBLOCKDESC("processStatus","finishing processing incoming SyncHdr Status"); } } // return handled status return handled; #endif // SYSYNC_SERVER } else { // nothing special return inherited::handleHeaderStatus(aStatusCmdP); } } // TSyncAgent::handleHeaderStatus // - start sync group (called in client or server roles) bool TSyncAgent::processSyncStart( SmlSyncPtr_t aSyncP, // the Sync element TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors bool &aQueueForLater // will be set if command must be queued for later (re-)execution ) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT if (fIncomingState!=psta_sync && fIncomingState!=psta_initsync) { aStatusCommand.setStatusCode(403); // forbidden in this context PDEBUGPRINTFX(DBG_ERROR,("Sync command not allowed outside of sync phase (-> 403)")); AbortSession(400,true); return false; } // just find appropriate database, must be already initialized for sync! // determine local database to sync with (target) TLocalEngineDS *datastoreP = findLocalDataStoreByURI(smlSrcTargLocURIToCharP(aSyncP->target)); if (!datastoreP) { // no such local datastore PDEBUGPRINTFX(DBG_ERROR,("Sync command for unknown DS locURI '%s' (-> 404)",smlSrcTargLocURIToCharP(aSyncP->target))); aStatusCommand.setStatusCode(404); // not found return false; } else { // save the pointer, will e.g. be used to route subsequent server commands fLocalSyncDatastoreP=datastoreP; // let local datastore know return fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater); } return true; #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER // Init datastores for sync localstatus sta = initSync( smlSrcTargLocURIToCharP(aSyncP->target), // local datastore smlSrcTargLocURIToCharP(aSyncP->source) // remote datastore ); if (sta!=LOCERR_OK) { aStatusCommand.setStatusCode(sta); return false; } // let local datastore prepare for sync as server // - let local process sync command bool ok=fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater); // Note: ok means that the sync command is addressing existing datastores. However, // it does not mean that the actual processing is already executed; aQueueForLater // could be set! // if ok and not queued: update package states if (ok) { if (fIncomingState==psta_init || fIncomingState==psta_initsync) { // detected sync command in init package -> this is combined init/sync #ifdef SYDEBUG if (fIncomingState==psta_init) DEBUGPRINTFX(DBG_HOT,(" started init package -> switching to combined init/sync")); #endif // - set new incoming state fIncomingState=psta_initsync; // - also update outgoing state, if it is in init package if (fOutgoingState==psta_init) fOutgoingState=psta_initsync; } else if (fCmdIncomingState!=psta_sync) { DEBUGPRINTFX(DBG_ERROR,( " found in wrong incoming package state '%s' -> aborting session", PackageStateNames[fCmdIncomingState] )); aStatusCommand.setStatusCode(403); // forbidden fLocalSyncDatastoreP->engAbortDataStoreSync(403,true); // abort, local problem ok=false; } else { // - show sync start DEBUGPRINTFX(DBG_HOT,( " started, cmd-incoming state='%s', incoming state='%s', outgoing state='%s'", PackageStateNames[fCmdIncomingState], PackageStateNames[fIncomingState], PackageStateNames[fOutgoingState] )); } } return ok; #endif // SYSYNC_SERVER } } // TSyncAgent::processSyncStart #ifdef ENGINEINTERFACE_SUPPORT // Support for EngineModule common interface // ========================================= /// @brief Get new session key to access details of this session appPointer TSyncAgent::newSessionKey(TEngineInterface *aEngineInterfaceP) { return new TAgentParamsKey(aEngineInterfaceP,this); } // TSyncAgent::newSessionKey #ifdef ENGINE_LIBRARY TSyError TSyncAgent::SessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { if (IS_CLIENT) { #ifdef SYSYNC_CLIENT return ClientSessionStep(aStepCmd,aInfoP); #endif // SYSYNC_CLIENT } else { #ifdef SYSYNC_SERVER return ServerSessionStep(aStepCmd,aInfoP); #endif // SYSYNC_SERVER } } // TSyncAgent::SessionStep #endif // ENGINE_LIBRARY #ifdef SYSYNC_SERVER // Server implementation // --------------------- #ifndef ENGINE_LIBRARY // dummy server engine support to allow AsKey from plugins #warning "using ENGINEINTERFACE_SUPPORT in old-style appbase-rooted environment. Should be converted to real engine usage later" // Engine factory function for non-Library case ENGINE_IF_CLASS *newServerEngine(void) { // For real engine based targets, newServerEngine must create a target-specific derivate // of the server engine, which then has a suitable newSyncAppBase() method to create the // appBase. For old-style environment, a generic TServerEngineInterface is ok, as this // in turn calls the global newSyncAppBase() which then returns the appropriate // target specific appBase. Here we just return a dummy server engine base. return new TDummyServerEngineInterface; } // newServerEngine /// @brief returns a new application base. TSyncAppBase *TDummyServerEngineInterface::newSyncAppBase(void) { // For not really engine based targets, the appbase factory function is // a global routine (for real engine targets, it is a true virtual of // the engineInterface, implemented in the target's leaf engineInterface derivate. // - for now, use the global appBase creator routine return sysync::newSyncAppBase(); // use global factory function } // TDummyServerEngineInterface::newSyncAppBase #else // old style // Real server engine support /// @brief Executes next step of the session /// @param aStepCmd[in/out] step command (STEPCMD_xxx): /// - tells caller to send or receive data or end the session etc. /// - instructs engine to abort or time out the session etc. /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { uInt16 stepCmdIn = aStepCmd; localstatus sta = LOCERR_WRONGUSAGE; // init default response aStepCmd = STEPCMD_ERROR; // error if (aInfoP) { aInfoP->eventtype=PEV_NOP; aInfoP->targetID=0; aInfoP->extra1=0; aInfoP->extra2=0; aInfoP->extra3=0; } // if session is already aborted, no more steps are required if (isAborted()) { fServerEngineState = ses_done; // we are done } // handle pre-processed step command according to current engine state switch (fServerEngineState) { // Done state case ses_done : // session done, nothing happens any more aStepCmd = STEPCMD_DONE; sta = LOCERR_OK; break; // Waiting for SyncML request data case ses_needdata: switch (stepCmdIn) { case STEPCMD_GOTDATA : { // got data, check content type MemPtr_t data = NULL; smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &fRequestSize); // get request size SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, fRequestSize); if (getEncoding()==SML_UNDEF) { // no encoding known so far - use what we found from looking at data PDEBUGPRINTFX(DBG_ERROR,( "Incoming data had no or invalid content type, Determined encoding by looking at data: %s", SyncMLEncodingNames[enc] )); setEncoding(enc); } else if (getEncoding()!=enc) { // already known encoding does not match actual encoding PDEBUGPRINTFX(DBG_ERROR,( "Warning: Incoming data encoding mismatch: expected=%s, found=%s", SyncMLEncodingNames[getEncoding()], SyncMLEncodingNames[enc] )); } if (getEncoding()==SML_UNDEF) { // if session encoding is still unknown at this point, reject data as non-SyncML PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML")); sta = LOCERR_BADCONTENT; // bad content type aStepCmd = STEPCMD_ERROR; // Note: we do not abort the session here - app could have a retry strategy and re-enter // this step with better data break; } // content type ok - switch to processing mode fServerEngineState = ses_processing; aStepCmd = STEPCMD_OK; sta = LOCERR_OK; break; } } // switch stepCmdIn for ces_processing break; // Waiting until SyncML answer data is sent // (only when session needs to continue, otherwise we are in ses_done) case ses_dataready: switch (stepCmdIn) { case STEPCMD_SENTDATA : // sent data, now wait for next request fServerEngineState = ses_needdata; aStepCmd = STEPCMD_NEEDDATA; sta = LOCERR_OK; break; } // switch stepCmdIn for ces_processing break; // Ready for generation steps case ses_generating: switch (stepCmdIn) { case STEPCMD_STEP : sta = ServerGeneratingStep(aStepCmd,aInfoP); break; } // switch stepCmdIn for ces_generating break; // Ready for processing steps case ses_processing: switch (stepCmdIn) { case STEPCMD_STEP : sta = ServerProcessingStep(aStepCmd,aInfoP); break; } // switch stepCmdIn for ces_processing break; case numServerEngineStates: // invalid break; } // switch fServerEngineState // done return sta; } // TSyncAgent::ServerSessionStep // Step that processes SyncML request data TSyError TSyncAgent::ServerProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { localstatus sta = LOCERR_WRONGUSAGE; InstanceID_t myInstance = getSmlWorkspaceID(); Ret_t rc; // now process next command PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)")); #ifdef SYDEBUG MemPtr_t data = NULL; MemSize_t datasize; smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize); #endif rc=smlProcessData( myInstance, SML_NEXT_COMMAND ); if (rc==SML_ERR_CONTINUE) { // processed ok, but message not completely processed yet // - engine state remains as is aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step sta = LOCERR_OK; } else if (rc==SML_ERR_OK) { // message completely processed // - switch engine state to generating answer message (if any) aStepCmd = STEPCMD_OK; fServerEngineState = ses_generating; sta = LOCERR_OK; } else { // processing failed PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc)); // dump the message that failed to process #ifdef SYDEBUG if (data) DumpSyncMLBuffer(data,datasize,false,rc); #endif // abort the session (causing proper error events to be generated and reported back) AbortSession(LOCERR_PROCESSMSG, true); // session is now done fServerEngineState = ses_done; // step by itself is ok - let app continue stepping (to restart session or complete abort) aStepCmd = STEPCMD_OK; sta = LOCERR_OK; } // done return sta; } // TSyncAgent::ServerProcessingStep // Step that generates (rest of) SyncML answer data at end of request TSyError TSyncAgent::ServerGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { bool done, hasdata; string respURI; // finish request done = EndRequest(hasdata, respURI, fRequestSize); // check different exit points if (hasdata) { // there is data to be sent aStepCmd = STEPCMD_SENDDATA; fServerEngineState = ses_dataready; } else { // no more data to send aStepCmd = STEPCMD_OK; // need one more step to finish } // in any case, if done, all susequent steps will return STEPCMD_DONE if (done) { // Session is done TerminateSession(); // subsequent steps will all return STEPCMD_DONE fServerEngineState = ses_done; } // request reset fRequestSize = 0; // return status return LOCERR_OK; } // TSyncAgent::ServerGeneratingStep #endif // ENGINE_LIBRARY #endif // SYSYNC_SERVER #ifdef SYSYNC_CLIENT #ifdef ENGINE_LIBRARY // Client implementation // --------------------- /// @brief Executes next step of the session /// @param aStepCmd[in/out] step command (STEPCMD_xxx): /// - tells caller to send or receive data or end the session etc. /// - instructs engine to suspend or abort the session etc. /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure TSyError TSyncAgent::ClientSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { uInt16 stepCmdIn = aStepCmd; localstatus sta = LOCERR_WRONGUSAGE; // init default response aStepCmd = STEPCMD_ERROR; // error if (aInfoP) { aInfoP->eventtype=PEV_NOP; aInfoP->targetID=0; aInfoP->extra1=0; aInfoP->extra2=0; aInfoP->extra3=0; } // if session is already aborted, no more steps are required if (isAborted()) { fClientEngineState = ces_done; // we are done } // handle pre-processed step command according to current engine state switch (fClientEngineState) { // Idle state case ces_done : { // session done, nothing happens any more aStepCmd = STEPCMD_DONE; sta = LOCERR_OK; break; } case ces_idle : { // in idle, we can only start a session switch (stepCmdIn) { case STEPCMD_CLIENTSTART: case STEPCMD_CLIENTAUTOSTART: // initialize a new session sta = InitializeSession(fProfileSelectorInternal,stepCmdIn==STEPCMD_CLIENTAUTOSTART); if (sta!=LOCERR_OK) break; // engine is now ready, start generating first request fClientEngineState = ces_generating; // ok with no status aStepCmd = STEPCMD_OK; break; } // switch stepCmdIn for ces_idle break; } // Ready for generation steps case ces_generating: { switch (stepCmdIn) { case STEPCMD_STEP : sta = ClientGeneratingStep(aStepCmd,aInfoP); break; } // switch stepCmdIn for ces_generating break; } // Ready for processing steps case ces_processing: { switch (stepCmdIn) { case STEPCMD_STEP : sta = ClientProcessingStep(aStepCmd,aInfoP); break; } // switch stepCmdIn for ces_processing break; } // Waiting for SyncML data case ces_needdata: { switch (stepCmdIn) { case STEPCMD_GOTDATA : { // got data, now start processing it OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_recvend,NULL,0,0,0); // check content type now MemPtr_t data = NULL; MemSize_t datasize; smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize); // check content type SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, datasize); if (enc!=getEncoding()) { PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML")); sta = LOCERR_BADCONTENT; // bad content type #ifdef SYDEBUG if (data) DumpSyncMLBuffer(data,datasize,false,SML_ERR_UNSPECIFIC); #endif // abort the session (causing proper error events to be generated and reported back) AbortSession(sta, true); // session is now done fClientEngineState = ces_done; aStepCmd = STEPCMD_ERROR; break; } // content type ok - switch to processing mode fIgnoreMsgErrs=false; // do not ignore errors by default fClientEngineState = ces_processing; aStepCmd = STEPCMD_OK; sta = LOCERR_OK; break; } case STEPCMD_RESENDDATA : // instead of having received new data, the network layer has found it needs to re-send the data. // performing the STEPCMD_RESENDDATA just generates a new send start event, but otherwise no engine action fClientEngineState = ces_resending; aStepCmd = STEPCMD_RESENDDATA; // return the same step command, to differentiate it from STEPCMD_SENDDATA OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0); sta = LOCERR_OK; break; } // switch stepCmdIn for ces_needdata break; } // Waiting until SyncML data is sent case ces_dataready: case ces_resending: { switch (stepCmdIn) { case STEPCMD_SENTDATA : // allowed in dataready or resending state // sent (or re-sent) data, now request answer data OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendend,NULL,0,0,0); fClientEngineState = ces_needdata; aStepCmd = STEPCMD_NEEDDATA; sta = LOCERR_OK; break; } // switch stepCmdIn for ces_dataready break; } case numClientEngineStates: { // invalid break; } } // switch fClientEngineState // done return sta; } // TSyncAgent::ClientSessionStep // Step that generates SyncML data TSyError TSyncAgent::ClientGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { localstatus sta = LOCERR_WRONGUSAGE; bool done; //%%% at this time, generate next message in one step sta = NextMessage(done); if (done) { // done with session, with or without error fClientEngineState = ces_done; // blocks any further activity with the session aStepCmd = STEPCMD_DONE; // terminate session to provoke all end-of-session progress events TerminateSession(); } else if (sta==LOCERR_OK) { // finished generating outgoing message // - make sure read pointer is set (advanced in case incoming // message had trailing garbage) to beginning of generated // answer. With incoming message being clean SyncML without // garbage, this call is not needed, however with garbage // it is important because otherwise outgoing message // would have that garbage inserted before actual message // start. smlReadOutgoingAgain(getSmlWorkspaceID()); // next is sending request to server fClientEngineState = ces_dataready; aStepCmd = STEPCMD_SENDDATA; OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0); } // return status return sta; } // TSyncAgent::ClientGeneratingStep // Step that processes SyncML data TSyError TSyncAgent::ClientProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP) { InstanceID_t myInstance = getSmlWorkspaceID(); Ret_t rc; localstatus sta = LOCERR_WRONGUSAGE; // now process next command PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)")); #ifdef SYDEBUG MemPtr_t data = NULL; MemSize_t datasize; smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize); #endif rc=smlProcessData( myInstance, SML_NEXT_COMMAND ); if (rc==SML_ERR_CONTINUE) { // processed ok, but message not completely processed yet // - engine state remains as is aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step sta = LOCERR_OK; } else if (rc==SML_ERR_OK) { // message completely processed // - switch engine state to generating next message (if any) aStepCmd = STEPCMD_OK; fClientEngineState = ces_generating; sta = LOCERR_OK; } else { // processing failed PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc)); // dump the message that failed to process #ifdef SYDEBUG if (data) DumpSyncMLBuffer(data,datasize,false,rc); #endif if (!fIgnoreMsgErrs) { // abort the session (causing proper error events to be generated and reported back) AbortSession(LOCERR_PROCESSMSG, true); // session is now done fClientEngineState = ces_done; } else { // we must ignore errors e.g. because of session restart and go back to generate next message fClientEngineState = ces_generating; } // anyway, step by itself is ok - let app continue stepping (to restart session or complete abort) aStepCmd = STEPCMD_OK; sta = LOCERR_OK; } // now check if this is a session restart if (sta==LOCERR_OK && isStarting()) { // this is still the beginning of a session, which means // that we are restarting the session and caller should close // possibly open communication with the server before sending the next message aStepCmd = STEPCMD_RESTART; } // done return sta; } // TSyncAgent::ClientProcessingStep #endif // ENGINE_LIBRARY #endif // SYSYNC_CLIENT // Session runtime settings key // --------------------------- // Constructor TAgentParamsKey::TAgentParamsKey(TEngineInterface *aEngineInterfaceP, TSyncAgent *aAgentP) : inherited(aEngineInterfaceP,aAgentP), fAgentP(aAgentP) { } // TAgentParamsKey::TAgentParamsKey // - read local session ID static TSyError readLocalSessionID( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); return TStructFieldsKey::returnString( mykeyP->fAgentP->getLocalSessionID(), aBuffer,aBufSize,aValSize ); } // readLocalSessionID // - read initial local URI static TSyError readInitialLocalURI( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); return TStructFieldsKey::returnString( mykeyP->fAgentP->getInitialLocalURI(), aBuffer,aBufSize,aValSize ); } // readInitialLocalURI // - read abort status static TSyError readAbortStatus( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); return TStructFieldsKey::returnInt( mykeyP->fAgentP->getAbortReasonStatus(), sizeof(TSyError), aBuffer,aBufSize,aValSize ); } // readAbortStatus // - write abort status, which means aborting a session TSyError writeAbortStatus( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, cAppPointer aBuffer, memSize aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); // abort the session TSyError sta = *((TSyError *)aBuffer); mykeyP->fAgentP->AbortSession(sta, true); return LOCERR_OK; } // writeAbortStatus // - read content type string static TSyError readContentType( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); string contentType = SYNCML_MIME_TYPE; mykeyP->fAgentP->addEncoding(contentType); return TStructFieldsKey::returnString( contentType.c_str(), aBuffer,aBufSize,aValSize ); } // readContentType // - write content type string static TSyError writeContentType( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, cAppPointer aBuffer, memSize aValSize ) { string contentType((cAppCharP)aBuffer,aValSize); TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); mykeyP->fAgentP->setEncoding(TSyncAppBase::encodingFromContentType(contentType.c_str())); return LOCERR_OK; } // writeContentType // - read connection URL static TSyError readConnectURI( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); return TStructFieldsKey::returnString( mykeyP->fAgentP->getSendURI(), aBuffer,aBufSize,aValSize ); } // readConnectURI // - read host part of connection URL static TSyError readConnectHost( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); string host; splitURL(mykeyP->fAgentP->getSendURI(),NULL,&host,NULL,NULL,NULL); return TStructFieldsKey::returnString( host.c_str(), aBuffer,aBufSize,aValSize ); } // readConnectHost // - read document part of connection URL static TSyError readConnectDoc( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); string doc; splitURL(mykeyP->fAgentP->getSendURI(),NULL,NULL,&doc,NULL,NULL); return TStructFieldsKey::returnString( doc.c_str(), aBuffer,aBufSize,aValSize ); } // readConnectDoc // - time when session was last used static TSyError readLastUsed( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); // return it return TStructFieldsKey::returnLineartime(mykeyP->fAgentP->getSessionLastUsed(), aBuffer, aBufSize, aValSize); } // readLastUsed // - server only: check session timeout static TSyError readTimedOut( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); // check if session has timed out bool timedout = mykeyP->fAgentP->getSessionLastUsed()+mykeyP->fAgentP->getSessionConfig()->getSessionTimeout() < mykeyP->fAgentP->getSystemNowAs(TCTX_UTC); // return it return TStructFieldsKey::returnInt(timedout, sizeof(bool), aBuffer, aBufSize, aValSize); } // readTimedOut #ifdef SYSYNC_SERVER // - server only: read respURI enable flag static TSyError readSendRespURI( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); return TStructFieldsKey::returnInt(mykeyP->fAgentP->fUseRespURI, sizeof(bool), aBuffer, aBufSize, aValSize); } // readSendRespURI // - write respURI enable flag static TSyError writeSendRespURI( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, cAppPointer aBuffer, memSize aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); mykeyP->fAgentP->fUseRespURI = *((uInt8P)aBuffer); return LOCERR_OK; } // writeSendRespURI #endif // SYSYNC_SERVER #ifdef SYSYNC_CLIENT // - write (volatile, write-only) password for running this session // (for cases where we don't want to rely on binfile storage for sensitive password data) TSyError writeSessionPassword( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, cAppPointer aBuffer, memSize aValSize ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); mykeyP->fAgentP->setServerPassword((cAppCharP)aBuffer, aValSize); return LOCERR_OK; } // writeSessionPassword #ifdef ENGINE_LIBRARY // - read display alert static TSyError readDisplayAlert( TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, appPointer aBuffer, memSize aBufSize, memSize &aValSize ) { TClientEngineInterface *clientEngineP = static_cast(aStructFieldsKeyP->getEngineInterface()); return TStructFieldsKey::returnString( clientEngineP->fAlertMessage.c_str(), aBuffer,aBufSize,aValSize ); } // readDisplayAlert #endif #endif // SYSYNC_CLIENT // accessor table for server session key static const TStructFieldInfo ServerParamFieldInfos[] = { // valName, valType, writable, fieldOffs, valSiz { "localSessionID", VALTYPE_TEXT, false, 0, 0, &readLocalSessionID, NULL }, { "initialLocalURI", VALTYPE_TEXT, false, 0, 0, &readInitialLocalURI, NULL }, { "abortStatus", VALTYPE_INT16, true, 0, 0, &readAbortStatus, &writeAbortStatus }, { "contenttype", VALTYPE_TEXT, true, 0, 0, &readContentType, &writeContentType }, { "connectURI", VALTYPE_TEXT, false, 0, 0, &readConnectURI, NULL }, { "connectHost", VALTYPE_TEXT, false, 0, 0, &readConnectHost, NULL }, { "connectDoc", VALTYPE_TEXT, false, 0, 0, &readConnectDoc, NULL }, { "timedout", VALTYPE_INT8, false, 0, 0, &readTimedOut, NULL }, { "lastused", VALTYPE_TIME64, false, 0, 0, &readLastUsed, NULL }, #ifdef SYSYNC_SERVER { "sendrespuri", VALTYPE_INT8, true, 0, 0, &readSendRespURI, &writeSendRespURI }, #endif #ifdef SYSYNC_CLIENT { "sessionPassword", VALTYPE_TEXT, true, 0, 0, NULL, &writeSessionPassword }, #ifdef ENGINE_LIBRARY { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL }, #endif #endif }; // get table describing the fields in the struct const TStructFieldInfo *TAgentParamsKey::getFieldsTable(void) { return ServerParamFieldInfos; } // TAgentParamsKey::getFieldsTable sInt32 TAgentParamsKey::numFields(void) { return sizeof(ServerParamFieldInfos)/sizeof(TStructFieldInfo); } // TAgentParamsKey::numFields // get actual struct base address uInt8P TAgentParamsKey::getStructAddr(void) { // prepared for accessing fields in server session object return (uInt8P)fAgentP; } // TAgentParamsKey::getStructAddr // open subkey by name (not by path!) TSyError TAgentParamsKey::OpenSubKeyByName( TSettingsKeyImpl *&aSettingsKeyP, cAppCharP aName, stringSize aNameSize, uInt16 aMode ) { #ifdef DBAPI_TUNNEL_SUPPORT if (strucmp(aName,"tunnel",aNameSize)==0) { // get tunnel datastore pointer TLocalEngineDS *ds = fAgentP->getTunnelDS(); if (!ds) return LOCERR_WRONGUSAGE; // opens current session's tunnel key aSettingsKeyP = ds->newTunnelKey(fEngineInterfaceP); } else #endif return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode); // opened a key return LOCERR_OK; } // TAgentParamsKey::OpenSubKeyByName #endif // ENGINEINTERFACE_SUPPORT } // namespace sysync // eof