Imported Upstream version 1.0beta3
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / syncagent.cpp
1 /*
2  *  File:         syncagent.cpp
3  *
4  *  Author:                       Lukas Zeller (luz@synthesis.ch)
5  *
6  *  TSyncAgent:   Provides functionality to run client or server
7  *                sessions.
8  *                Unifies former TSyncClient and TSyncServer
9  *
10  *  Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
11  *
12  *  2009-09-30 : luz : created from syncclient.cpp and syncserver.cpp
13  *
14  */
15
16
17 // includes
18 #include "prefix_file.h"
19 #include "sysync.h"
20 #include "syncagent.h"
21 #include "syncappbase.h"
22
23 #ifdef HARD_CODED_SERVER_URI
24   #include "syserial.h"
25 #endif
26
27 // includes that can't be in .h due to circular references
28 #ifdef SYSYNC_SERVER
29 #include "syncsessiondispatch.h"
30 #endif
31 #ifdef SYSYNC_CLIENT
32 #include "syncclientbase.h"
33 #endif
34
35 #ifdef SYSYNC_TOOL
36 #include <errno.h>
37 #endif
38
39
40 namespace sysync {
41
42
43 #ifdef SYSYNC_TOOL
44
45 // Support for SySync Diagnostic Tool
46 // ==================================
47
48
49 // test login into database
50 int testLogin(int argc, const char *argv[])
51 {
52   if (argc<0) {
53     // help requested
54     CONSOLEPRINTF(("  login [<username> <password>] [<deviceid>]"));
55     CONSOLEPRINTF(("    test login to database with syncml user/password and optional deviceid"));
56     return EXIT_SUCCESS;
57   }
58
59   TSyncSession *sessionP = NULL;
60   const char *username = NULL;
61   const char *password = NULL;
62   const char *deviceid = "sysytool_test";
63
64   // check for argument
65   if (argc<2) {
66     // no user/password, test anonymous login
67         if (argc>0) deviceid = argv[0];
68   }
69   else {
70     // login with user/password
71     username = argv[0];
72     password = argv[1];
73     if (argc>2) {
74       // explicit device ID
75       deviceid = argv[2];
76     }
77   }
78     
79   // get session to work with
80   sessionP =
81     static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
82
83   bool authok = false;
84   
85   // try login
86   if (username) {
87     // real login with user and password
88     authok = sessionP->SessionLogin(username, password, sectyp_clearpass, deviceid);
89   }
90   else {
91     // anonymous - do a "login" with empty credentials
92     authok = sessionP->SessionLogin("anonymous", NULL, sectyp_anonymous, deviceid);
93   }
94   
95   if (authok) {
96     CONSOLEPRINTF(("+++++ Successfully authorized"));
97   }
98   else {
99     CONSOLEPRINTF(("----- Authorisation failed"));
100   }
101   
102   return authok;
103 } // testLogin
104
105
106 // convert user data into internal format and back
107 int convertData(int argc, const char *argv[])
108 {
109   if (argc<0) {
110     // help requested
111     CONSOLEPRINTF(("  convert <datastore name> <data file, vcard or vcalendar etc.> [<explicit input type>] [<output type>]"));
112     CONSOLEPRINTF(("    Convert data to internal format of specified datastore and back"));
113     return EXIT_SUCCESS;
114   }
115
116   TSyncSession *sessionP = NULL;
117   const char *datastore = NULL;
118   const char *rawfilename = NULL;
119   const char *inputtype = NULL;
120   const char *outputtype = NULL;
121
122   // check for argument
123   if (argc<2) {
124         CONSOLEPRINTF(("required datatype name and raw file name arguments"));
125         return EXIT_FAILURE;
126   }
127   datastore = argv[0];
128   rawfilename = argv[1];
129   if (argc>=3) {
130     // third arg is explicit input type
131     inputtype=argv[2];
132   }
133   outputtype=inputtype; // default to input type
134   if (argc>=4) {
135     // fourth arg is explicit output type
136     outputtype=argv[3];
137   }
138   
139   // get session to work with
140   sessionP =
141     static_cast<TSyncSessionDispatch *>(getSyncAppBase())->getSySyToolSession();
142   // configure session
143   sessionP->fRemoteCanHandleUTC = true; // run generator and parser in UTC enabled mode
144   
145   
146   // switch mimimal debugging on
147   sessionP->getDbgLogger()->setMask(sessionP->getDbgLogger()->getMask() | (DBG_PARSE+DBG_GEN));
148     
149   // find datastore
150   TLocalEngineDS *datastoreP = sessionP->findLocalDataStore(datastore);
151   TSyncItemType *inputtypeP = NULL;
152   TSyncItemType *outputtypeP = NULL;
153   if (!datastoreP) {
154         CONSOLEPRINTF(("datastore type '%s' not found",datastore));
155         return EXIT_FAILURE;
156   }
157
158   // find input type
159   if (inputtype) {
160     // search in datastore
161     inputtypeP=datastoreP->getReceiveType(inputtype,NULL);
162   }
163   else {
164     // use preferred rx type
165     inputtypeP=datastoreP->getPreferredRxItemType();
166   }
167   if (!inputtypeP) {
168         CONSOLEPRINTF(("input type not found"));
169         return EXIT_FAILURE;
170   }
171   // find output type
172   if (outputtype) {
173     // search in datastore
174     outputtypeP=datastoreP->getSendType(outputtype,NULL);
175   }
176   else {
177     // use preferred rx type
178     outputtypeP=datastoreP->getPreferredTxItemType();
179   }
180   if (!outputtypeP) {
181         CONSOLEPRINTF(("output type not found"));
182         return EXIT_FAILURE;
183   }
184   // prepare type usage
185   if (inputtypeP==outputtypeP)
186     inputtypeP->initDataTypeUse(datastoreP, true, true);
187   else {
188     inputtypeP->initDataTypeUse(datastoreP, false, true);
189     outputtypeP->initDataTypeUse(datastoreP, true, false);
190   }    
191
192   // now open file and read data item
193   FILE *infile;
194   size_t insize=0;
195   uInt8 *databuffer;
196   
197   infile = fopen(rawfilename,"rb");
198   if (!infile) {
199         CONSOLEPRINTF(("Cannot open input file '%s' (%d)",rawfilename,errno));
200         return EXIT_FAILURE;    
201   }
202   // - get size of file
203   fseek(infile,0,SEEK_END);
204   insize=ftell(infile);
205   fseek(infile,0,SEEK_SET);
206   // - create buffer of appropriate size
207   databuffer = new uInt8[insize];
208   if (!databuffer) {
209         CONSOLEPRINTF(("Not enough memory to read input file '%s' (%d)",rawfilename,errno));
210         return EXIT_FAILURE;    
211   }  
212   // - read data
213   if (fread(databuffer,1,insize,infile)<insize) {
214         CONSOLEPRINTF(("Error reading input file '%s' (%d)",rawfilename,errno));
215         return EXIT_FAILURE;    
216   }
217   CONSOLEPRINTF(("\nNow converting into internal field representation\n"));
218   // create a sml item
219   TStatusCommand statusCmd(sessionP);
220   SmlItemPtr_t smlitemP = newItem();
221   smlitemP->data=newPCDataStringX(databuffer,true,insize);
222   delete[] databuffer;
223   // create and fill a Sync item
224   TSyncItem *syncitemP = inputtypeP->newSyncItem(
225     smlitemP, // SyncML toolkit item Data to be converted into SyncItem
226     sop_replace, // the operation to be performed with this item
227     fmt_chr,    // assume default (char) format
228     inputtypeP, // target myself
229     datastoreP, // local datastore
230     statusCmd // status command that might be modified in case of error
231   );
232   // forget SyncML version
233   smlFreeItemPtr(smlitemP);
234   if (!syncitemP) {
235         CONSOLEPRINTF(("Error converting input file to internal format (SyncML status code=%hd)",statusCmd.getStatusCode()));
236         return EXIT_FAILURE;
237   }
238
239   CONSOLEPRINTF(("\nNow copying item and convert back to transport format\n"));
240
241   // make new for output type
242   TSyncItem *outsyncitemP = outputtypeP->newSyncItem(
243     outputtypeP, // target myself
244     datastoreP  // local datastore
245   );
246   // copy data
247   outsyncitemP->replaceDataFrom(*syncitemP);
248   delete syncitemP;
249   // convert back
250   smlitemP=outputtypeP->newSmlItem(
251     outsyncitemP,   // the syncitem to be represented as SyncML
252     datastoreP // local datastore
253   );
254   if (!syncitemP) {
255         CONSOLEPRINTF(("Could not convert back item data"));
256         return EXIT_FAILURE;
257   }
258   
259   // forget converted back item
260   smlFreeItemPtr(smlitemP);
261   
262   return EXIT_SUCCESS;
263 } // convertData
264
265 #endif // SYSYNC_TOOL
266
267
268
269
270 #ifdef PRECONFIGURED_SYNCREQUESTS
271
272 // Implementation of TSyncReqConfig
273 // ================================
274
275 // config for databases to sync with
276 TSyncReqConfig::TSyncReqConfig(TLocalDSConfig *aLocalDSCfg, TConfigElement *aParentElement) :
277   TConfigElement("syncrequest",aParentElement),
278   fLocalDSConfig(aLocalDSCfg)
279 {
280   clear();
281 } // TSyncReqConfig::TSyncReqConfig
282
283
284 TSyncReqConfig::~TSyncReqConfig()
285 {
286   // nop so far
287 } // TSyncReqConfig::~TSyncReqConfig
288
289
290 // init defaults
291 void TSyncReqConfig::clear(void)
292 {
293   // init defaults
294   // - local client datatstore subselection path or CGI (such as "test" in "contact/test")
295   fLocalPathExtension.erase();
296   // - remote server DB layer auth
297   fDBUser.erase();
298   fDBPassword.erase();
299   // - remote server datastore path
300   fServerDBPath.erase();
301   // - sync mode
302   fSyncMode=smo_twoway;
303   fSlowSync=false; // default to non-slow
304   // - DS 1.2 filtering parameters
305   fRecordFilterQuery.erase();
306   fFilterInclusive=false;
307   // clear inherited
308   inherited::clear();
309 } // TSyncReqConfig::clear
310
311
312 // config element parsing
313 bool TSyncReqConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
314 {
315   // checking the elements
316   if (strucmp(aElementName,"localpathextension")==0)
317     expectString(fLocalPathExtension);
318   else if (strucmp(aElementName,"dbuser")==0)
319     expectString(fDBUser);
320   else if (strucmp(aElementName,"dbpassword")==0)
321     expectString(fDBPassword);
322   else if (strucmp(aElementName,"dbpath")==0)
323     expectString(fServerDBPath);
324   else if (strucmp(aElementName,"syncmode")==0)
325     expectEnum(sizeof(fSyncMode),&fSyncMode,SyncModeNames,numSyncModes);
326   else if (strucmp(aElementName,"slowsync")==0)
327     expectBool(fSlowSync);
328   else if (strucmp(aElementName,"recordfilter")==0)
329     expectString(fRecordFilterQuery);
330   else if (strucmp(aElementName,"filterinclusive")==0)
331     expectBool(fFilterInclusive);
332
333   // - none known here
334   else
335     return inherited::localStartElement(aElementName,aAttributes,aLine);
336   // ok
337   return true;
338 } // TSyncReqConfig::localStartElement
339
340
341 // resolve
342 void TSyncReqConfig::localResolve(bool aLastPass)
343 {
344   if (aLastPass) {
345     // check for required settings
346     // %%% tbd
347   }
348   // resolve inherited
349   inherited::localResolve(aLastPass);
350 } // TSyncReqConfig::localResolve
351
352
353 // create appropriate type of local datastore from config and init sync parameters
354 TLocalEngineDS *TSyncReqConfig::initNewLocalDataStore(TSyncSession *aSessionP)
355 {
356   // - create appropriate type of localdsP
357   TLocalEngineDS *localdsP = fLocalDSConfig->newLocalDataStore(aSessionP);
358   // - set parameters
359   localdsP->dsSetClientSyncParams(
360     fSyncMode,
361     fSlowSync,
362     fServerDBPath.c_str(),
363     fDBUser.c_str(),
364     fDBPassword.c_str(),
365     fLocalPathExtension.c_str(),
366     fRecordFilterQuery.c_str(),
367     fFilterInclusive
368   );
369   return localdsP;
370 } // TSyncReqConfig::initNewLocalDataStore
371
372 #endif // PRECONFIGURED_SYNCREQUESTS
373
374
375 // Implementation of TAgentConfig
376 // ==============================
377
378
379 TAgentConfig::TAgentConfig(const char* aName, TConfigElement *aParentElement) :
380   inherited(aName,aParentElement)
381 {
382   clear();
383 } // TAgentConfig::TAgentConfig
384
385
386 TAgentConfig::~TAgentConfig()
387 {
388   clear();
389 } // TAgentConfig::~TAgentConfig
390
391
392 // init defaults
393 void TAgentConfig::clear(void)
394 {
395   // clear inherited  
396   inherited::clear();
397         // Note: we always clear both client and server fields - even if we'll only use one set later
398         #ifdef SYSYNC_CLIENT
399   // init client auth defaults (note that these MUST correspond with the defaults set by loadRemoteParams() !!!
400   fAssumedServerAuth=auth_none; // start with no auth
401   fAssumedServerAuthEnc=fmt_chr; // start with char encoding
402   fAssumedNonce.erase(); // start with no nonce
403   // auth retry options (mainly for stupid servers like SCTS)
404   #ifdef SCTS_COMPATIBILITY_HACKS
405   fNewSessionForAuthRetry=false;
406   fNoRespURIForAuthRetry=false;
407   #else
408   fNewSessionForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
409   fNoRespURIForAuthRetry=true; // all production Synthesis clients had it hardcoded (ifdeffed) this way until 2.9.8.7
410   #endif
411   fSmartAuthRetry=true; // try to be smart and try different auth retry (different from fNewSessionForAuthRetry/fNoRespURIForAuthRetry) if first attempts fail
412   // other defaults
413   fPutDevInfAtSlowSync=true; // smartner server needs it, and it does not harm so we have it on by default
414   #ifndef NO_LOCAL_DBLOGIN
415   fLocalDBUser.erase();
416   fLocalDBPassword.erase();
417   fNoLocalDBLogin=false;
418   #endif
419   #ifdef PRECONFIGURED_SYNCREQUESTS
420   fEncoding=SML_XML; // default to more readable XML
421   fServerUser.erase();
422   fServerPassword.erase();
423   fServerURI.erase();
424   fTransportUser.erase();
425   fTransportPassword.erase();
426   fSocksHost.erase();
427   fProxyHost.erase();
428   fProxyUser.erase();
429   fProxyPassword.erase();
430   // remove sync db specifications
431   TSyncReqList::iterator pos;
432   for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
433     delete *pos;
434   fSyncRequests.clear();
435   #endif
436   // clear inherited
437   inherited::clear();
438   // modify timeout after inherited sets it
439   fSessionTimeout=DEFAULT_CLIENTSESSIONTIMEOUT;
440   // SyncML version support
441   fAssumedServerVersion=MAX_SYNCML_VERSION; // try with highest version we support
442   fMaxSyncMLVersionSupported=MAX_SYNCML_VERSION; // support what we request (overrides session default)
443   #endif
444   #ifdef SYSYNC_SERVER
445   // init server defaults
446   fRequestedAuth = auth_md5;
447   fRequiredAuth = auth_md5;
448   fAutoNonce = true;
449   fConstantNonce.erase();
450   fExternalURL.erase();
451   fMaxGUIDSizeSent = 32; // reasonable size, but prevent braindamaged Exchange-size IDs to be sent
452   fUseRespURI = true;
453   fRespURIOnlyWhenDifferent = true;
454   // modify timeout after inherited sets it
455   fSessionTimeout=DEFAULT_SERVERSESSIONTIMEOUT;
456   #endif
457 } // TAgentConfig::clear
458
459
460 #ifndef HARDCODED_CONFIG
461
462 // config element parsing
463 bool TAgentConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
464 {
465   if (IS_CLIENT) {
466           // check the client elements
467         #ifdef SYSYNC_CLIENT
468     // - defaults for starting a session
469     if (strucmp(aElementName,"defaultsyncmlversion")==0)
470       expectEnum(sizeof(fAssumedServerVersion),&fAssumedServerVersion,SyncMLVersionNames,numSyncMLVersions);
471     else if (strucmp(aElementName,"defaultauth")==0)
472       expectEnum(sizeof(fAssumedServerAuth),&fAssumedServerAuth,authTypeNames,numAuthTypes);
473     else if (strucmp(aElementName,"defaultauthencoding")==0)
474       expectEnum(sizeof(fAssumedServerAuthEnc),&fAssumedServerAuthEnc,encodingFmtSyncMLNames,numFmtTypes);
475     else if (strucmp(aElementName,"defaultauthnonce")==0)
476       expectString(fAssumedNonce);
477     else if (strucmp(aElementName,"newsessionforretry")==0)
478       expectBool(fNewSessionForAuthRetry);
479     else if (strucmp(aElementName,"originaluriforretry")==0)
480       expectBool(fNoRespURIForAuthRetry);
481     else if (strucmp(aElementName,"smartauthretry")==0)
482       expectBool(fSmartAuthRetry);
483     // - other options
484     else if (strucmp(aElementName,"putdevinfatslowsync")==0)
485       expectBool(fPutDevInfAtSlowSync);
486     else if (strucmp(aElementName,"fakedeviceid")==0)
487       expectString(fFakeDeviceID);
488     else
489     #ifndef NO_LOCAL_DBLOGIN
490     if (strucmp(aElementName,"localdbuser")==0)
491       expectString(fLocalDBUser);
492     else if (strucmp(aElementName,"localdbpassword")==0)
493       expectString(fLocalDBPassword);
494     else if (strucmp(aElementName,"nolocaldblogin")==0)
495       expectBool(fNoLocalDBLogin);
496     else
497     #endif
498     // serverURL is always available to allow define fixed URL in config that can't be overridden in profiles
499     if (strucmp(aElementName,"serverurl")==0)
500       expectString(fServerURI);
501     else
502     #ifdef PRECONFIGURED_SYNCREQUESTS
503     if (strucmp(aElementName,"syncmlencoding")==0)
504       expectEnum(sizeof(fEncoding),&fEncoding,SyncMLEncodingNames,numSyncMLEncodings);
505     else if (strucmp(aElementName,"serveruser")==0)
506       expectString(fServerUser);
507     else if (strucmp(aElementName,"serverpassword")==0)
508       expectString(fServerPassword);
509     else if (strucmp(aElementName,"sockshost")==0)
510       expectString(fSocksHost);
511     else if (strucmp(aElementName,"proxyhost")==0)
512       expectString(fProxyHost);
513     else if (strucmp(aElementName,"proxyuser")==0)
514       expectString(fProxyUser);
515     else if (strucmp(aElementName,"proxypassword")==0)
516       expectString(fProxyPassword);
517     else if (strucmp(aElementName,"transportuser")==0)
518       expectString(fTransportUser);
519     else if (strucmp(aElementName,"transportpassword")==0)
520       expectString(fTransportPassword);
521     // - Sync DB specification
522     else if (strucmp(aElementName,"syncrequest")==0) {
523       // definition of a new datastore
524       const char* nam = getAttr(aAttributes,"datastore");
525       if (!nam) {
526         ReportError(true,"syncrequest missing 'datastore' attribute");
527       }
528       else {
529         // search datastore
530         TLocalDSConfig *localDSCfgP = getLocalDS(nam);
531         if (!localDSCfgP)
532           return fail("unknown local datastore '%s' specified",nam);
533         // create new syncDB config linked to that datastore
534         TSyncReqConfig *syncreqcfgP = new TSyncReqConfig(localDSCfgP,this);
535         // - save in list
536         fSyncRequests.push_back(syncreqcfgP);
537         // - let element handle parsing
538         expectChildParsing(*syncreqcfgP);
539       }
540     }
541     else
542     #endif
543         // - none known here
544       return inherited::localStartElement(aElementName,aAttributes,aLine);
545     #endif // SYSYNC_CLIENT
546   }
547   else {
548         #ifdef SYSYNC_SERVER
549     // check the server elements
550     if (strucmp(aElementName,"requestedauth")==0)
551       expectEnum(sizeof(fRequestedAuth),&fRequestedAuth,authTypeNames,numAuthTypes);
552     else if (strucmp(aElementName,"requiredauth")==0)
553       expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
554     // here to maintain compatibility with old pre 1.0.5.3 config files
555     else if (strucmp(aElementName,"reqiredauth")==0)
556       expectEnum(sizeof(fRequiredAuth),&fRequiredAuth,authTypeNames,numAuthTypes);
557     else if (strucmp(aElementName,"autononce")==0)
558       expectBool(fAutoNonce);
559     else if (strucmp(aElementName,"constantnonce")==0)
560       expectString(fConstantNonce);
561     else if (strucmp(aElementName,"externalurl")==0)
562       expectString(fExternalURL);
563     else if (strucmp(aElementName,"maxguidsizesent")==0)
564       expectUInt16(fMaxGUIDSizeSent);
565     else if (strucmp(aElementName,"sendrespuri")==0)
566       expectBool(fUseRespURI);
567     else if (strucmp(aElementName,"respurionlywhendifferent")==0)
568       expectBool(fRespURIOnlyWhenDifferent);
569     // - none known here
570     else
571       return inherited::localStartElement(aElementName,aAttributes,aLine);
572         #endif // SYSYNC_SERVER
573   }
574   // ok
575   return true;
576 } // TAgentConfig::localStartElement
577
578 #endif // HARDCODED_CONFIG
579
580
581 // resolve
582 void TAgentConfig::localResolve(bool aLastPass)
583 {
584   if (aLastPass) {
585         if (IS_CLIENT) {
586       #ifdef SYSYNC_CLIENT
587       #ifdef PRECONFIGURED_SYNCREQUESTS
588       // - resolve requests
589       TSyncReqList::iterator pos;
590       for(pos=fSyncRequests.begin();pos!=fSyncRequests.end();pos++)
591         (*pos)->Resolve(aLastPass);
592       #endif
593       #endif // SYSYNC_CLIENT
594     }
595     else {
596       #ifdef SYSYNC_SERVER
597       if (!fAutoNonce && fConstantNonce.empty())
598         ReportError(false,"Warning: 'constantnonce' should be defined when 'autononce' is not set");
599       #endif // SYSYNC_SERVER
600     }
601   }
602   // resolve inherited
603   inherited::localResolve(aLastPass);
604 } // TAgentConfig::localResolve
605
606
607
608 // Implementation of TSyncAgent
609 // =============================
610
611
612 // constructor
613 TSyncAgent::TSyncAgent(
614   TSyncAppBase *aAppBaseP,
615   TSyncSessionHandle *aSessionHandleP,
616   const char *aSessionID // a session ID
617 ) :
618   TSyncSession(aAppBaseP,aSessionID)  
619 {
620         if (IS_CLIENT) {
621                 #ifdef SYSYNC_CLIENT
622     #ifdef HARD_CODED_SERVER_URI
623     fNoCRCPrefixLen=0;
624     #endif
625           #ifdef ENGINE_LIBRARY
626                 // engine
627         fClientEngineState = ces_idle;
628     #endif
629     // reset session now to get correct initial state
630     InternalResetSession();
631     // restart with session numbering at 1 (incremented before use)
632     fClientSessionNo=0;  
633                 #endif // SYSYNC_CLIENT
634   }
635   else {
636                 #ifdef SYSYNC_SERVER
637     // init answer buffer
638     fBufferedAnswer = NULL;
639     fBufferedAnswerSize = 0;
640     // reset data counts
641     fIncomingBytes = 0;
642     fOutgoingBytes = 0;
643           #ifdef ENGINE_LIBRARY
644     // engine
645     fServerEngineState = ses_needdata;
646     fRequestSize = 0;
647     #endif
648     // init own stuff
649     InternalResetSession();
650     // save session handle
651     fSessionHandleP = aSessionHandleP; // link to handle
652     // get config defaults
653     TAgentConfig *configP = static_cast<TAgentConfig *>(aAppBaseP->getRootConfig()->fAgentConfigP);
654     fUseRespURI = configP->fUseRespURI;
655     // create all locally available datastores from config
656     TLocalDSList::iterator pos;
657     for (pos=configP->fDatastores.begin(); pos!=configP->fDatastores.end(); pos++) {
658       // create the datastore
659       addLocalDataStore(*pos);
660     }
661     #endif // SYSYNC_SERVER
662   }
663 } // TSyncAgent::TSyncAgent
664
665
666 // destructor
667 TSyncAgent::~TSyncAgent()
668 {
669         if (IS_CLIENT) {
670     // make sure everything is terminated BEFORE destruction of hierarchy begins
671     TerminateSession();
672   }
673   else {
674         #ifdef SYSYNC_SERVER
675     // forget any buffered answers
676     bufferAnswer(NULL,0);
677     // reset session
678     InternalResetSession();
679     // show session data transfer
680     PDEBUGPRINTFX(DBG_HOT,(
681       "Session data transfer statistics: incoming bytes=%ld, outgoing bytes=%ld",
682       (long)fIncomingBytes,
683       (long)fOutgoingBytes
684     ));
685     // DO NOT remove session from dispatcher here,
686     //   this is the task of the dispatcher itself!
687     CONSOLEPRINTF(("Terminated SyncML session (server id=%s)\n",getLocalSessionID()));
688     // show end of session in global level
689     POBJDEBUGPRINTFX(getSyncAppBase(),DBG_HOT,(
690       "TSyncAgent::~TSyncAgent: Deleted SyncML session (local session id=%s)",
691       getLocalSessionID()
692     ));
693                 #endif // SYSYNC_SERVER
694   }
695 } // TSyncAgent::~TSyncAgent
696
697
698 // Terminate session
699 void TSyncAgent::TerminateSession()
700 {
701   #ifdef SYSYNC_CLIENT
702   if (IS_CLIENT && !fTerminated) {
703     InternalResetSession();
704     #ifdef ENGINE_LIBRARY
705     // switch state to done to prevent any further activity via SessionStep()
706     fClientEngineState = ces_done;
707     #endif
708   }
709   #endif // SYSYNC_CLIENT
710   inherited::TerminateSession();
711 } // TSyncAgent::TerminateSession
712
713
714
715
716 void TSyncAgent::InternalResetSession(void)
717 {
718         if (IS_CLIENT) {
719         #ifdef SYSYNC_CLIENT
720     // use remote URI as specified to start a session
721     fRespondURI = fRemoteURI;
722     #ifdef HARD_CODED_SERVER_URI
723     #if defined(CUSTOM_URI_SUFFIX) && !defined(HARD_CODED_SERVER_URI_LEN)
724     #error "HARD_CODED_SERVER_URI_LEN must be defined when using CUSTOM_URI_SUFFIX"
725     #endif
726     #ifdef HARD_CODED_SERVER_URI_LEN
727     // only part of URL must match (max HARD_CODED_SERVER_URI_LEN chars will be added, less if URI is shorter)
728     fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false, HARD_CODED_SERVER_URI_LEN);
729     #else
730     // entire URL (except prefix) must match
731     fServerURICRC = addNameToCRC(SYSER_CRC32_SEED, fRemoteURI.c_str()+fNoCRCPrefixLen, false);
732     #endif
733     #endif
734     // set SyncML version
735     // Note: will be overridden with call to loadRemoteParams()
736     fSyncMLVersion = syncml_vers_unknown; // unknown
737     // will be cleared to suppress automatic use of DS 1.2 SINCE/BEFORE filters
738     // (e.g. for date range in func_SetDaysRange())
739     fServerHasSINCEBEFORE = true;
740     // no outgoing alert 222 sent so far
741     fOutgoingAlert222Count = 0;
742     #endif
743   }
744   else {
745                 #ifdef SYSYNC_SERVER
746     // %%% remove this as soon as Server is 1.1 compliant
747     //fSyncMLVersion=syncml_vers_1_0; // only accepts 1.0 for now %%%%
748     #endif  
749   }
750 } // TSyncAgent::InternalResetSession
751
752
753 // Virtual version
754 void TSyncAgent::ResetSession(void)
755 {
756   // let ancestor do its stuff
757   TSyncSession::ResetSession();
758   // do my own stuff (and probably modify settings of ancestor!)
759   InternalResetSession();
760 } // TSyncAgent::ResetSession
761
762
763 bool TSyncAgent::MessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
764 {
765         if (IS_CLIENT) {
766                 #ifdef SYSYNC_CLIENT
767     return ClientMessageStarted(aContentP,aStatusCommand,aBad);
768                 #endif // SYSYNC_CLIENT
769   }
770   else {
771     #ifdef SYSYNC_SERVER
772     return ServerMessageStarted(aContentP,aStatusCommand,aBad);
773     #endif // SYSYNC_SERVER
774   }
775 }
776
777 void TSyncAgent::MessageEnded(bool aIncomingFinal)
778 {
779         if (IS_CLIENT) {
780                 #ifdef SYSYNC_CLIENT
781     ClientMessageEnded(aIncomingFinal);
782                 #endif // SYSYNC_CLIENT
783   }
784   else {
785     #ifdef SYSYNC_SERVER
786     ServerMessageEnded(aIncomingFinal);
787     #endif // SYSYNC_SERVER
788   }
789 }
790
791
792 // starting with engine version 2.0.8.7 a client's device ID (in devinf) is no longer
793 // a constant string, but the device's unique ID
794 string TSyncAgent::getDeviceID(void)
795 {
796   if (fLocalURI.empty()) {
797         if (IS_SERVER)
798             return SYSYNC_SERVER_DEVID; // return default ID
799         else    
800             return SYSYNC_CLIENT_DEVID; // return default ID
801   }
802   else
803     return fLocalURI;
804 } // TSyncAgent::getDeviceID
805
806
807 // ask syncappbase for device type
808 string TSyncAgent::getDeviceType(void)
809 {
810   // taken from configuration or default for engine type (client/server)
811   return getSyncAppBase()->getDevTyp();
812 } // TSyncAgent::getDeviceType
813
814
815 #ifdef SYSYNC_CLIENT
816
817 // initialize the client session and link it with the SML toolkit
818 localstatus TSyncAgent::InitializeSession(uInt32 aProfileSelector, bool aAutoSyncSession)
819 {
820   localstatus sta;
821
822   // Select profile now (before creating instance, as encoding is dependent on profile)
823   sta=SelectProfile(aProfileSelector, aAutoSyncSession);
824   if (sta) return sta;
825   // Start a SyncML toolkit instance now and set the encoding from config
826   InstanceID_t myInstance;
827   if (!getSyncAppBase()->newSmlInstance(
828     getEncoding(),
829     getRootConfig()->fLocalMaxMsgSize * 2, // twice the message size
830     myInstance
831   )) {
832     return LOCERR_SMLFATAL;
833   }
834   // let toolkit know the session pointer
835   if (getSyncAppBase()->setSmlInstanceUserData(myInstance,this)!=SML_ERR_OK) // toolkit must know session (as userData)
836     return LOCERR_SMLFATAL;
837   // remember the instance myself
838   setSmlWorkspaceID(myInstance); // session must know toolkit workspace
839   // done
840   return LOCERR_OK;
841 } // TSyncAgent::InitializeSession
842
843
844
845 // select a profile (returns false if profile not found)
846 // Note: base class just tries to retrieve information from
847 //       config
848 localstatus TSyncAgent::SelectProfile(uInt32 aProfileSelector, bool aAutoSyncSession)
849 {
850   #ifndef PRECONFIGURED_SYNCREQUESTS
851   // no profile settings in config -> error
852   return LOCERR_NOCFG;
853   #else
854   // get profile settings from config
855   TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
856   // - get encoding
857   fEncoding=configP->fEncoding; // SyncML encoding
858   // - set server access details
859   fRemoteURI=configP->fServerURI; // Remote URI = Server URI
860   fTransportUser=configP->fTransportUser; // transport layer user (e.g. HTTP auth)
861   fTransportPassword=configP->fTransportPassword; // transport layer password (e.g. HTTP auth)
862   fServerUser=configP->fServerUser; // Server layer authentification user name
863   fServerPassword=configP->fServerPassword; // Server layer authentification password
864   #ifndef NO_LOCAL_DBLOGIN
865   fLocalDBUser=configP->fLocalDBUser; // Local DB authentification user name (empty if local DB is single user)
866   fNoLocalDBLogin=configP->fNoLocalDBLogin; // if set, no local DB auth takes place, but fLocalDBUser is used as userkey (depending on DB implementation)
867   fLocalDBPassword=configP->fLocalDBPassword; // Local DB authentification password
868   #endif
869   fProxyHost=configP->fProxyHost; // Proxy host
870   fSocksHost=configP->fSocksHost; // Socks host
871   fProxyUser=configP->fProxyUser;
872   fProxyPassword=configP->fProxyPassword;
873   // Reset session after profile change
874   // and also remove any datastores we might have
875   ResetAndRemoveDatastores();
876   // if tunnel, that's all for now
877   if (aProfileSelector==TUNNEL_PROFILE_ID) return LOCERR_OK;
878   // - create and init datastores needed for this session from config
879   //   Note: probably config has no sync requests, but they are created later
880   //         programmatically
881   TSyncReqList::iterator pos;
882   for (pos=configP->fSyncRequests.begin(); pos!=configP->fSyncRequests.end(); pos++) {
883     // create and init the datastore
884     fLocalDataStores.push_back(
885       (*pos)->initNewLocalDataStore(this)
886     );
887   }
888   // create "new" session ID (derivates will do this better)
889   fClientSessionNo++;
890   return LOCERR_OK;
891   #endif
892 } // TSyncAgent::SelectProfile
893
894
895 // make sure we are logged in to local datastore
896 localstatus TSyncAgent::LocalLogin(void)
897 {
898   #ifndef NO_LOCAL_DBLOGIN
899   if (!fNoLocalDBLogin && !fLocalDBUser.empty()) {
900     // check authorisation (login to correct user) in local DB
901     if (!SessionLogin(fLocalDBUser.c_str(),fLocalDBPassword.c_str(),sectyp_clearpass,fRemoteURI.c_str())) {
902       return localError(401); // done & error
903     }
904   }
905   #endif
906   return LOCERR_OK;
907 } // TSyncAgent::LocalLogin
908
909
910
911 // process message in the instance buffer
912 localstatus TSyncAgent::processAnswer(void)
913 {
914   InstanceID_t myInstance = getSmlWorkspaceID();
915   Ret_t err;
916
917   // now process data
918   DEBUGPRINTF(("===> now calling smlProcessData"));
919   #ifdef SYDEBUG
920   MemPtr_t data = NULL;
921   MemSize_t datasize;
922   smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
923   #endif  
924   fIgnoreMsgErrs=false;
925   err=smlProcessData(
926     myInstance,
927     SML_ALL_COMMANDS
928   );
929   if (err) {
930     // dump the message that failed to process
931     #ifdef SYDEBUG
932     if (data) DumpSyncMLBuffer(data,datasize,false,err);
933     #endif    
934         if (!fIgnoreMsgErrs) {
935       PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)err));
936       // other problem or already using SyncML 1.0 --> error
937       return LOCERR_PROCESSMSG;
938     }
939   }
940   // now check if this is a session restart
941   if (isStarting()) {
942     // this is still the beginning of a session
943     return LOCERR_SESSIONRST;
944   }
945   return LOCERR_OK;
946 } // TSyncAgentBase::processAnswer
947
948
949
950 // let session produce (or finish producing) next message into
951 // SML workspace
952 // - returns aDone if no answer needs to be sent (=end of session)
953 // - returns 0 if successful
954 // - returns SyncML status code if unsucessfully aborted session
955 localstatus TSyncAgent::NextMessage(bool &aDone)
956 {
957   TLocalDataStorePContainer::iterator pos;
958   TSyError status;
959
960   TP_START(fTPInfo,TP_general); // could be new thread
961   // default to not continuing
962   aDone=true;
963   #ifdef PROGRESS_EVENTS
964   // check for user suspend
965   if (!getSyncAppBase()->NotifyProgressEvent(pev_suspendcheck)) {
966     SuspendSession(LOCERR_USERSUSPEND);
967   }
968   #endif
969   // done if session was aborted by last received commands
970   if (isAborted()) return getAbortReasonStatus(); // done & error
971   // check package state
972   if (fOutgoingState==psta_idle) {
973     // if suspended here, we'll just stop - nothing has happened yet
974     if (isSuspending()) {
975       AbortSession(fAbortReasonStatus,true);
976       return getAbortReasonStatus();
977     }
978     // start of an entirely new client session
979     #ifdef HARD_CODED_SERVER_URI
980     // extra check to limit hacking
981     if (fServerURICRC != SERVER_URI_CRC) {
982       // someone has tried to change the URI
983       DEBUGPRINTFX(DBG_ERROR,("hardcoded Server URI CRC mismatch"));
984       return LOCERR_LIMITED; // user will not know what this means, but we will
985     }
986     #endif
987     // - check if we have client requests
988     if (fLocalDataStores.size()<1) {
989       PDEBUGPRINTFX(DBG_ERROR,("No datastores defined to sync with"));
990       return LOCERR_NOCFG;
991     }
992     // %%% later, we could probably load cached info about
993     //     server requested auth, devinf etc. here
994     // use remote URI as specified to start a session
995     fRespondURI=fRemoteURI;
996     // get default params for sending first message to remote
997     // Note: may include remote flag settings that influence creation of my own ID below, that's why we do it now here
998     loadRemoteParams();
999     // get info about my own URI, whatever that is
1000     #ifndef HARDCODED_CONFIG
1001     if (!static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID.empty()) {
1002       // return fake Device ID if we have one defined in the config file (useful for testing)
1003       fLocalURI = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fFakeDeviceID;
1004     }
1005     else
1006     #endif
1007     {
1008       if (!getSyncAppBase()->getMyDeviceID(fLocalURI) || devidWithUserHash()) {
1009         // Device ID is not really unique, make a hash including user name to make it pseudo-unique
1010         // create MD5 hash from non-unique ID and user name
1011         // Note: when compiled with GUARANTEED_UNIQUE_DEVICID, devidWithUserHash() is always false.
1012         md5::SYSYNC_MD5_CTX context;
1013         uInt8 digest[16]; // for MD5 digest
1014         md5::Init (&context);
1015         // - add what we got for ID
1016         md5::Update (&context, (uInt8 *)fLocalURI.c_str(), fLocalURI.size());
1017         // - add user name, if any
1018         if (fLocalURI.size()>0) md5::Update (&context, (uInt8 *)fServerUser.c_str(), fServerUser.size());
1019         // - done
1020         md5::Final (digest, &context);
1021         // now make hex string of that
1022         fLocalURI = devidWithUserHash() ? 'x' : 'X'; // start with X to document this special case (lowercase = forced by remoteFlag)
1023         for (int n=0; n<16; n++) {
1024           AppendHexByte(fLocalURI,digest[n]);
1025         }
1026       }
1027     }
1028     // get my own name (if any)
1029     getPlatformString(pfs_device_name,fLocalName);
1030     // override some of these if not set by loadRemoteParams()
1031     if (fSyncMLVersion==syncml_vers_unknown)
1032       fSyncMLVersion=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerVersion;
1033     if (fRemoteRequestedAuth==auth_none)
1034       fRemoteRequestedAuth=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuth;
1035     if (fRemoteRequestedAuthEnc==fmt_chr)
1036       fRemoteRequestedAuthEnc=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedServerAuthEnc;
1037     if (fRemoteNonce.empty())
1038       fRemoteNonce=static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fAssumedNonce;
1039
1040     // we are not yet authenticated for the entire session
1041     fNeedAuth=true;
1042     // now ready for init
1043     fOutgoingState=psta_init; // %%%% could also set psta_initsync for combined init/sync
1044     fIncomingState=psta_idle; // remains idle until first answer SyncHdr with OK status is received
1045     fInProgress=true; // assume in progress
1046     // set session ID string
1047     StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)fClientSessionNo);
1048     // now we have a session id, can now display debug stuff
1049     #ifdef SYDEBUG
1050     string t;
1051     StringObjTimestamp(t,getSystemNowAs(TCTX_SYSTEM));
1052     PDEBUGPRINTFX(DBG_HOT,("\n[%s] =================> Starting new client session",t.c_str()));
1053     #endif
1054     // - make sure we are logged into the local database (if needed)
1055     status=LocalLogin();
1056     if (status!=LOCERR_OK) return status;
1057     // create header for first message no noResp
1058     issueHeader(false);
1059   }
1060   else {
1061     // check for proper end of session (caused by MessageEnded analysis)
1062     if (!fInProgress) {
1063       // give an opportunity to let make outgoing message end and flush xml end message 
1064       FinishMessage(true, false);  
1065       // end sync in all datastores (save anchors etc.)
1066       PDEBUGPRINTFX(DBG_PROTO,("Successful end of session -> calling engFinishDataStoreSync() for datastores now"));
1067       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
1068         (*pos)->engFinishDataStoreSync(); // successful end
1069       PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
1070       return LOCERR_OK; // done & ok
1071     }
1072   }
1073   // check expired case
1074   #ifdef APP_CAN_EXPIRE
1075   if (getClientBase()->fAppExpiryStatus!=LOCERR_OK) {
1076     PDEBUGPRINTFX(DBG_ERROR,("Evaluation Version expired - Please contact Synthesis AG for release version"));
1077     return getClientBase()->fAppExpiryStatus; // payment required, done & error
1078   }
1079   #endif
1080   if (fOutgoingState==psta_init || fOutgoingState==psta_initsync) {
1081     // - if suspended in init, nothing substantial has happened already, so just exit
1082     if (isSuspending() && fOutgoingState==psta_init) {
1083       AbortSession(fAbortReasonStatus,true);
1084       return getAbortReasonStatus();
1085     }
1086     // - prepare Alert(s) for databases to sync
1087     bool anyfirstsyncs=false;
1088     bool anyslowsyncs=false;
1089     TLocalEngineDS *localDS;
1090     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1091       // prepare alert (Note: datastore may be run by a superdatastore)
1092       localDS = *pos;
1093       status=localDS->engPrepareClientSyncAlert();
1094       if (status!=LOCERR_OK) {
1095         // local database error
1096         return localError(status); // not found
1097       }
1098       if (localDS->fFirstTimeSync) anyfirstsyncs=true;
1099       if (localDS->fSlowSync) anyslowsyncs=true;
1100     }
1101     // send devinf in Put command right away with init message if either...
1102     // - mustSendDevInf() returns true signalling an external condition that suggests sending devInf (like changed config)
1103     // - any datastore is doing first time sync
1104     // - fPutDevInfAtSlowSync is true and any datastore is doing slow sync
1105     if (
1106         mustSendDevInf() ||
1107       anyfirstsyncs ||
1108       (anyslowsyncs && static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync)
1109     ) {
1110       TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this);
1111       issueRootPtr(putcmdP);
1112     }
1113     // try to load devinf from cache (only if we don't know it already)
1114     if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
1115       SmlDevInfDevInfPtr_t devinfP;
1116       if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
1117         // we have cached devinf, analyze it now
1118         analyzeRemoteDevInf(devinfP);
1119       }
1120     }
1121     // GET the server's info if server didn't send it and we haven't cached at least the datastores
1122     if (!fRemoteDataStoresKnown) {
1123       // if we know datastores here, but not types, this means that remote does not have
1124       // CTCap, so it makes no sense to issue a GET again.
1125       #ifndef NO_DEVINF_GET
1126       PDEBUGPRINTFX(DBG_REMOTEINFO,("Nothing known about server, request DevInf using GET command"));
1127       TGetCommand *getcommandP = new TGetCommand(this);
1128       getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
1129       string devinftype=SYNCML_DEVINF_META_TYPE;
1130       addEncoding(devinftype);
1131       getcommandP->setMeta(newMetaType(devinftype.c_str()));
1132       ISSUE_COMMAND_ROOT(this,getcommandP);
1133       #endif
1134     }
1135     // - create Alert(s) for databases to sync
1136     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1137       // create alert for non-subdatastores
1138       localDS = *pos;
1139       if (!localDS->isSubDatastore()) {
1140         TAlertCommand *alertcmdP;
1141         status = localDS->engGenerateClientSyncAlert(alertcmdP);
1142         if (status!=0) {
1143           // local database error
1144           return status; // not found
1145         }
1146         ///%%%% unneeded (probably got here by copy&paste accidentally): if (localDS->fFirstTimeSync) anyfirstsyncs=true;
1147         // issue alert
1148         issueRootPtr(alertcmdP);
1149       }
1150     }
1151     // append sync phase if we have combined init/sync
1152     if (fOutgoingState==psta_initsync) fOutgoingState=psta_sync;
1153   }
1154   // process sync/syncop/map generating phases after init
1155   if (!isSuspending()) {
1156     // normal, session continues
1157     if (fOutgoingState==psta_sync) {
1158       // hold back sync until server has finished first package (init or initsync)
1159       if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1160         // start sync for alerted datastores
1161         for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1162           // Note: some datastores might be aborted due to unsuccessful alert.
1163           if ((*pos)->isActive()) {
1164                 // prepare engine for sync (%%% new routine in 3.2.0.3, summarizing engInitForSyncOps() and
1165             // switching to dssta_dataaccessstarted, i.e. loading sync set), but do in only once
1166             if (!((*pos)->testState(dssta_syncsetready))) {
1167               // not yet started
1168               status = (*pos)->engInitForClientSync();
1169               if (status!=LOCERR_OK ) {
1170                 // failed
1171                 if (status!=LOCERR_DATASTORE_ABORT) {
1172                   AbortSession(status,true);
1173                   return getAbortReasonStatus();
1174                 } 
1175               }
1176             }
1177             // start or continue (which is largely nop, as continuing works via unfinished sync command)
1178             // generating sync items
1179             (*pos)->engClientStartOfSyncMessage();
1180           }
1181         }
1182       }
1183     }
1184     else if (fOutgoingState==psta_map) {
1185       // hold back map until server has started sync at least (incominstate >=psta_sync)
1186       // NOTE: This is according to the specs, which says that client can begin
1187       //   with Map/update status package BEFORE sync package from server is
1188       //   completely received.
1189       // NOTE: Starfish server expects this and generates a few 222 alerts
1190       //   if we wait here, but then goes to map as well
1191       //   (so (fIncomingState==psta_map)-version works as well here!
1192       // %%%% other version: wait until server has started map phase as well
1193       // %%%% if (fIncomingState==psta_map) {
1194       if (fIncomingState>=psta_sync) {
1195         // start map for synced datastores
1196         for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1197           // Note: some datastores might be aborted due to unsuccessful alert.
1198           if ((*pos)->isActive()) {
1199             // now call datastore to generate map command if not already done
1200             (*pos)->engClientStartOfMapMessage(fIncomingState<psta_map);
1201           }
1202         }
1203       }
1204     }
1205     else if (fOutgoingState==psta_supplement) {
1206         // we are waiting for the server to complete a pending phase altough we are already done
1207       // with everything we want to send.
1208       // -> just generate a Alert 222 and wait for server to complete
1209       PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("Client finished so far, but needs to wait in supplement outgoing state until server finishes phase"));
1210     }
1211     else if (fOutgoingState!=psta_init) {
1212       // NOTE: can be psta_init because "if" begins again after psta_init checking
1213       //       to allow psta_init appending psta_sync for combined init/sync
1214       // no known state
1215       return 9999; // %%%%%
1216     }
1217   } // if not suspended
1218
1219   // security only: exit here if session got aborted in between
1220   if (isAborted())
1221     return getAbortReasonStatus(); // done & error
1222   if (!fInProgress)
1223     return 9999; // that's fine with us
1224   // now, we know that we will (most probably) send a message, so default for aDone is false from now on
1225   aDone=false;
1226   bool outgoingfinal;
1227
1228   // check for suspend
1229   if (isSuspending()) {
1230     // make sure we send a Suspend Alert
1231     TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)224);
1232     // - we just put local and remote URIs here
1233     SmlItemPtr_t itemP = newItem();
1234     itemP->target = newLocation(fRemoteURI.c_str());
1235     itemP->source = newLocation(fLocalURI.c_str());
1236     alertCmdP->addItem(itemP);
1237     ISSUE_COMMAND_ROOT(this,alertCmdP);
1238     // outgoing message is final, regardless of any session state
1239     outgoingfinal=true;
1240     MarkSuspendAlertSent(true);
1241   }
1242   else {
1243     // Determine if package can be final and if we need an 222 Alert
1244     // NOTE: if any commands were interruped or not sent due to outgoing message
1245     //       size limits, FinishMessage() will prevent final anyway, so no
1246     //       separate checking for enOfSync or endOfMap is needed.
1247     // - can finalize message when server has at least started answering current package
1248     //   OR if this is the first message (probably repeatedly) sent
1249     outgoingfinal = fIncomingState >= fOutgoingState || fIncomingState==psta_idle;
1250     if (outgoingfinal) {
1251       // allow early success here in case of nothing to respond, and nothing pending
1252       // StarFish server does need this...
1253       if (!fNeedToAnswer) {
1254         if (hasPendingCommands()) {
1255           // we have pending commands, cannot be final message
1256           outgoingfinal=false;
1257         }
1258         else {
1259           // no pending commands -> we're done now
1260           PDEBUGPRINTFX(DBG_PROTO,("Early end of session (nothing to send to server any more) -> calling engFinishDataStoreSync() for datastores now"));
1261           // - end sync in all datastores (save anchors etc.)
1262           for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos)
1263             (*pos)->engFinishDataStoreSync(); // successful end
1264           PDEBUGPRINTFX(DBG_PROTO,("Session not any more in progress: NextMessage() returns OK status=0"));
1265           // done & ok
1266           aDone=true;
1267           return LOCERR_OK;
1268         }
1269       }
1270     }
1271   }
1272   /* A dummy alert indicates this is a message with only alert222 request*/
1273   bool dummyAlert = false;
1274   if (!outgoingfinal) {
1275     // - send Alert 222 if we need to continue package but have nothing to send
1276     //   (or ALWAYS_CONTINUE222 defined)
1277     #ifndef ALWAYS_CONTINUE222
1278     if (!fNeedToAnswer)
1279     #endif
1280     {
1281       /* End-less loop detection
1282        * Some servers will never end and triggers client sends
1283        * ALERT222 forever. Detect this scenario and abort the session if
1284        * detected.
1285        * It is still valid for the server to use ALERT222 to "keep-alive" the
1286        * connection.
1287        * Therefore the loop detection criteria is:
1288        * - Nothing to send except the 222 Alert (!fNeedToAnswer)
1289        * - 5 adjacent 222 alerts within 20 seconds
1290        * - no status for an actual sync op command received (fOutgoingAlert222Count will be reset by those)
1291        *   because a server sending pending status in small chunks could also trigger the detector otherwise
1292        */
1293       if (!fNeedToAnswer) {
1294         dummyAlert = true;
1295         if (fOutgoingAlert222Count++ == 0) {
1296                 // start of 222 loop detection time
1297           fLastOutgoingAlert222 = getSystemNowAs(TCTX_UTC);
1298         } else if (fOutgoingAlert222Count > 5) {
1299           lineartime_t curTime = getSystemNowAs(TCTX_UTC);
1300           if (curTime - fLastOutgoingAlert222 < 20*secondToLinearTimeFactor) {
1301             PDEBUGPRINTFX(DBG_ERROR,(
1302               "Warning: More than 5 consecutive Alert 222 within 20 seconds- "
1303               "looks like endless loop, abort session"
1304             ));
1305             AbortSession(400, false);
1306             return getAbortReasonStatus();
1307           } else {
1308             fOutgoingAlert222Count = 0;
1309           }
1310         }
1311       }
1312       // not final, and nothing to answer otherwise: create alert-Command to request more info
1313       TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
1314       // %%% not clear from spec what has to be in item for 222 alert code
1315       //     but there MUST be an Item for the Alert command according to SyncML TK
1316       // - we just put local and remote URIs here
1317       SmlItemPtr_t itemP = newItem();
1318       itemP->target = newLocation(fRemoteURI.c_str());
1319       itemP->source = newLocation(fLocalURI.c_str());
1320       alertCmdP->addItem(itemP);
1321       ISSUE_COMMAND_ROOT(this,alertCmdP);
1322     }
1323   }
1324   // We send a response with no dummy alert, so reset the alert detector
1325   if (!dummyAlert) {
1326     fOutgoingAlert222Count = 0;
1327   }
1328
1329   // send custom end-of session puts
1330   if (!isSuspending() && outgoingfinal && fOutgoingState==psta_map) {
1331     // End of outgoing map package; let custom PUTs which may transmit some session statistics etc. happen now
1332     issueCustomEndPut();
1333   }
1334   // message complete, now finish it
1335   FinishMessage(
1336     outgoingfinal, // allowed if possible
1337     false // final not prevented
1338   );
1339   // Note, now fNewOutgoingPackage is set (by FinishMessage())
1340   // if next message will be responded to with a new package
1341
1342   // debug info
1343   #ifdef SYDEBUG
1344   if (PDEBUGMASK & DBG_SESSION) {
1345     PDEBUGPRINTFX(DBG_SESSION,(
1346       "---> NextMessage, outgoing state='%s', incoming state='%s'",
1347       PackageStateNames[fOutgoingState],
1348       PackageStateNames[fIncomingState]
1349     ));
1350     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1351       // Show state of local datastores
1352       PDEBUGPRINTFX(DBG_SESSION,(
1353         "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
1354         (*pos)->getName(),
1355         (*pos)->isAborted() ? "ABORTED - " : "",
1356         (*pos)->getDSStateName(),
1357         (*pos)->isResuming() ? "RESUMED " : "",
1358         (*pos)->fSlowSync ? "SLOW" : "normal",
1359         SyncModeDescriptions[(*pos)->fSyncMode],
1360         (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1361       ));
1362     }
1363   }
1364   PDEBUGENDBLOCK("SyncML_Outgoing");
1365   if (getLastIncomingMsgID()>0) {
1366     // we have already received an incoming message, so we have started an "SyncML_Incoming" blocks sometime
1367     PDEBUGENDBLOCK("SyncML_Incoming"); // terminate debug block of previous incoming message as well
1368   }
1369   #endif
1370   // ok
1371   return LOCERR_OK; // ok
1372 } // TSyncAgent::NextMessage
1373
1374
1375 // called after successful decoding of an incoming message
1376 bool TSyncAgent::ClientMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
1377 {
1378   // message not authorized by default
1379   fMessageAuthorized=false;
1380
1381   // Check information from SyncHdr
1382   if (
1383     aBad ||
1384     (!(fSynchdrSessionID==smlPCDataToCharP(aContentP->sessionID))) ||
1385     (!(fLocalURI==smlSrcTargLocURIToCharP(aContentP->target)))
1386   ) {
1387     // bad response
1388     PDEBUGPRINTFX(DBG_ERROR,(
1389       "Bad SyncHeader from Server. Syntax %s, SessionID (rcvd/correct) = '%s' / '%s', LocalURI (rcvd/correct) = '%s' / '%s'",
1390       aBad ? "ok" : "BAD",
1391       smlPCDataToCharP(aContentP->sessionID),
1392       fSynchdrSessionID.c_str(),
1393       smlSrcTargLocURIToCharP(aContentP->target),
1394       fLocalURI.c_str()
1395     ));
1396     aStatusCommand.setStatusCode(400); // bad response/request
1397     AbortSession(400,true);
1398     return false;
1399   }
1400   // check for suspend: if we are suspended at this point, this means that we have sent the Suspend Alert already
1401   // in the previous message (due to user suspend request), so we can now terminate the session
1402   if (isSuspending() && isSuspendAlertSent()) {
1403     AbortSession(514,true,LOCERR_USERSUSPEND);
1404     return false;
1405   }
1406   // - RespURI (remote URI to respond to)
1407   if (aContentP->respURI) {
1408     fRespondURI=smlPCDataToCharP(aContentP->respURI);
1409     DEBUGPRINTFX(DBG_PROTO,("RespURI set to = '%s'",fRespondURI.c_str()));
1410   }
1411   // authorization check
1412   // Note: next message will be started not before status for last one
1413   //       has been processed. Commands issued before will automatically
1414   //       be queued by issuePtr()
1415   // %%% none for now
1416   fSessionAuthorized=true;
1417   fMessageAuthorized=true;
1418   // returns false on BAD header (but true on wrong/bad/missing cred)
1419   return true;
1420 } // TSyncAgent::ClientMessageStarted
1421
1422
1423 // determines new package states and sets fInProgress
1424 void TSyncAgent::ClientMessageEnded(bool aIncomingFinal)
1425 {
1426   TLocalDataStorePContainer::iterator pos;
1427
1428   // show status before processing
1429   PDEBUGPRINTFX(DBG_SESSION,(
1430     "MessageEnded starts   : old outgoing state='%s', old incoming state='%s', %sNeedToAnswer",
1431     PackageStateNames[fOutgoingState],
1432     PackageStateNames[fIncomingState],
1433     fNeedToAnswer ? "" : "NO "
1434   ));
1435   bool allFromClientOnly=false;
1436   // process exceptions
1437   if (fAborted) {
1438     PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
1439     fOutgoingState=psta_idle;
1440     fIncomingState=psta_idle;
1441     fInProgress=false;
1442   } // if aborted
1443   else if (!fMessageAuthorized) {
1444     // not authorized messages will just be ignored, so
1445     // nothing changes in states
1446     // %%% this will probably not really work, as we would need to repeat the last
1447     //     message in this (unlikely) case that fMessageAuthorized is not set for
1448     //     a non-first message (first message case is handled in handleHeaderStatus)
1449     DEBUGPRINTFX(DBG_ERROR,("***** received Message not authorized, ignore and DONT end package"));
1450     fInProgress=true;
1451   }
1452   else {
1453     fInProgress=true; // assume we need to continue
1454     // Note: the map phase will not take place, if all datastores are in
1455     //       send-to-server-only mode and we are not in non-conformant old
1456     //       synthesis-compatible fCompleteFromClientOnly mode.
1457     if (!fCompleteFromClientOnly) {
1458       // let all local datastores know that message has ended
1459       allFromClientOnly=true;
1460       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1461         // check sync modes
1462         if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
1463           allFromClientOnly=false;
1464           break;
1465         }
1466       }
1467     }
1468     // new outgoing state is determined by the incomingState of this message
1469     // (which is the answer to the <final/> message of the previous outgoing package)
1470     if (fNewOutgoingPackage && fIncomingState!=psta_idle) {
1471       // last message sent was an end-of-package, so next will be a new package
1472       if (fIncomingState==psta_init) {
1473         // server has responded (or is still responding) to our finished init,
1474         // so client enters sync state now (but holds back sync until server
1475         // has finished init)
1476         fOutgoingState=psta_sync;
1477       }
1478       else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1479         // server has started (or already finished) sending statuses for our
1480         // <sync> or its own <sync>
1481         // client can enter map state (but holds back maps until server
1482         // has finished sync/initsync). In case of allFromClientOnly, we skip the map phase
1483         // but only if there is no need to answer.
1484         // Otherwise, this is most probably an old (pre 2.9.8.2) Synthesis server that has
1485         // sent an empty <sync> (and the status for it has set fNeedToAnswer), so we still
1486         // go to map phase.
1487         if (allFromClientOnly && !fNeedToAnswer) {
1488           fOutgoingState=psta_supplement; // all datastores are from-client-only, skip map phase
1489           PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode, and no need to answer: skip map phase"));
1490         }
1491         else {
1492           fOutgoingState=psta_map; // Some datastores do from-server-only or twoway, so we need a map phase
1493           allFromClientOnly=false; // do not skip map phase
1494         }
1495       }
1496       else {
1497         // map is finished as well, we might need extra packages just to
1498         // finish getting results for map commands
1499         fOutgoingState=psta_supplement;
1500       }
1501     }
1502     // New incoming state is simply derived from the incoming state of
1503     // this message
1504     if (aIncomingFinal && fIncomingState!=psta_idle) {
1505       if (fIncomingState==psta_init) {
1506         // switch to sync
1507         fIncomingState=psta_sync;
1508       }
1509       else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
1510         // check what to do
1511         if (allFromClientOnly) {
1512           // no need to answer and allFromClientOnly -> this is the end of the session
1513           fIncomingState=psta_supplement;
1514           fInProgress=false; // normally, at end of map answer, we are done
1515         }
1516         else {
1517           fIncomingState=psta_map;
1518         }
1519       }
1520       else {
1521         // end of a map phase - end of session (if no fNeedToAnswer)
1522         fIncomingState=psta_supplement;
1523         // this only ALLOWS ending the session, but it will continue as long
1524         // as more than OK for SyncHdr (fNeedToAnswer) must be sent
1525         fInProgress=false; // normally, at end of map answer, we are done
1526       }
1527     }
1528     // continue anyway as long as we need to answer
1529     if (fNeedToAnswer) fInProgress=true;
1530   }
1531   // show states
1532   PDEBUGPRINTFX(DBG_HOT,(
1533     "MessageEnded finishes : new outgoing state='%s', new incoming state='%s', %sNeedToAnswer",
1534     PackageStateNames[fOutgoingState],
1535     PackageStateNames[fIncomingState],
1536     fNeedToAnswer ? "" : "NO "
1537   ));
1538   // let all local datastores know that message has ended
1539   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1540     // let them know
1541     (*pos)->engEndOfMessage();
1542     // Show state of local datastores
1543     PDEBUGPRINTFX(DBG_HOT,(
1544       "Local Datastore '%s': %sState=%s, %s%s sync, %s%s",
1545       (*pos)->getName(),
1546       (*pos)->isAborted() ? "ABORTED - " : "",
1547       (*pos)->getDSStateName(),
1548       (*pos)->isResuming() ? "RESUMED " : "",
1549       (*pos)->isSlowSync() ? "SLOW" : "normal",
1550       SyncModeDescriptions[(*pos)->getSyncMode()],
1551       (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1552     ));
1553   }
1554   // thread might end here, so stop profiling
1555   TP_STOP(fTPInfo);
1556 } // TSyncAgent::ClientMessageEnded
1557
1558
1559 // get credentials/username to authenticate with remote party, NULL if none
1560 SmlCredPtr_t TSyncAgent::newCredentialsForRemote(void)
1561 {
1562   if (fNeedAuth) {
1563     // generate cretentials from username/password
1564     PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA,("Authenticating with server as user '%s'", fServerUser.c_str()));
1565     PDEBUGPRINTFX(DBG_PROTO+DBG_USERDATA+DBG_EXOTIC,("- using nonce '%s'", fRemoteNonce.c_str()));
1566     // NOTE: can be NULL when fServerRequestedAuth is auth_none
1567     return newCredentials(
1568       fServerUser.c_str(),
1569       fServerPassword.c_str()
1570     );
1571   }
1572   else {
1573     // already authorized, no auth needed
1574     return NULL;
1575   }
1576 } // TSyncAgent::newCredentialsForRemote
1577
1578
1579 // get client base
1580 TSyncClientBase *TSyncAgent::getClientBase(void)
1581 {
1582   return static_cast<TSyncClientBase *>(getSyncAppBase());
1583 } // TSyncAgent::getClientBase
1584
1585
1586 // retry older protocol, returns false if no older protocol to try
1587 bool TSyncAgent::retryOlderProtocol(bool aSameVersionRetry, bool aOldMessageInBuffer)
1588 {
1589   if (fIncomingState==psta_idle) {
1590     // if we have not started a session yet and not using oldest protocol already,
1591     // we want to retry with next older SyncML version
1592     if (aSameVersionRetry) {
1593       // just retry same version
1594       PDEBUGPRINTFX(DBG_PROTO,("Retrying session start with %s",SyncMLVerProtoNames[fSyncMLVersion]));
1595     }
1596     else if (fSyncMLVersion>getSessionConfig()->fMinSyncMLVersionSupported) {
1597       // next lower
1598       fSyncMLVersion=(TSyncMLVersions)(((uInt16)fSyncMLVersion)-1);
1599       PDEBUGPRINTFX(DBG_PROTO,("Server does not support our SyncML version, trying with %s",SyncMLVerProtoNames[fSyncMLVersion]));
1600     }
1601     else {
1602       // cannot retry
1603       return false;
1604     }
1605     // retry
1606     retryClientSessionStart(aOldMessageInBuffer);
1607     return true;
1608   }
1609   // session already started or no older protocol to try
1610   return false;
1611 } // TSyncAgent::retryOlderProtocol
1612
1613
1614 // prepares client session such that it will do a retry to start a session
1615 // (but keeping already received auth/nonce/syncML-Version state)
1616 void TSyncAgent::retryClientSessionStart(bool aOldMessageInBuffer)
1617 {
1618   TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
1619
1620   // now restarting
1621   PDEBUGPRINTFX(DBG_HOT,("=================> Retrying Client Session Start"));
1622   bool newSessionForAuthRetry = configP->fNewSessionForAuthRetry;
1623   bool noRespURIForAuthRetry = configP->fNoRespURIForAuthRetry;
1624         // check if we should use modified behaviour (smart retries)
1625   if (configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) {
1626         if (newSessionForAuthRetry) {
1627         // if we had new session for retry, switch to in-session retry now
1628       newSessionForAuthRetry = false;
1629       noRespURIForAuthRetry = false;
1630     }
1631     else {
1632         // if we had in-session retry, try new session retry now
1633       newSessionForAuthRetry = true;
1634       noRespURIForAuthRetry = true;    
1635     }
1636     PDEBUGPRINTFX(DBG_PROTO,("Smart retry with modified behaviour: newSessionForAuthRetry=%d, noRespURIForAuthRetry=%d",newSessionForAuthRetry,noRespURIForAuthRetry));
1637   }
1638   // now retry
1639   if (newSessionForAuthRetry) {
1640     // Notes:
1641     // - must apparently be disabled for SCTS 3.1.2 and possibly Mightyphone
1642     // - must be enabled e.g for for Magically Server
1643     // Create new session ID
1644     StringObjPrintf(fSynchdrSessionID,"%hd",(sInt16)++fClientSessionNo);
1645     // restart message counting at 1
1646     fIncomingMsgID=0;
1647     fOutgoingMsgID=0;
1648     // we must terminate the block here when we reset fIncomingMsgID, as NextMessage
1649     // only closes the incoming block when fIncomingMsgID>0
1650     PDEBUGENDBLOCK("SyncML_Incoming");
1651   }
1652   if (noRespURIForAuthRetry) {
1653     // Notes:
1654     // - must apparently be switched on for Starfish.
1655     // - must apparently be switched off for SCTS 3.1.2.
1656     // make sure we send next msg to the original URL
1657     fRespondURI=fRemoteURI;
1658   }
1659   // - make sure status for SyncHdr will not be generated!
1660   forgetHeaderWaitCommands();
1661   // check if we have already started next outgoing message
1662   if (!fOutgoingStarted) {
1663     if (aOldMessageInBuffer) {
1664       // make sure we start with a fresh output buffer
1665       // Note: This usually only occur when we are not currently parsing
1666       //       part of the buffer. If we are parsing, the remaining incoming
1667       //       message gets cleared as well.
1668       getClientBase()->clrUnreadSmlBufferdata();
1669     }
1670     // start a new message
1671     issueHeader(false);
1672   }
1673   else {
1674     if (aOldMessageInBuffer) {
1675       PDEBUGPRINTFX(DBG_ERROR,("Warning - restarting session with old message in output buffer"));
1676     }
1677   }
1678   // - make sure subsequent commands (most probably statuses for Alerts)
1679   //   don't get processed
1680   AbortCommandProcessing(0); // silently discard all further commands
1681   // - make sure possible processing errors do not abort the session
1682   fIgnoreMsgErrs = true;
1683 } // TSyncAgent::retryClientSessionStart
1684
1685
1686 #endif // SYSYNC_CLIENT
1687
1688 #ifdef SYSYNC_SERVER
1689
1690 // undefine these only for tests. Introduced to find problem with T68i
1691 #define RESPURI_ONLY_WHEN_NEEDED
1692
1693 // create a RespURI string. If none needed, return NULL
1694 SmlPcdataPtr_t TSyncAgent::newResponseURIForRemote(void)
1695 {
1696   // do it in a transport-independent way, therefore let dispatcher do it
1697   string respURI; // empty string
1698   if (fUseRespURI) {  
1699     getSyncAppBase()->generateRespURI(
1700       respURI,  // remains unaffected if no RespURI could be calculated
1701       fInitialLocalURI.c_str(), // initial URI used by remote to send first message
1702       fLocalSessionID.c_str()  // server generated unique session ID
1703     );
1704     // Omit RespURI if local URI as seen by client is identical
1705     if (getServerConfig()->fRespURIOnlyWhenDifferent) {
1706       // create RespURI only if different from original URI
1707       if (respURI==fLocalURI) {
1708         respURI.erase();
1709         DEBUGPRINTFX(DBG_SESSION,(
1710           "Generated RespURI and sourceLocURI are equal (%s)-> RespURI omitted",
1711           fLocalURI.c_str()
1712         ));
1713       }
1714     }
1715   }
1716   // Note: returns NULL if respURI is empty string
1717   return newPCDataOptString(respURI.c_str());
1718 } // newResponseURIForRemote
1719
1720
1721 // called after successful decoding of an incoming message
1722 bool TSyncAgent::ServerMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand &aStatusCommand, bool aBad)
1723 {
1724   // message not authorized by default
1725   fMessageAuthorized=false;
1726   
1727   // Get information from SyncHdr which is needed for answers
1728   // - session ID to be used for responses
1729   fSynchdrSessionID=smlPCDataToCharP(aContentP->sessionID);
1730   // - local URI (as seen by remote client)
1731   fLocalURI=smlSrcTargLocURIToCharP(aContentP->target);
1732   fLocalName=smlSrcTargLocNameToCharP(aContentP->target);
1733   // - also remember URI to which first message was sent
1734   // %%% note: incoming ID is not a criteria, because it might be >1 due to
1735   //     client retrying something which it thinks is for the same session
1736   //if (fIncomingMsgID==1) {
1737   if (fOutgoingMsgID==0) {
1738     // this is the first message, remember first URI used to contact server
1739     // (or set preconfigured string from <externalurl>)
1740     if (getServerConfig()->fExternalURL.empty())
1741       fInitialLocalURI=fLocalURI; // use what client sends to us
1742     else
1743       fInitialLocalURI=getServerConfig()->fExternalURL; // use preconfigured URL
1744     // Many clients, including SCTS send the second login attempt with a MsgID>1,
1745     // and depending on how they handle RespURI, they might get a new session for that
1746     // -> so, just handle the case that a new session does not start with MsgID=1
1747     if (fIncomingMsgID>1) {
1748       PDEBUGPRINTFX(DBG_ERROR,(
1749         "New session gets first message with MsgID=%ld (should be 1). Might be due to retries, adjusting OutgoingID as well",
1750         (long)fIncomingMsgID
1751       ));
1752       fOutgoingMsgID=fIncomingMsgID-1; // to make it match what client expects
1753     }
1754   }
1755   // - remote URI
1756   fRemoteURI=smlSrcTargLocURIToCharP(aContentP->source);
1757   fRemoteName=smlSrcTargLocNameToCharP(aContentP->source);
1758   // - RespURI (remote URI to respond to, if different from source)
1759   fRespondURI.erase();
1760   if (aContentP->respURI) {
1761     fRespondURI=smlPCDataToCharP(aContentP->respURI);
1762     DEBUGPRINTFX(DBG_PROTO,("RespURI specified = '%s'",fRespondURI.c_str()));
1763   }
1764   if (fRespondURI==fRemoteURI) fRespondURI.erase(); // if specified but equal to remote: act as if not specified
1765   // More checking if header was ok
1766   if (aBad) {
1767     // bad header, only do what is needed to get a status back to client
1768     fSessionAuthorized=false;
1769     fIncomingState=psta_init;
1770     fOutgoingState=psta_init;
1771     fNewOutgoingPackage=true;
1772     // issue header to make sure status can be sent back to client
1773     if (!fMsgNoResp)
1774       issueHeader(false); // issue header, do not prevent responses
1775   }
1776   else {  
1777     // check busy (or expired) case
1778     if (serverBusy()) {
1779       #ifdef APP_CAN_EXPIRE
1780         if (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK) {
1781                 aStatusCommand.setStatusCode(511); // server failure (expired)
1782                 aStatusCommand.addItemString("License expired or invalid");
1783         PDEBUGPRINTFX(DBG_ERROR,("License expired or invalid - Please contact Synthesis AG to obtain license"));
1784       }
1785         else
1786         #endif
1787         {
1788         aStatusCommand.setStatusCode(101); // busy
1789       }
1790       issueHeader(false); // issue header, do not prevent responses
1791       AbortSession(0,true); // silently discard rest of commands
1792       return false; // header not ok
1793     }  
1794     // now check what state we are in
1795     if (fIncomingState==psta_idle) {  
1796       // Initialize
1797       // - session-wide authorization not yet there
1798       fSessionAuthorized=false;
1799       fMapSeen=false;
1800       // - session has started, we are processing first incoming
1801       //   package and generating first outgoing package
1802       //   (init, possibly changed to combined init/sync by <sync> in this package)
1803       fIncomingState=psta_init;
1804       fOutgoingState=psta_init;
1805       fNewOutgoingPackage=true;
1806     }
1807     // authorization check
1808     if (fIncomingState>=psta_init) {
1809       // now check authorization
1810       if (!fSessionAuthorized) {    
1811         // started, but not yet permanently authorized
1812         fMessageAuthorized=checkCredentials(
1813           smlSrcTargLocNameToCharP(aContentP->source), // user name in clear text according to SyncML 1.0.1
1814           aContentP->cred, // actual credentials
1815           aStatusCommand
1816         );
1817         // NOTE: aStatusCommand has now the appropriate status and chal (set by checkCredentials())
1818         // if credentials do not match, stop processing commands (but stay with the session)
1819         if (!fMessageAuthorized) {
1820           AbortCommandProcessing(aStatusCommand.getStatusCode());  
1821           PDEBUGPRINTFX(DBG_PROTO,("Authorization failed with status %hd, stop command processing",aStatusCommand.getStatusCode()));
1822         }
1823         // now determine if authorization is permanent or not
1824         if (fMessageAuthorized) {
1825           fAuthFailures=0; // reset count
1826           if (messageAuthRequired()) {
1827             // each message needs autorisation again (or no auth at all)
1828             // - 200 ok, next message needs authorization again (or again: none)
1829             fSessionAuthorized=false; // no permanent authorization
1830             aStatusCommand.setStatusCode(200);
1831             // - add challenge for next auth (different nonce)
1832             aStatusCommand.setChallenge(newSessionChallenge());
1833             PDEBUGPRINTFX(DBG_PROTO,("Authorization ok, but required again for subsequent messages: 200 + chal"));
1834           }
1835           else {
1836             // entire session is authorized
1837             fSessionAuthorized=true; // permanent authorization
1838             // - 212 authentication accepted (or 200 if none is reqired at all)          
1839             aStatusCommand.setStatusCode(requestedAuthType()==auth_none ? 200 : 212);
1840             // - add challenge for next auth (in next session, but as we support carry
1841             //   forward via using sessionID, we need to send one here as well)
1842             aStatusCommand.setChallenge(newSessionChallenge());
1843             PDEBUGPRINTFX(DBG_PROTO,("Authorization accepted: 212"));
1844           }
1845         }
1846       } // authorisation check
1847       else {
1848         // already authorized from previous message
1849         PDEBUGPRINTFX(DBG_PROTO,("Authorization ok from previous request: 200"));
1850         fMessageAuthorized=true;
1851       }
1852       // Start response message AFTER auth check, to allow issueHeader
1853       // to check auth state and customize the header accordingly (no
1854       // RespURI for failed auth for example)
1855       if (!fMsgNoResp) {
1856         issueHeader(false); // issue header, do not prevent responses
1857       }
1858     } // if started at least
1859   } // if not aBad
1860   // return startmessage status
1861   // debug info
1862   #ifdef SYDEBUG
1863   if (PDEBUGMASK & DBG_SESSION) {
1864     PDEBUGPRINTFX(DBG_SESSION,(
1865       "---> MessageStarted, Message %sauthorized, incoming state='%s', outgoing state='%s'",
1866       fMessageAuthorized ? "" : "NOT ",
1867       PackageStateNames[fIncomingState],
1868       PackageStateNames[fOutgoingState]
1869     ));
1870         TLocalDataStorePContainer::iterator pos;
1871     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1872       // Show state of local datastores
1873       PDEBUGPRINTFX(DBG_SESSION,(
1874         "Local Datastore '%s': State=%s, %s%s sync, %s%s",
1875         (*pos)->getName(),
1876         (*pos)->getDSStateName(),
1877         (*pos)->isResuming() ? "RESUMED " : "",
1878         (*pos)->fSlowSync ? "SLOW" : "normal",
1879         SyncModeDescriptions[(*pos)->fSyncMode],
1880         (*pos)->fServerAlerted ? ", Server-Alerted" : ""
1881       ));
1882     }
1883   }
1884   #endif
1885   // final check for too many auth failures
1886   if (!fMessageAuthorized) {
1887     #ifdef NO_NONCE_OLD_BEAHVIOUR
1888     AbortSession(aStatusCommand.getStatusCode(),true); // local error
1889     // avoid special treatment of non-authorized message, we have aborted, this is enough
1890     fMessageAuthorized=true;
1891     #else
1892     // Unsuccessful auth, count this
1893     fAuthFailures++;
1894     PDEBUGPRINTFX(DBG_ERROR,(
1895       "Authorization failed %hd. time, (any reason), sending status %hd",
1896       fAuthFailures,
1897       aStatusCommand.getStatusCode()
1898     ));
1899     // - abort session after too many auth failures
1900     if (fAuthFailures>=MAX_AUTH_ATTEMPTS) {
1901       PDEBUGPRINTFX(DBG_ERROR,("Too many (>=%hd) failures, aborting session",MAX_AUTH_ATTEMPTS));
1902       AbortSession(400,true);
1903     }
1904     #endif        
1905   }  
1906   // returns false on BAD header (but true on wrong/bad/missing cred)
1907   return true;
1908 } // TSyncAgent::ServerMessageStarted
1909
1910
1911 void TSyncAgent::ServerMessageEnded(bool aIncomingFinal)
1912 {
1913   bool alldone;
1914   TPackageStates newoutgoingstate,newincomingstate;
1915   TLocalDataStorePContainer::iterator pos;
1916   bool allFromClientOnly=false;
1917   
1918   // Incoming message ends here - what is following are commands initiated by the server
1919   // not directly related to a incoming command.
1920   PDEBUGENDBLOCK("SyncML_Incoming");
1921   // assume that outgoing package is NOT finished, so outgoing state does not change
1922   newoutgoingstate=fOutgoingState;
1923   // new incoming state depends on whether this message is final or not
1924   if ((aIncomingFinal || (fIncomingState==psta_supplement)) && fMessageAuthorized) {
1925     // Note: in supplement state, incoming final is not relevant (may or may not be present, there is
1926     //       no next phase anyway 
1927     // find out if this is a shortened session (no map phase) due to
1928     // from-client-only in all datastores
1929     if (!fCompleteFromClientOnly) {
1930       allFromClientOnly=true;
1931       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
1932         // check sync modes
1933         if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) {
1934           allFromClientOnly=false;
1935           break;
1936         }
1937       }
1938     }
1939     // determine what package comes next
1940     switch (fIncomingState) {
1941       case psta_init :
1942         newincomingstate=psta_sync;
1943         break;
1944       case psta_sync :
1945       case psta_initsync :
1946         // end of sync phase means end of session if all datastores are in from-client-only mode
1947         if (allFromClientOnly) {
1948           PDEBUGPRINTFX(DBG_PROTO+DBG_HOT,("All datastores in from-client-only mode: don't expect map phase from client"));
1949           newincomingstate=psta_supplement;
1950         }
1951         else {
1952           newincomingstate=psta_map;
1953         }
1954         break;
1955       case psta_map :
1956       case psta_supplement : // supplement state does not exit automatically
1957         // after map, possibly some supplement status/alert 222 messages are needed from client
1958         newincomingstate=psta_supplement;
1959         break;
1960       default:
1961         // by default, back to idle
1962         newincomingstate=psta_idle;
1963         break;
1964     } // switch
1965   }
1966   else {
1967     // not final or not authorized: no change in state
1968     newincomingstate=fIncomingState;
1969   }
1970   // show status before processing
1971   PDEBUGPRINTFX(DBG_SESSION,(
1972     "---> MessageEnded starts   : old incoming state='%s', old outgoing state='%s', %sNeedToAnswer",
1973     PackageStateNames[fIncomingState],
1974     PackageStateNames[fOutgoingState],
1975     fNeedToAnswer ? "" : "NO "
1976   ));
1977   // process
1978   if (isAborted()) {
1979     // actual aborting has already taken place
1980     PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'aborted' -> MessageEnded ends package and session"));
1981     newoutgoingstate=psta_idle;
1982     newincomingstate=psta_idle;
1983     fInProgress=false;
1984   } // if aborted
1985   else if (isSuspending()) {
1986     // only flagged for suspend - but datastores are not yet aborted, do it now
1987     AbortSession(514,true,getAbortReasonStatus());
1988     PDEBUGPRINTFX(DBG_ERROR,("***** Session is flagged 'suspended' -> MessageEnded ends package and session"));
1989     newoutgoingstate=psta_idle;
1990     newincomingstate=psta_idle;
1991     fInProgress=false;
1992   }
1993   else if (!fMessageAuthorized) {
1994     // not authorized messages will just be ignored, no matter if final or not,
1995     // so outgoing will NEVER be final on non-authorized messages
1996     // %%% before 1.0.4.9, this was fInProgress=true
1997     //  DEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore and DONT end package, session continues"));
1998     //  fInProgress=true; 
1999     PDEBUGPRINTFX(DBG_ERROR,("***** Message not authorized, ignore msg and terminate session"));
2000     fInProgress=false; 
2001   }
2002   else {
2003     // determine if session continues living or not
2004     // - if in other than idle state, session will continue
2005     fInProgress =
2006       (newincomingstate!=psta_idle) || // if not idle, we'll continue
2007       !fMessageAuthorized; // if not authorized, we'll continue as well (retrying auth)   
2008     // Check if we need to send an Alert 222 to get more messages of this package
2009     if (!aIncomingFinal) {
2010       // not end of incoming package
2011       #ifndef ALWAYS_CONTINUE222
2012       if (!fNeedToAnswer)
2013       #endif
2014       {
2015         #ifdef COMBINE_SYNCANDMAP
2016         // %%% make sure session gets to an end in case combined sync/map was used
2017         if (fMapSeen && fIncomingState==psta_map && fOutgoingState==psta_map) {
2018           DEBUGPRINTFX(DBG_HOT,("********** Incoming, non-final message in (combined)map state needs no answer -> force end of outgoing package"));          
2019           newoutgoingstate=psta_idle;
2020         }
2021         else
2022         #endif
2023         {
2024           // detected 222-loop on init here: when we have nothing to answer in init
2025           // and nothing is alerted -> break session
2026           // %%% not sure if this is always ok
2027           if (fIncomingState<=psta_init) {
2028             PDEBUGPRINTFX(DBG_ERROR,("############## Looks like if we were looping in an init-repeat loop -> force final"));
2029             fInProgress=false;
2030             fOutgoingState=psta_idle;
2031           }
2032           else {
2033             // not final, and nothing to answer otherwise: create alert-Command to request more info
2034             TAlertCommand *alertCmdP = new TAlertCommand(this,NULL,(uInt16)222);
2035             // %%% not clear from spec what has to be in item for 222 alert code
2036             //     but there MUST be an Item for the Alert command according to SyncML TK
2037             // - we just put local and remote URIs here 
2038             SmlItemPtr_t itemP = newItem();
2039             itemP->target = newLocation(fRemoteURI.c_str());
2040             itemP->source = newLocation(fLocalURI.c_str());
2041             alertCmdP->addItem(itemP);
2042             ISSUE_COMMAND_ROOT(this,alertCmdP);
2043           }
2044         }
2045       }
2046     }
2047     else {
2048       // end of package, finish processing package
2049       if (fIncomingState==psta_init) {
2050         // - try to load devinf from cache (only if we don't have both datastores and type info already)
2051         if (!fRemoteDataStoresKnown || !fRemoteDataTypesKnown) {
2052           SmlDevInfDevInfPtr_t devinfP;
2053           TStatusCommand dummystatus(this);  
2054           if (loadRemoteDevInf(getRemoteURI(),devinfP)) {
2055             // we have cached devinf, analyze it now
2056             localstatus sta = analyzeRemoteDevInf(devinfP);
2057             PDEBUGPRINTFX(DBG_ERROR,("devInf from Cache could not be analyzed: error=%hd",sta));
2058           }
2059         }
2060         // - if no DevInf for remote datastores cached or received yet,
2061         //   issue GET for it now
2062         if (!fRemoteDataStoresKnown) {
2063           // if we know datastores here, but not types, this means that remote does not have
2064           // CTCap, so it makes no sense to issue a GET again.
2065           #ifndef NO_DEVINF_GET
2066           // end of initialisation package, but datastores not known yet
2067           // (=no DevInf Put received) --> ask for devinf now
2068           PDEBUGPRINTFX(DBG_REMOTEINFO,("No DevInf received or cached, request DevInf using GET command"));
2069           TGetCommand *getcommandP = new TGetCommand(this);
2070           getcommandP->addTargetLocItem(SyncMLDevInfNames[fSyncMLVersion]);
2071           string devinftype=SYNCML_DEVINF_META_TYPE;
2072           addEncoding(devinftype);
2073           getcommandP->setMeta(newMetaType(devinftype.c_str()));
2074           ISSUE_COMMAND_ROOT(this,getcommandP);
2075           #endif
2076         }
2077       }
2078     }
2079     // make sure syncing local datastores get informed of end-of-<Sync>-message
2080     if (fIncomingState==psta_sync || fIncomingState==psta_initsync) {
2081       // end of an incoming message of the Sync Package
2082       // - let all local datastores know, this is now the time to generate
2083       //   <sync> commands, if needed
2084       // Note: if there are SyncEnd commands delayed, this means that this is
2085       //       not yet the time to start <sync> commands. Instead, when all
2086       //       queued SyncEnd commands are executed later, engEndOfSyncFromRemote()
2087       //       will be called with the endOfAllSyncCommands flag true instead
2088       //       of now. 
2089       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2090         (*pos)->engEndOfSyncFromRemote(aIncomingFinal && !delayedSyncEndsPending());
2091       }
2092     }
2093     // Detect outgoing package state transitions
2094     // - init to sync
2095     if (fOutgoingState==psta_init && newincomingstate>psta_init) {
2096       // new outgoing state is sync.
2097       // Note: In combined init&sync mode, sync command received in init state
2098       //       will set outgoing state from init to init-sync while processing message,
2099       //       so no transition needs to be detected here
2100       newoutgoingstate=psta_sync;
2101     }
2102     // - sync to map
2103     else if (
2104       (fOutgoingState==psta_sync || fOutgoingState==psta_initsync) && // outgoing is sync..
2105       (newincomingstate>=psta_initsync) && // ..and incoming has finished sync
2106       !allFromClientOnly // ..and this is not a session with all datastores doing from-client-only
2107     ) {
2108       // outgoing message belongs to Sync package
2109       // - ask all local datastores if they are finished with sync command generation
2110       alldone=true;
2111       for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2112         alldone = alldone && (*pos)->isSyncDone();
2113       }
2114       if (alldone) {
2115         // outgoing state changes to map (or supplement if all datastores are from-client-only
2116         PDEBUGPRINTFX(DBG_HOT,("All datastores are done with generating <Sync>"));
2117         newoutgoingstate=psta_map;
2118         #ifdef COMBINE_SYNCANDMAP
2119         // %%% it seems as if 9210 needs combined Sync/Map package and 
2120         if (fMapSeen) {
2121           // prevent FINAL to be sent at end of message
2122           DEBUGPRINTFX(DBG_HOT,("********** Combining outgoing sync and map-response packages into one"));
2123           fOutgoingState=psta_map;
2124         }
2125         #endif
2126       }
2127     }
2128     // - map (or from-client-only sync) to idle
2129     else if (
2130       (fOutgoingState==psta_map && newincomingstate==psta_supplement) ||
2131       (allFromClientOnly && (fOutgoingState==psta_sync || fOutgoingState==psta_initsync))
2132     ) {
2133       // we are going back to idle now
2134       newoutgoingstate=psta_idle;
2135       // session ends if it doesn't need to continue for session-level reasons
2136       if (!sessionMustContinue()) {
2137         PDEBUGPRINTFX(DBG_HOT,("Session completed, now let datastores terminate all sync operations"));
2138         for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2139           // finished with Map: end of sync
2140           (*pos)->engFinishDataStoreSync(); // successful
2141         }
2142         // session ends now
2143         fInProgress=false;
2144         // let custom PUTs which may transmit some session statistics etc. happen now
2145         issueCustomEndPut();
2146       }
2147     }
2148     // - if no need to answer (e.g. nothing to send back except OK status for SyncHdr),
2149     //   session is over now (as well)
2150     if (!fNeedToAnswer) fInProgress=false;
2151   } // else 
2152   // Now finish outgoing message
2153   #ifdef DONT_FINAL_BAD_AUTH_ATTEMPTS
2154   // - PREVENT final flag after failed auth attempts
2155   if(FinishMessage(
2156     fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
2157     !fMessageAuthorized || serverBusy() // busy or unauthorized prevent final flag at any rate
2158   ))
2159   #else
2160   // - DO set final flag after failed auth attempts
2161   if(FinishMessage(
2162     !fMessageAuthorized || fOutgoingState!=newoutgoingstate || fOutgoingState==psta_idle, // final when state changed or idle
2163     serverBusy() // busy prevents final flag at any rate
2164   ))  
2165   #endif
2166   {
2167     // outgoing state HAS changed
2168     fOutgoingState=newoutgoingstate;
2169   }
2170   // Now update incoming state
2171   fIncomingState=newincomingstate;
2172   // show states
2173   PDEBUGPRINTFX(DBG_HOT,(
2174     "---> MessageEnded finishes : new incoming state='%s', new outgoing state='%s', %sNeedToAnswer",
2175     PackageStateNames[fIncomingState],
2176     PackageStateNames[fOutgoingState],
2177     fNeedToAnswer ? "" : "NO "
2178   ));
2179   // let all local datastores know that message has ended
2180   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2181     // let them know
2182     (*pos)->engEndOfMessage();
2183     // Show state of local datastores
2184     PDEBUGPRINTFX(DBG_HOT,(
2185       "Local Datastore '%s': State=%s, %s%s sync, %s%s",
2186       (*pos)->getName(),
2187       (*pos)->getDSStateName(),
2188       (*pos)->isResuming() ? "RESUMED " : "",
2189       (*pos)->fSlowSync ? "SLOW" : "normal",
2190       SyncModeDescriptions[(*pos)->fSyncMode],
2191       (*pos)->fServerAlerted ? ", Server-Alerted" : ""
2192     ));
2193   }
2194   // End of outgoing message
2195   PDEBUGPRINTFX(DBG_HOT,(
2196     "=================> Finished generating outgoing message #%ld, request=%ld",
2197     (long)fOutgoingMsgID,
2198     (long)getSyncAppBase()->requestCount()
2199   ));
2200   PDEBUGENDBLOCK("SyncML_Outgoing");
2201 } // TSyncAgent::ServerMessageEnded
2202
2203
2204 void TSyncAgent::RequestEnded(bool &aHasData)
2205 {
2206   // to make sure, finish any unfinished message
2207   FinishMessage(true); // final allowed, as this is an out-of-normal-order case anyway  
2208   // if we need to answer, we have data
2209   // - SyncML specs 1.0.1 says that server must always respond, even if message
2210   //   contains of a Status for the SyncHdr only 
2211   aHasData=true;
2212   // %%% first drafts of 1.0.1 said that SyncHdr Status only messages must not be sent...
2213   // aHasData=fNeedToAnswer; // %%%
2214   
2215   // now let all datastores know that request processing ends here (so they might
2216   // prepare for a thread switch)
2217   // terminate sync with all datastores
2218   TLocalDataStorePContainer::iterator pos;
2219   for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
2220     // now let them know that request has ended
2221     (*pos)->engRequestEnded();
2222   }
2223 } // TSyncAgent::RequestEnded
2224
2225
2226 // Called at end of Request, returns true if session must be deleted
2227 // returns flag if data to be returned. If response URI was specified
2228 // different, it is returned in aRespURI, otherwise aRespURI is empty.
2229 bool TSyncAgent::EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes)
2230 {
2231   // count incoming data
2232   fIncomingBytes+=aReqBytes;
2233   // let client or server do what is needed
2234   if (fMessageRetried) {
2235     // Message processing cancelled
2236     CancelMessageProcessing();
2237     // Nothing happened 
2238           // - but count bytes 
2239     fOutgoingBytes+=fBufferedAnswerSize;
2240     PDEBUGPRINTFX(DBG_HOT,(
2241             "========= Finished retried request with re-sending buffered answer (session %sin progress), incoming bytes=%ld, outgoing bytes=%ld",
2242             fInProgress ? "" : "NOT ",
2243             (long)aReqBytes,
2244             (long)fBufferedAnswerSize
2245           ));
2246           aHasData=false; // we do not have data in the sml instance (but we have/had some in the retry re-send buffer)
2247   }
2248   else {
2249     // end request
2250           RequestEnded(aHasData);
2251           // count bytes
2252     fOutgoingBytes+=getOutgoingMessageSize();
2253           PDEBUGPRINTFX(DBG_HOT,(
2254             "========= Finished request (session %sin progress), processing time=%ld msec, incoming bytes=%ld, outgoing bytes=%ld",
2255             fInProgress ? "" : "NOT ",
2256             (long)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) * nanosecondsPerLinearTime / 1000000),
2257             (long)aReqBytes,
2258       (long)getOutgoingMessageSize()
2259           ));     
2260           // return RespURI (is empty if none specified or equal to message source URI)
2261           aRespURI = fRespondURI;
2262   }
2263   if (!fInProgress) {
2264     // terminate datastores here already in case we are not in progress any more
2265     // here. If any of the datastores are in progress at this point, this is a
2266     // protocol violation, and therefore we return a 400.
2267     // Note: resetting the session later will also call TerminateDatastores, but then
2268     //       with a 408 (which is misleading when the session ends here due to protocol
2269     //       problem.
2270     TerminateDatastores(400);
2271   }
2272   //%%% moved to happen before end of SyncML_Outgoing 
2273   //PDEBUGENDBLOCK("SyncML_Incoming");
2274   if (fRequestMinTime>0) {
2275     // make sure we spent enough time with this request, if not, artificially extend time
2276     // - get number of seconds already spent
2277     sInt32 t =
2278       (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
2279     // - delay if needed
2280     if (t<fRequestMinTime) {
2281           PDEBUGPRINTFX(DBG_HOT,(
2282             "requestmintime is set to %ld seconds, we have spent only %ld seconds so far -> sleeping %ld seconds",
2283             (long)fRequestMinTime,
2284             (long)t,
2285             (long)fRequestMinTime-t
2286           ));
2287       CONSOLEPRINTF(("  ...delaying response by %ld seconds because requestmintime is set to %ld",fRequestMinTime,fRequestMinTime-t));
2288           sleepLineartime((lineartime_t)(fRequestMinTime-t)*secondToLinearTimeFactor);
2289     }
2290   }
2291   // thread might end here, so stop profiling
2292   TP_STOP(fTPInfo);
2293   #ifdef SYDEBUG
2294   // we are not the main thread any longer
2295   getDbgLogger()->DebugThreadOutputDone();
2296   #endif
2297   // return true if session is not in progress any more
2298   return(!fInProgress);
2299 } // TSyncAgent::EndRequest
2300
2301
2302 // buffer answer in the session's buffer if transport allows it
2303 Ret_t TSyncAgent::bufferAnswer(MemPtr_t aAnswer, MemSize_t aAnswerSize)
2304 {
2305   // get rid of previous buffered answer
2306   if (fBufferedAnswer)
2307     delete[] fBufferedAnswer;
2308   fBufferedAnswer=NULL;
2309   fBufferedAnswerSize=0;
2310   // save new answer (if not empty)
2311   if (aAnswer && aAnswerSize) {
2312     // allocate buffer
2313     fBufferedAnswer = new unsigned char[aAnswerSize];
2314     // copy data
2315     if (!fBufferedAnswer) return SML_ERR_NOT_ENOUGH_SPACE;
2316     memcpy(fBufferedAnswer,aAnswer,aAnswerSize);
2317     // save size
2318     fBufferedAnswerSize=aAnswerSize;
2319   }
2320   return SML_ERR_OK;
2321 } // TSyncAgent::bufferAnswer
2322
2323
2324 // get buffered answer from the session's buffer if there is any
2325 void TSyncAgent::getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize)
2326 {
2327   aAnswer=fBufferedAnswer;
2328   aAnswerSize=fBufferedAnswerSize;  
2329   PDEBUGPRINTFX(DBG_HOT,(
2330     "Buffered answer read from session: %ld bytes",
2331     fBufferedAnswerSize
2332   ));
2333 } // TSyncAgent::getBufferedAnswer
2334
2335
2336 // returns remaining time for request processing [seconds]
2337 sInt32 TSyncAgent::RemainingRequestTime(void)
2338 {
2339         if (IS_CLIENT) {
2340         // clients don't process requests, so there's no limit
2341         return 0x7FFFFFFF; // "infinite"
2342   }
2343   else {
2344     // if no request timeout specified, use session timeout
2345     sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout;
2346     // calculate number of remaining seconds
2347     return
2348       t==0 ?
2349         0x7FFFFFFF : // "infinite"
2350         t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
2351   }
2352 } // TSyncAgent::RemainingRequestTime
2353
2354
2355
2356
2357
2358 // process a Map command in context of server session
2359 bool TSyncAgent::processMapCommand(
2360   SmlMapPtr_t aMapCommandP,      // the map command contents
2361   TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
2362   bool &aQueueForLater
2363 )
2364 {
2365   bool allok=false; // assume not ok
2366   localstatus sta;
2367
2368   // remember that this session has seen a map command already
2369   fMapSeen=true;
2370   // Detecting a map command in supplement incomin state indicates a
2371   // client like funambol that send to many <final/> in pre-map phases
2372   // (such as in 222-Alert messages). So we reset the session state back
2373   // to incoming/outgoing map to correct this client bug
2374   if (fIncomingState==psta_supplement) {
2375     // back to map phase, as client apparently IS still in map phase, despite too many
2376     // <final/> sent
2377     PDEBUGPRINTFX(DBG_ERROR,(
2378       "Warning: detected <Map> command after end of Map phase - buggy client sent too many <final/>. Re-entering map phase to compensate"
2379     ));
2380     fIncomingState=psta_map;
2381     fOutgoingState=psta_map;
2382   }
2383   // find database(s)
2384   // - get relative URI of requested database
2385   const char *targetdburi = smlSrcTargLocURIToCharP(aMapCommandP->target);
2386   TLocalEngineDS *datastoreP = findLocalDataStoreByURI(targetdburi);
2387   if (!datastoreP) {
2388     // no such local datastore
2389     aStatusCommand.setStatusCode(404); // not found
2390   }
2391   else {
2392     // local datastore found
2393     // - maps can be processed when we are at least ready for early (chached by client from previous session) maps
2394     if (datastoreP->testState(dssta_syncmodestable)) {
2395       // datastore is ready 
2396       PDEBUGBLOCKFMT(("ProcessMap", "Processing items from Map command", "datastore=%s", targetdburi));
2397       allok=true; // assume all ok
2398       SmlMapItemListPtr_t nextnode = aMapCommandP->mapItemList;
2399       while (nextnode) {
2400         POINTERTEST(nextnode->mapItem,("MapItemList node w/o MapItem"));
2401         PDEBUGPRINTFX(DBG_HOT,(
2402           "Mapping remoteID='%s' to localID='%s'",
2403           smlSrcTargLocURIToCharP(nextnode->mapItem->source),
2404           smlSrcTargLocURIToCharP(nextnode->mapItem->target)
2405         ));
2406         sta = datastoreP->engProcessMap(
2407           #ifdef DONT_STRIP_PATHPREFIX_FROM_REMOTEIDS
2408           smlSrcTargLocURIToCharP(nextnode->mapItem->source),
2409           #else 
2410           relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->source)),
2411           #endif
2412           relativeURI(smlSrcTargLocURIToCharP(nextnode->mapItem->target))
2413         );
2414         if (sta!=LOCERR_OK) {
2415           PDEBUGPRINTFX(DBG_ERROR,("    Mapping FAILED!"));
2416           aStatusCommand.setStatusCode(sta);
2417           allok=false;
2418           break;
2419         }
2420         // next mapitem
2421         nextnode=nextnode->next;
2422       } // while more mapitems
2423       // terminate Map command
2424       allok=datastoreP->MapFinishAsServer(allok,aStatusCommand);
2425       PDEBUGENDBLOCK("ProcessMap");
2426     }
2427     else {
2428       // we must queue the command for later execution
2429       aQueueForLater=true;
2430       allok=true; // ok for now, we'll re-execute this later
2431     }
2432   } // database found
2433   return allok;
2434 } // TSyncAgent::processMapCommand
2435     
2436
2437 // get next nonce string top be sent to remote party for subsequent MD5 auth
2438 void TSyncAgent::getNextNonce(const char *aDeviceID, string &aNextNonce)
2439 {
2440   fLastNonce.erase();
2441   if (getServerConfig()->fAutoNonce) {
2442     // generate nonce out of source ref and session ID
2443     // This scheme can provide nonce carrying forward between 
2444     // sessions by initializing lastNonce with the srcRef/sessionid-1
2445     // assuming client to use nonce from last session.
2446     sInt32 sid;
2447     // use current day as nonce varying number
2448     sid = time(NULL) / 3600 / 24; 
2449     generateNonce(fLastNonce,aDeviceID,sid);
2450   }
2451   else {
2452     // get constant nonce (if empty, this is NO nonce)
2453     fLastNonce=getServerConfig()->fConstantNonce;
2454   }
2455   // return new nonce
2456   DEBUGPRINTFX(DBG_PROTO,("getNextNonce: created nonce='%s'",fLastNonce.c_str()));
2457   aNextNonce=fLastNonce;
2458 } // TSyncAgent::getNextNonce
2459
2460
2461 // - get nonce string for specified deviceID
2462 void TSyncAgent::getAuthNonce(const char *aDeviceID, string &aAuthNonce)
2463 {
2464   // if no device ID, use session default nonce
2465   if (!aDeviceID) {
2466     TSyncSession::getAuthNonce(aDeviceID,fLastNonce);
2467   }
2468   else {
2469     // Basic nonce mechanism needing no per-device storage:
2470     // - we have no stored last nonce, but we can re-create nonce used
2471     //   for last session with this device by the used algorithm
2472     if (getServerConfig()->fAutoNonce) {
2473       if (fLastNonce.empty()) {
2474         // none available, produce new one
2475         sInt32 sid;
2476         // use current day as nonce varying number
2477         sid = time(NULL) / 3600 / 24; 
2478         generateNonce(fLastNonce,aDeviceID,sid);
2479       }
2480     }
2481     else {
2482       // return constant nonce
2483       fLastNonce=getServerConfig()->fConstantNonce;
2484     }
2485   }
2486   DEBUGPRINTFX(DBG_PROTO,("getAuthNonce: current auth nonce='%s'",fLastNonce.c_str()));
2487   aAuthNonce=fLastNonce;
2488 } // TSyncAgent::getAuthNonce
2489
2490
2491
2492 // info about server status
2493 bool TSyncAgent::serverBusy(void)
2494 {
2495         // return flag (which might have been set by some connection
2496         // limit code in sessiondispatch).
2497         // When app is expired, all server sessions are busy anyway
2498         #ifdef APP_CAN_EXPIRE
2499         return fSessionIsBusy || (getSyncAppBase()->fAppExpiryStatus!=LOCERR_OK);
2500         #else
2501         return fSessionIsBusy;
2502         #endif
2503 } // TSyncAgent::serverBusy
2504
2505
2506 // access to config
2507 TAgentConfig *TSyncAgent::getServerConfig(void)
2508 {
2509   TAgentConfig *scP;
2510   GET_CASTED_PTR(scP,TAgentConfig,getSyncAppBase()->getRootConfig()->fAgentConfigP,DEBUGTEXT("no TAgentConfig","ss1"));
2511   return scP;
2512 } // TSyncAgent::getServerConfig
2513
2514
2515 #endif // SYSYNC_SERVER
2516
2517
2518 // info about requested auth type
2519 TAuthTypes TSyncAgent::requestedAuthType(void)
2520 {
2521         if (IS_SERVER) {
2522         #ifdef SYSYNC_SERVER
2523           return getServerConfig()->fRequestedAuth;
2524     #endif
2525   }
2526   else {
2527         return auth_none; // client does not require auth
2528   }
2529 } // TSyncAgent::requestedAuthType
2530
2531
2532 // check if auth type is allowed
2533 bool TSyncAgent::isAuthTypeAllowed(TAuthTypes aAuthType)
2534 {
2535         if (IS_SERVER) {
2536         #ifdef SYSYNC_SERVER
2537           return aAuthType>=getServerConfig()->fRequiredAuth;
2538     #endif
2539   }
2540   else {
2541         return true; // client accepts any auth
2542   }
2543 } // TSyncAgent::isAuthTypeAllowed
2544
2545
2546 // called when incoming SyncHdr fails to execute
2547 bool TSyncAgent::syncHdrFailure(bool aTryAgain)
2548 {
2549         if (IS_CLIENT) {
2550     // do not try to re-execute the header, just let message processing fail;
2551     // this will cause the client's main loop to try using an older protocol
2552     return false;
2553   }
2554   else {
2555         #ifdef SYSYNC_SERVER
2556     if (!aTryAgain) {
2557       // not already retried executing
2558       // special case: header failed to execute, this means that session must be reset
2559       // - Reset session (aborts all DB transactions etc.)
2560       ResetSession();
2561       PDEBUGPRINTFX(DBG_ERROR,("Trying to recover SyncHdr failure: =========== Session restarted ====================="));
2562       // - now all session infos are gone except this command which is owned by
2563       //   this function alone. Execute it again.
2564       aTryAgain=true;
2565     }
2566     else {
2567       // special special case: header failed to execute the second time
2568       DEBUGPRINTFX(DBG_ERROR,("Fatal internal problem, SyncHdr execution failed twice"));
2569       aTryAgain=false; // just to make sure
2570       SYSYNC_THROW(TSyncException("SyncHdr fatal execution problem"));
2571     }
2572     return aTryAgain;    
2573     #endif
2574   }
2575 } // TSyncAgent::syncHdrFailure
2576
2577
2578 // handle status received for SyncHdr, returns false if not handled
2579 bool TSyncAgent::handleHeaderStatus(TStatusCommand *aStatusCmdP)
2580 {
2581         if (IS_CLIENT) {
2582         #ifdef SYSYNC_CLIENT
2583     TAgentConfig *configP = static_cast<TAgentConfig *>(getRootConfig()->fAgentConfigP);
2584     bool handled=true;
2585     const char *txt;
2586     SmlMetInfMetInfPtr_t chalmetaP=NULL;
2587     SmlChalPtr_t chalP;
2588
2589     // first evaluate possible challenge in header status
2590     chalP = aStatusCmdP->getStatusElement()->chal;
2591     if (chalP) {
2592       chalmetaP = smlPCDataToMetInfP(chalP->meta);
2593       if (chalmetaP) {
2594         sInt16 ty;
2595         // - get auth type
2596         if (!chalmetaP->type) AbortSession(401,true); // missing auth, but no type
2597         txt = smlPCDataToCharP(chalmetaP->type);
2598         PDEBUGPRINTFX(DBG_PROTO,("Remote requests auth type='%s'",txt));
2599         if (StrToEnum(authTypeSyncMLNames,numAuthTypes,ty,txt))
2600           fRemoteRequestedAuth=(TAuthTypes)ty;
2601         else {
2602           AbortSession(406,true); // unknown auth type, not supported
2603           goto donewithstatus;
2604         }
2605         // - get auth format
2606         if (!smlPCDataToFormat(chalmetaP->format, fRemoteRequestedAuthEnc)) {
2607           AbortSession(406,true); // unknown auth format, not supported
2608           goto donewithstatus;
2609         }
2610         // - get next nonce
2611         if (chalmetaP->nextnonce) {
2612           // decode B64
2613           uInt32 l;
2614           uInt8 *nonce = b64::decode(smlPCDataToCharP(chalmetaP->nextnonce), 0, &l);
2615           fRemoteNonce.assign((char *)nonce,l);
2616           b64::free(nonce);
2617         }
2618         // - show
2619         PDEBUGPRINTFX(DBG_PROTO,(
2620           "Next Cred will have type='%s' and format='%s' and use nonce='%s'",
2621           authTypeNames[fRemoteRequestedAuth],
2622           encodingFmtNames[fRemoteRequestedAuthEnc],
2623           fRemoteNonce.c_str()
2624         ));
2625       }
2626       /* %%% do not save here already, we don't know if SyncML version is ok
2627              moved to those status code cases below that signal
2628       // let descendant possibly save auth params
2629       saveRemoteParams();
2630       */
2631     }
2632     // now evaluate status code
2633     switch (aStatusCmdP->getStatusCode()) {
2634       case 101: // Busy
2635         // Abort
2636         AbortSession(101,false);
2637         break;
2638       case 212: // authentication accepted for entire session
2639         fNeedAuth=false; // no need for further auth
2640         PDEBUGPRINTFX(DBG_PROTO,("Remote accepted authentication for entire session"));
2641       case 200: // authentication accepted for this message
2642         // if this is the first authorized message we get an OK for the synchdr, this is
2643         // also the first incoming message that is really processed as init message
2644         if (fIncomingState==psta_idle && fMessageAuthorized) {
2645           // first incoming is expected to be same as first outgoing (init or initsync)
2646           fIncomingState=fOutgoingState;
2647           PDEBUGPRINTFX(DBG_PROTO,("Authenticated successfully with remote server"));
2648         }
2649         else {
2650           PDEBUGPRINTFX(DBG_PROTO,("Authentication with server ok for this message"));
2651         }
2652         // let descendant possibly save auth params
2653         saveRemoteParams();
2654         break;
2655       case 501: // handle a "command not implemented" for the SyncHdr like 513 (indication that server does not like our header)
2656       case 400: // ..and 400 as well (sync4j case, as it seems)
2657       case 513: // bad protocol version
2658       case 505: // bad DTD version (NextHaus/DeskNow case)
2659         // try with next lower protocol
2660         PDEBUGENDBLOCK("processStatus"); // done processing status
2661         if (!retryOlderProtocol()) {
2662           // no older SyncML protocol we can try --> abort
2663           AbortSession(513,false); // server does not know any of our SyncML versions
2664         }
2665         break;
2666       case 401: // bad authentication
2667         // Bad authorisation
2668         if (fAuthRetries==0)
2669           // if first attempt is rejected with "bad", we conclude that the
2670           // last attempt was carrying auth data and was not a attempt to get challenge
2671           // from server. Therefore we count this as two tries (one get chal, one really failing)
2672           fAuthRetries=2;
2673         else
2674           fAuthRetries++; // just count attempt to auth
2675         /* %%% no longer required, is tested below at authfail:
2676         if (fAuthRetries>MAX_AUTH_RETRIES) {
2677           AbortSession(401,false); // abort session, too many retries
2678           break;
2679         }
2680         */
2681         // Treat no nonce like empty nonce to make sure that a server (like SySync old versions...)
2682         // that does not send a nonce at all does not get auth with some old, invalid nonce string included.
2683         if (chalmetaP && chalmetaP->nextnonce==NULL) fRemoteNonce.erase();
2684         // otherwise treat like 407
2685         goto authfail;
2686       case 407: // authentication required
2687         // new since 2.0.4.6: count this as well (normally this happens once when sending
2688         // no auth to the server to force it to send us auth chal first).
2689         fAuthRetries++;
2690       authfail:
2691         PDEBUGPRINTFX(DBG_ERROR,("Authentication failed (status=%hd) with remote server",aStatusCmdP->getStatusCode()));
2692         // Auth fail after we have received a valid response for the init message indicates protocol messed up
2693         if (fIncomingState!=psta_idle) {
2694           AbortSession(400,true); // error in protocol handling from remote
2695           break;
2696         }
2697         // Check if smart retries (with modified in-session vs out-of-session behaviour) are enabled
2698         if (!configP->fSmartAuthRetry && fAuthRetries>MAX_NORMAL_AUTH_RETRIES) {
2699           fAuthRetries = MAX_SMART_AUTH_RETRIES+1; // skip additional smart retries
2700         }
2701         // Missing or bad authorisation, evaluate chal
2702         if (!chalmetaP || fAuthRetries>MAX_SMART_AUTH_RETRIES) {
2703           #ifdef SYDEBUG
2704           if (!chalmetaP) {
2705             PDEBUGPRINTFX(DBG_ERROR,("Bad auth but no challenge in response status -> can't work - no retry"));
2706           }
2707           #endif
2708           AbortSession(aStatusCmdP->getStatusCode(),false); // retries exhausted or no retry possible (no chal) -> stop session
2709           break;
2710         }
2711         // let descendant possibly save auth params
2712         saveRemoteParams();
2713         // modify session for re-start
2714         PDEBUGENDBLOCK("processStatus"); // done processing status
2715         retryClientSessionStart(false); // no previously sent message in the buffer
2716         break;
2717       default:
2718         handled=false; // could not handle status
2719     } // switch
2720   donewithstatus:
2721     // Anyway, reception of status for header enables generation of next message header
2722     // (plus already generated commands such as status for response header)
2723     if (!fMsgNoResp && !isAborted()) {
2724       // issue header now if not already issued above
2725       if (!fOutgoingStarted) {
2726         // interrupt status processing block here as issueHeader will do a start-of-message PDEBUGBLOCK
2727         PDEBUGENDBLOCK("processStatus");
2728         issueHeader(false);
2729         PDEBUGBLOCKDESC("processStatus","finishing processing incoming SyncHdr Status");
2730       }
2731     }
2732     // return handled status
2733     return handled;
2734     #endif // SYSYNC_SERVER
2735   }
2736   else {
2737         // nothing special
2738     return inherited::handleHeaderStatus(aStatusCmdP);
2739   }
2740 } // TSyncAgent::handleHeaderStatus
2741
2742
2743 // - start sync group (called in client or server roles)
2744 bool TSyncAgent::processSyncStart(
2745   SmlSyncPtr_t aSyncP,           // the Sync element
2746   TStatusCommand &aStatusCommand, // pre-set 200 status, can be modified in case of errors
2747   bool &aQueueForLater // will be set if command must be queued for later (re-)execution
2748 )
2749 {
2750         if (IS_CLIENT) {
2751         #ifdef SYSYNC_CLIENT
2752     if (fIncomingState!=psta_sync && fIncomingState!=psta_initsync) {
2753       aStatusCommand.setStatusCode(403); // forbidden in this context
2754       PDEBUGPRINTFX(DBG_ERROR,("Sync command not allowed outside of sync phase (-> 403)"));
2755       AbortSession(400,true);
2756       return false;
2757     }
2758     // just find appropriate database, must be already initialized for sync!
2759     // determine local database to sync with (target)
2760     TLocalEngineDS *datastoreP = findLocalDataStoreByURI(smlSrcTargLocURIToCharP(aSyncP->target));
2761     if (!datastoreP) {
2762       // no such local datastore
2763       PDEBUGPRINTFX(DBG_ERROR,("Sync command for unknown DS locURI '%s' (-> 404)",smlSrcTargLocURIToCharP(aSyncP->target)));
2764       aStatusCommand.setStatusCode(404); // not found
2765       return false;
2766     }
2767     else {
2768       // save the pointer, will e.g. be used to route subsequent server commands
2769       fLocalSyncDatastoreP=datastoreP;
2770       // let local datastore know
2771       return fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
2772     }
2773     return true;
2774                 #endif // SYSYNC_CLIENT
2775   }
2776   else {
2777     #ifdef SYSYNC_SERVER
2778     // Init datastores for sync
2779     localstatus sta = initSync(
2780       smlSrcTargLocURIToCharP(aSyncP->target),  // local datastore
2781       smlSrcTargLocURIToCharP(aSyncP->source)  // remote datastore
2782     );
2783     if (sta!=LOCERR_OK) {
2784       aStatusCommand.setStatusCode(sta);
2785       return false;
2786     }
2787     // let local datastore prepare for sync as server
2788     // - let local process sync command
2789     bool ok=fLocalSyncDatastoreP->engProcessSyncCmd(aSyncP,aStatusCommand,aQueueForLater);
2790     // Note: ok means that the sync command is addressing existing datastores. However,
2791     //       it does not mean that the actual processing is already executed; aQueueForLater
2792     //       could be set!
2793     // if ok and not queued: update package states
2794     if (ok) {
2795       if (fIncomingState==psta_init || fIncomingState==psta_initsync) {
2796         // detected sync command in init package -> this is combined init/sync
2797         #ifdef SYDEBUG
2798         if (fIncomingState==psta_init)
2799           DEBUGPRINTFX(DBG_HOT,("<Sync> started init package -> switching to combined init/sync"));
2800         #endif
2801         // - set new incoming state
2802         fIncomingState=psta_initsync;
2803         // - also update outgoing state, if it is in init package
2804         if (fOutgoingState==psta_init)
2805           fOutgoingState=psta_initsync;
2806       }
2807       else if (fCmdIncomingState!=psta_sync) {
2808         DEBUGPRINTFX(DBG_ERROR,(
2809           "<Sync> found in wrong incoming package state '%s' -> aborting session",
2810           PackageStateNames[fCmdIncomingState]
2811         ));
2812         aStatusCommand.setStatusCode(403); // forbidden
2813         fLocalSyncDatastoreP->engAbortDataStoreSync(403,true); // abort, local problem
2814         ok=false;
2815       }
2816       else {
2817         // - show sync start    
2818         DEBUGPRINTFX(DBG_HOT,(
2819           "<Sync> started, cmd-incoming state='%s', incoming state='%s', outgoing state='%s'",
2820           PackageStateNames[fCmdIncomingState],
2821           PackageStateNames[fIncomingState],
2822           PackageStateNames[fOutgoingState]
2823         ));
2824       }
2825     }
2826     return ok;
2827     #endif // SYSYNC_SERVER
2828   }
2829 } // TSyncAgent::processSyncStart
2830
2831
2832
2833
2834 #ifdef ENGINEINTERFACE_SUPPORT
2835
2836 // Support for EngineModule common interface
2837 // =========================================
2838
2839
2840 /// @brief Get new session key to access details of this session
2841 appPointer TSyncAgent::newSessionKey(TEngineInterface *aEngineInterfaceP)
2842 {
2843         return new TAgentParamsKey(aEngineInterfaceP,this);
2844 } // TSyncAgent::newSessionKey
2845
2846
2847 #ifdef ENGINE_LIBRARY
2848
2849 TSyError TSyncAgent::SessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
2850 {
2851         if (IS_CLIENT) {
2852                 #ifdef SYSYNC_CLIENT
2853     return ClientSessionStep(aStepCmd,aInfoP);
2854                 #endif // SYSYNC_CLIENT
2855   }
2856   else {
2857     #ifdef SYSYNC_SERVER
2858     return ServerSessionStep(aStepCmd,aInfoP);
2859     #endif // SYSYNC_SERVER
2860   }
2861 } // TSyncAgent::SessionStep
2862
2863 #endif // ENGINE_LIBRARY
2864
2865
2866 #ifdef SYSYNC_SERVER
2867
2868 // Server implementation
2869 // ---------------------
2870
2871 #ifndef ENGINE_LIBRARY
2872
2873 // dummy server engine support to allow AsKey from plugins
2874
2875 #warning "using ENGINEINTERFACE_SUPPORT in old-style appbase-rooted environment. Should be converted to real engine usage later"
2876
2877 // Engine factory function for non-Library case
2878 ENGINE_IF_CLASS *newServerEngine(void)
2879 {
2880   // For real engine based targets, newServerEngine must create a target-specific derivate
2881   // of the server engine, which then has a suitable newSyncAppBase() method to create the
2882   // appBase. For old-style environment, a generic TServerEngineInterface is ok, as this
2883   // in turn calls the global newSyncAppBase() which then returns the appropriate
2884   // target specific appBase. Here we just return a dummy server engine base.
2885   return new TDummyServerEngineInterface;
2886 } // newServerEngine
2887
2888 /// @brief returns a new application base.
2889 TSyncAppBase *TDummyServerEngineInterface::newSyncAppBase(void)
2890 {
2891   // For not really engine based targets, the appbase factory function is
2892   // a global routine (for real engine targets, it is a true virtual of
2893   // the engineInterface, implemented in the target's leaf engineInterface derivate.
2894   // - for now, use the global appBase creator routine
2895   return sysync::newSyncAppBase(); // use global factory function 
2896 } // TDummyServerEngineInterface::newSyncAppBase
2897
2898 #else // old style
2899
2900 // Real server engine support
2901
2902 /// @brief Executes next step of the session
2903 /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
2904 ///        - tells caller to send or receive data or end the session etc.
2905 ///        - instructs engine to abort or time out the session etc.
2906 /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
2907 /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
2908 TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
2909 {
2910   uInt16 stepCmdIn = aStepCmd;
2911   localstatus sta = LOCERR_WRONGUSAGE;
2912
2913   // init default response
2914   aStepCmd = STEPCMD_ERROR; // error
2915   if (aInfoP) {
2916     aInfoP->eventtype=PEV_NOP;
2917     aInfoP->targetID=0;
2918     aInfoP->extra1=0;
2919     aInfoP->extra2=0;
2920     aInfoP->extra3=0;
2921   }
2922
2923   // if session is already aborted, no more steps are required
2924   if (isAborted()) {
2925         fServerEngineState = ses_done; // we are done
2926   }
2927
2928   // handle pre-processed step command according to current engine state
2929   switch (fServerEngineState) {
2930
2931     // Done state
2932     case ses_done :
2933       // session done, nothing happens any more
2934       aStepCmd = STEPCMD_DONE;
2935       sta = LOCERR_OK;
2936       break;
2937
2938     // Waiting for SyncML request data
2939     case ses_needdata:
2940       switch (stepCmdIn) {
2941         case STEPCMD_GOTDATA : {
2942                 // got data, check content type
2943           MemPtr_t data = NULL;
2944           smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &fRequestSize); // get request size      
2945           SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, fRequestSize);
2946                                         if (getEncoding()==SML_UNDEF) {
2947                 // no encoding known so far - use what we found from looking at data
2948             PDEBUGPRINTFX(DBG_ERROR,(
2949                 "Incoming data had no or invalid content type, Determined encoding by looking at data: %s",
2950               SyncMLEncodingNames[enc]
2951             ));
2952             setEncoding(enc);
2953           }
2954           else if (getEncoding()!=enc) {
2955                 // already known encoding does not match actual encoding
2956             PDEBUGPRINTFX(DBG_ERROR,(
2957                 "Warning: Incoming data encoding mismatch: expected=%s, found=%s",
2958               SyncMLEncodingNames[getEncoding()],
2959               SyncMLEncodingNames[enc]
2960             ));
2961           }
2962           if (getEncoding()==SML_UNDEF) {
2963                 // if session encoding is still unknown at this point, reject data as non-SyncML
2964             PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML"));
2965             sta = LOCERR_BADCONTENT; // bad content type
2966                                     aStepCmd = STEPCMD_ERROR;
2967             // Note: we do not abort the session here - app could have a retry strategy and re-enter
2968             //       this step with better data
2969             break;            
2970           }
2971           // content type ok - switch to processing mode
2972           fServerEngineState = ses_processing;
2973           aStepCmd = STEPCMD_OK;
2974           sta = LOCERR_OK;
2975           break;
2976         }
2977       } // switch stepCmdIn for ses_needdata
2978       break;
2979
2980     // Waiting until SyncML answer data is sent
2981     // (only when session needs to continue, otherwise we are in ses_done)
2982     case ses_dataready:
2983       switch (stepCmdIn) {
2984         case STEPCMD_SENTDATA :
2985           // sent data, now wait for next request
2986           fServerEngineState = ses_needdata;
2987           aStepCmd = STEPCMD_NEEDDATA;
2988           sta = LOCERR_OK;
2989           break;
2990       } // switch stepCmdIn for ses_dataready
2991       break;
2992
2993
2994     // Ready for generation steps
2995     case ses_generating:
2996       switch (stepCmdIn) {
2997         case STEPCMD_STEP :
2998           sta = ServerGeneratingStep(aStepCmd,aInfoP);
2999           break;
3000       } // switch stepCmdIn for ses_generating
3001       break;
3002
3003     // Ready for processing steps
3004     case ses_processing:
3005       switch (stepCmdIn) {
3006         case STEPCMD_STEP :
3007           sta = ServerProcessingStep(aStepCmd,aInfoP);
3008           break;
3009       } // switch stepCmdIn for ses_processing
3010       break;
3011
3012   case numServerEngineStates:
3013       // invalid
3014       break;
3015
3016   } // switch fServerEngineState
3017
3018   // done
3019   return sta;
3020 } // TSyncAgent::ServerSessionStep
3021
3022
3023
3024
3025 // Step that processes SyncML request data
3026 TSyError TSyncAgent::ServerProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3027 {
3028   localstatus sta = LOCERR_WRONGUSAGE;
3029   InstanceID_t myInstance = getSmlWorkspaceID();
3030   Ret_t rc;
3031
3032   // now process next command
3033   PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)"));
3034   #ifdef SYDEBUG
3035   MemPtr_t data = NULL;
3036   MemSize_t datasize;
3037   smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3038   #endif  
3039   rc=smlProcessData(
3040     myInstance,
3041     SML_NEXT_COMMAND
3042   );
3043   if (rc==SML_ERR_CONTINUE) {
3044     // processed ok, but message not completely processed yet
3045     // - engine state remains as is
3046     aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step
3047     sta = LOCERR_OK;
3048   }
3049   else if (rc==SML_ERR_OK) {
3050     // message completely processed
3051     // - switch engine state to generating answer message (if any)
3052     aStepCmd = STEPCMD_OK;
3053     fServerEngineState = ses_generating;
3054     sta = LOCERR_OK;
3055   }
3056   else if (rc==LOCERR_RETRYMSG) {
3057                 // server has detected that this message is a retry - report this to the app such that app can
3058     // first discard the instance buffer (consume everything in it)
3059     if (smlLockReadBuffer(myInstance,&data,&datasize)==SML_ERR_OK)
3060       smlUnlockReadBuffer(myInstance,datasize);
3061     // indicate that transport must resend the previous response
3062     PDEBUGPRINTFX(DBG_ERROR,(
3063       "Incoming message was identified as a retry - report STEPCMD_RESENDDATA - caller must resent last response"
3064     ));
3065     aStepCmd = STEPCMD_RESENDDATA;
3066     fServerEngineState = ses_dataready;
3067         }
3068         else {
3069     // processing failed
3070     PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc));
3071     // dump the message that failed to process
3072     #ifdef SYDEBUG
3073     if (data) DumpSyncMLBuffer(data,datasize,false,rc);
3074     #endif    
3075     // abort the session (causing proper error events to be generated and reported back)
3076     AbortSession(LOCERR_PROCESSMSG, true);
3077     // session is now done
3078     fServerEngineState = ses_done;
3079     // step by itself is ok - let app continue stepping (to restart session or complete abort)
3080     aStepCmd = STEPCMD_OK;
3081     sta = LOCERR_OK;
3082   }
3083   // done
3084   return sta;
3085 } // TSyncAgent::ServerProcessingStep
3086
3087
3088
3089 // Step that generates (rest of) SyncML answer data at end of request
3090 TSyError TSyncAgent::ServerGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3091 {
3092   bool done, hasdata;
3093   string respURI;
3094
3095         // finish request
3096         done = EndRequest(hasdata, respURI, fRequestSize);
3097   // check different exit points
3098   if (hasdata) {
3099         // there is data to be sent
3100     aStepCmd = STEPCMD_SENDDATA;
3101     fServerEngineState = ses_dataready;
3102   }
3103   else {
3104         // no more data to send
3105         aStepCmd = STEPCMD_OK; // need one more step to finish
3106   }
3107   // in any case, if done, all susequent steps will return STEPCMD_DONE
3108   if (done) {
3109         // Session is done
3110         TerminateSession();
3111     // subsequent steps will all return STEPCMD_DONE
3112         fServerEngineState = ses_done;
3113   }
3114   // request reset
3115   fRequestSize = 0;
3116
3117   // finished generating outgoing message
3118   // - make sure read pointer is set (advanced in case incoming
3119   //   message had trailing garbage) to beginning of generated
3120   //   answer. With incoming message being clean SyncML without
3121   //   garbage, this call is not needed, however with garbage
3122   //   it is important because otherwise outgoing message
3123   //   would have that garbage inserted before actual message
3124   //   start.
3125   smlReadOutgoingAgain(getSmlWorkspaceID());
3126
3127   // return status
3128   return LOCERR_OK;
3129 } // TSyncAgent::ServerGeneratingStep
3130
3131 #endif // ENGINE_LIBRARY
3132
3133 #endif // SYSYNC_SERVER
3134
3135
3136 #ifdef SYSYNC_CLIENT
3137
3138 #ifdef ENGINE_LIBRARY
3139
3140 // Client implementation
3141 // ---------------------
3142
3143 /// @brief Executes next step of the session
3144 /// @param aStepCmd[in/out] step command (STEPCMD_xxx):
3145 ///        - tells caller to send or receive data or end the session etc.
3146 ///        - instructs engine to suspend or abort the session etc.
3147 /// @param aInfoP[in] pointer to a TEngineProgressInfo structure, NULL if no progress info needed
3148 /// @return LOCERR_OK on success, SyncML or LOCERR_xxx error code on failure
3149 TSyError TSyncAgent::ClientSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3150 {
3151   uInt16 stepCmdIn = aStepCmd;
3152   localstatus sta = LOCERR_WRONGUSAGE;
3153
3154   // init default response
3155   aStepCmd = STEPCMD_ERROR; // error
3156   if (aInfoP) {
3157     aInfoP->eventtype=PEV_NOP;
3158     aInfoP->targetID=0;
3159     aInfoP->extra1=0;
3160     aInfoP->extra2=0;
3161     aInfoP->extra3=0;
3162   }
3163
3164   // if session is already aborted, no more steps are required
3165   if (isAborted()) {
3166         fClientEngineState = ces_done; // we are done
3167   }
3168
3169   // handle pre-processed step command according to current engine state
3170   switch (fClientEngineState) {
3171
3172     // Idle state
3173     case ces_done : {
3174       // session done, nothing happens any more
3175       aStepCmd = STEPCMD_DONE;
3176       sta = LOCERR_OK;
3177       break;
3178     }
3179
3180     case ces_idle : {
3181       // in idle, we can only start a session
3182       switch (stepCmdIn) {
3183         case STEPCMD_CLIENTSTART:
3184         case STEPCMD_CLIENTAUTOSTART:
3185           // initialize a new session
3186           sta = InitializeSession(fProfileSelectorInternal,stepCmdIn==STEPCMD_CLIENTAUTOSTART);
3187           if (sta!=LOCERR_OK) break;
3188           // engine is now ready, start generating first request
3189           fClientEngineState = ces_generating;
3190           // ok with no status
3191           aStepCmd = STEPCMD_OK;
3192           break;
3193       } // switch stepCmdIn for ces_idle
3194       break;
3195     }
3196
3197     // Ready for generation steps
3198     case ces_generating: {
3199       switch (stepCmdIn) {
3200         case STEPCMD_STEP :
3201           sta = ClientGeneratingStep(aStepCmd,aInfoP);
3202           break;
3203       } // switch stepCmdIn for ces_generating
3204       break;
3205     }
3206
3207     // Ready for processing steps
3208     case ces_processing: {
3209       switch (stepCmdIn) {
3210         case STEPCMD_STEP :
3211           sta = ClientProcessingStep(aStepCmd,aInfoP);
3212           break;
3213       } // switch stepCmdIn for ces_processing
3214       break;
3215     }
3216
3217     // Waiting for SyncML data
3218     case ces_needdata: {
3219       switch (stepCmdIn) {
3220         case STEPCMD_GOTDATA : {
3221           // got data, now start processing it
3222           OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_recvend,NULL,0,0,0);
3223           // check content type now
3224           MemPtr_t data = NULL;
3225           MemSize_t datasize;
3226           smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3227           // check content type
3228           SmlEncoding_t enc = TSyncAppBase::encodingFromData(data, datasize);
3229           if (enc!=getEncoding()) {
3230             PDEBUGPRINTFX(DBG_ERROR,("Incoming data is not SyncML"));
3231             sta = LOCERR_BADCONTENT; // bad content type
3232             #ifdef SYDEBUG
3233             if (data) DumpSyncMLBuffer(data,datasize,false,SML_ERR_UNSPECIFIC);
3234             #endif    
3235             // abort the session (causing proper error events to be generated and reported back)
3236             AbortSession(sta, true);
3237             // session is now done
3238             fClientEngineState = ces_done;
3239                                     aStepCmd = STEPCMD_ERROR;
3240             break;
3241           }
3242           // content type ok - switch to processing mode
3243           fIgnoreMsgErrs=false; // do not ignore errors by default
3244           fClientEngineState = ces_processing;
3245           aStepCmd = STEPCMD_OK;
3246           sta = LOCERR_OK;
3247           break;
3248         }
3249         case STEPCMD_RESENDDATA :
3250                 // instead of having received new data, the network layer has found it needs to re-send the data.
3251           // performing the STEPCMD_RESENDDATA just generates a new send start event, but otherwise no engine action
3252           fClientEngineState = ces_resending;
3253           aStepCmd = STEPCMD_RESENDDATA; // return the same step command, to differentiate it from STEPCMD_SENDDATA
3254           OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0);
3255           sta = LOCERR_OK;
3256           break;
3257       } // switch stepCmdIn for ces_needdata
3258       break;
3259     }
3260
3261     // Waiting until SyncML data is sent
3262     case ces_dataready:
3263     case ces_resending: {
3264       switch (stepCmdIn) {
3265         case STEPCMD_SENTDATA :
3266                 // allowed in dataready or resending state
3267           // sent (or re-sent) data, now request answer data
3268           OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendend,NULL,0,0,0);
3269           fClientEngineState = ces_needdata;
3270           aStepCmd = STEPCMD_NEEDDATA;
3271           sta = LOCERR_OK;
3272           break;
3273       } // switch stepCmdIn for ces_dataready
3274       break;
3275     }
3276
3277         case numClientEngineStates: {
3278       // invalid
3279       break;
3280     }
3281
3282   } // switch fClientEngineState
3283
3284   // done
3285   return sta;
3286 } // TSyncAgent::ClientSessionStep
3287
3288
3289
3290 // Step that generates SyncML data
3291 TSyError TSyncAgent::ClientGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3292 {
3293   localstatus sta = LOCERR_WRONGUSAGE;
3294   bool done;
3295
3296   //%%% at this time, generate next message in one step
3297   sta = NextMessage(done);
3298   if (done) {
3299     // done with session, with or without error
3300     fClientEngineState = ces_done; // blocks any further activity with the session
3301     aStepCmd = STEPCMD_DONE;
3302     // terminate session to provoke all end-of-session progress events
3303     TerminateSession();
3304   }
3305   else if (sta==LOCERR_OK) {
3306         // finished generating outgoing message
3307     // - make sure read pointer is set (advanced in case incoming
3308     //   message had trailing garbage) to beginning of generated
3309     //   answer. With incoming message being clean SyncML without
3310     //   garbage, this call is not needed, however with garbage
3311     //   it is important because otherwise outgoing message
3312     //   would have that garbage inserted before actual message
3313     //   start.
3314     smlReadOutgoingAgain(getSmlWorkspaceID());
3315     // next is sending request to server
3316     fClientEngineState = ces_dataready;
3317     aStepCmd = STEPCMD_SENDDATA;
3318     OBJ_PROGRESS_EVENT(getSyncAppBase(),pev_sendstart,NULL,0,0,0);
3319   }
3320   // return status
3321   return sta;
3322 } // TSyncAgent::ClientGeneratingStep
3323
3324
3325
3326 // Step that processes SyncML data
3327 TSyError TSyncAgent::ClientProcessingStep(uInt16 &aStepCmd, TEngineProgressInfo *aInfoP)
3328 {
3329   InstanceID_t myInstance = getSmlWorkspaceID();
3330   Ret_t rc;
3331   localstatus sta = LOCERR_WRONGUSAGE;
3332
3333   // now process next command
3334   PDEBUGPRINTFX(DBG_EXOTIC,("Calling smlProcessData(NEXT_COMMAND)"));
3335   #ifdef SYDEBUG
3336   MemPtr_t data = NULL;
3337   MemSize_t datasize;
3338   smlPeekMessageBuffer(getSmlWorkspaceID(), false, &data, &datasize);
3339   #endif  
3340   rc=smlProcessData(
3341     myInstance,
3342     SML_NEXT_COMMAND
3343   );
3344   if (rc==SML_ERR_CONTINUE) {
3345     // processed ok, but message not completely processed yet
3346     // - engine state remains as is
3347     aStepCmd = STEPCMD_OK; // ok w/o progress %%% for now, progress is delivered via queue in next step
3348     sta = LOCERR_OK;
3349   }
3350   else if (rc==SML_ERR_OK) {
3351     // message completely processed
3352     // - switch engine state to generating next message (if any)
3353     aStepCmd = STEPCMD_OK;
3354     fClientEngineState = ces_generating;
3355     sta = LOCERR_OK;
3356   }
3357   else {
3358     // processing failed
3359     PDEBUGPRINTFX(DBG_ERROR,("===> smlProcessData failed, returned 0x%hX",(sInt16)rc));
3360     // dump the message that failed to process
3361     #ifdef SYDEBUG
3362     if (data) DumpSyncMLBuffer(data,datasize,false,rc);
3363     #endif    
3364     if (!fIgnoreMsgErrs) {
3365             // abort the session (causing proper error events to be generated and reported back)
3366         AbortSession(LOCERR_PROCESSMSG, true);
3367       // session is now done
3368       fClientEngineState = ces_done;
3369     }
3370     else {
3371         // we must ignore errors e.g. because of session restart and go back to generate next message
3372       fClientEngineState = ces_generating;
3373     }
3374     // anyway, step by itself is ok - let app continue stepping (to restart session or complete abort)
3375     aStepCmd = STEPCMD_OK;
3376     sta = LOCERR_OK;
3377   }
3378   // now check if this is a session restart
3379   if (sta==LOCERR_OK && isStarting()) {
3380     // this is still the beginning of a session, which means
3381     // that we are restarting the session and caller should close
3382     // possibly open communication with the server before sending the next message
3383     aStepCmd = STEPCMD_RESTART;
3384   }
3385   // done
3386   return sta;
3387 } // TSyncAgent::ClientProcessingStep
3388
3389 #endif // ENGINE_LIBRARY
3390
3391 #endif // SYSYNC_CLIENT
3392
3393
3394
3395 // Session runtime settings key
3396 // ---------------------------
3397
3398 // Constructor
3399 TAgentParamsKey::TAgentParamsKey(TEngineInterface *aEngineInterfaceP, TSyncAgent *aAgentP) :
3400   inherited(aEngineInterfaceP,aAgentP),
3401   fAgentP(aAgentP)
3402 {
3403 } // TAgentParamsKey::TAgentParamsKey
3404
3405
3406
3407 // - read local session ID
3408 static TSyError readLocalSessionID(
3409   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3410   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3411 )
3412 {
3413   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3414   return TStructFieldsKey::returnString(
3415     mykeyP->fAgentP->getLocalSessionID(),
3416     aBuffer,aBufSize,aValSize
3417   );
3418 } // readLocalSessionID
3419
3420
3421 // - read initial local URI
3422 static TSyError readInitialLocalURI(
3423   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3424   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3425 )
3426 {
3427   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3428   return TStructFieldsKey::returnString(
3429     mykeyP->fAgentP->getInitialLocalURI(),
3430     aBuffer,aBufSize,aValSize
3431   );
3432 } // readInitialLocalURI
3433
3434
3435 // - read abort status
3436 static TSyError readAbortStatus(
3437   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3438   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3439 )
3440 {
3441   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3442   return TStructFieldsKey::returnInt(
3443         mykeyP->fAgentP->getAbortReasonStatus(),
3444     sizeof(TSyError),
3445     aBuffer,aBufSize,aValSize
3446   );
3447 } // readAbortStatus
3448
3449
3450
3451 // - write abort status, which means aborting a session
3452 TSyError writeAbortStatus(
3453   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3454   cAppPointer aBuffer, memSize aValSize
3455 )
3456 {
3457   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3458   // abort the session
3459   TSyError sta = *((TSyError *)aBuffer);
3460         mykeyP->fAgentP->AbortSession(sta, true);
3461   return LOCERR_OK;
3462 } // writeAbortStatus
3463
3464
3465
3466 // - read content type string
3467 static TSyError readContentType(
3468   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3469   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3470 )
3471 {
3472   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3473   string contentType = SYNCML_MIME_TYPE;
3474   mykeyP->fAgentP->addEncoding(contentType);
3475   return TStructFieldsKey::returnString(
3476     contentType.c_str(),
3477     aBuffer,aBufSize,aValSize
3478   );
3479 } // readContentType
3480
3481
3482 // - write content type string
3483 static TSyError writeContentType(
3484   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3485   cAppPointer aBuffer, memSize aValSize
3486 )
3487 {
3488   string contentType((cAppCharP)aBuffer,aValSize);
3489   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3490   mykeyP->fAgentP->setEncoding(TSyncAppBase::encodingFromContentType(contentType.c_str()));
3491   return LOCERR_OK;
3492 } // writeContentType
3493
3494
3495 // - read connection URL
3496 static TSyError readConnectURI(
3497   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3498   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3499 )
3500 {
3501   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3502   return TStructFieldsKey::returnString(
3503     mykeyP->fAgentP->getSendURI(),
3504     aBuffer,aBufSize,aValSize
3505   );
3506 } // readConnectURI
3507
3508
3509 // - read host part of connection URL
3510 static TSyError readConnectHost(
3511   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3512   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3513 )
3514 {
3515   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3516   string host;
3517   splitURL(mykeyP->fAgentP->getSendURI(),NULL,&host,NULL,NULL,NULL);
3518   return TStructFieldsKey::returnString(
3519     host.c_str(),
3520     aBuffer,aBufSize,aValSize
3521   );
3522 } // readConnectHost
3523
3524
3525 // - read document part of connection URL
3526 static TSyError readConnectDoc(
3527   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3528   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3529 )
3530 {
3531   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3532   string doc;
3533   splitURL(mykeyP->fAgentP->getSendURI(),NULL,NULL,&doc,NULL,NULL);
3534   return TStructFieldsKey::returnString(
3535     doc.c_str(),
3536     aBuffer,aBufSize,aValSize
3537   );
3538 } // readConnectDoc
3539
3540
3541 // - time when session was last used
3542 static TSyError readLastUsed(
3543   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3544   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3545 )
3546 {
3547   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3548   // return it
3549   return TStructFieldsKey::returnLineartime(mykeyP->fAgentP->getSessionLastUsed(), aBuffer, aBufSize, aValSize);
3550 } // readLastUsed
3551
3552
3553 // - server only: check session timeout
3554 static TSyError readTimedOut(
3555   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3556   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3557 )
3558 {
3559   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3560         // check if session has timed out
3561   bool timedout = mykeyP->fAgentP->getSessionLastUsed()+mykeyP->fAgentP->getSessionConfig()->getSessionTimeout() < mykeyP->fAgentP->getSystemNowAs(TCTX_UTC);
3562   // return it
3563   return TStructFieldsKey::returnInt(timedout, sizeof(bool), aBuffer, aBufSize, aValSize);
3564 } // readTimedOut
3565
3566
3567 #ifdef SYSYNC_SERVER
3568
3569 // - server only: read respURI enable flag
3570 static TSyError readSendRespURI(
3571   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3572   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3573 )
3574 {
3575   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3576   return TStructFieldsKey::returnInt(mykeyP->fAgentP->fUseRespURI, sizeof(bool), aBuffer, aBufSize, aValSize);
3577 } // readSendRespURI
3578
3579
3580 // - write respURI enable flag
3581 static TSyError writeSendRespURI(
3582   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3583   cAppPointer aBuffer, memSize aValSize
3584 )
3585 {
3586   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3587         mykeyP->fAgentP->fUseRespURI = *((uInt8P)aBuffer);
3588   return LOCERR_OK;
3589 } // writeSendRespURI
3590
3591
3592 #endif // SYSYNC_SERVER
3593
3594
3595 #ifdef SYSYNC_CLIENT
3596
3597 // - write (volatile, write-only) password for running this session
3598 // (for cases where we don't want to rely on binfile storage for sensitive password data)
3599 TSyError writeSessionPassword(
3600   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3601   cAppPointer aBuffer, memSize aValSize
3602 )
3603 {
3604   TAgentParamsKey *mykeyP = static_cast<TAgentParamsKey *>(aStructFieldsKeyP);
3605         mykeyP->fAgentP->setServerPassword((cAppCharP)aBuffer, aValSize);
3606   return LOCERR_OK;
3607 } // writeSessionPassword
3608
3609
3610 #ifdef ENGINE_LIBRARY
3611 // - read display alert
3612 static TSyError readDisplayAlert(
3613   TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP,
3614   appPointer aBuffer, memSize aBufSize, memSize &aValSize
3615 )
3616 {
3617   TClientEngineInterface *clientEngineP =
3618     static_cast<TClientEngineInterface *>(aStructFieldsKeyP->getEngineInterface());
3619   return TStructFieldsKey::returnString(
3620     clientEngineP->fAlertMessage.c_str(),
3621     aBuffer,aBufSize,aValSize
3622   );
3623 } // readDisplayAlert
3624 #endif
3625
3626 #endif // SYSYNC_CLIENT
3627
3628
3629 // accessor table for server session key
3630 static const TStructFieldInfo ServerParamFieldInfos[] =
3631 {  
3632   // valName, valType, writable, fieldOffs, valSiz
3633   { "localSessionID", VALTYPE_TEXT, false, 0, 0, &readLocalSessionID, NULL },
3634   { "initialLocalURI", VALTYPE_TEXT, false, 0, 0, &readInitialLocalURI, NULL },
3635   { "abortStatus", VALTYPE_INT16, true, 0, 0, &readAbortStatus, &writeAbortStatus },
3636   { "contenttype", VALTYPE_TEXT, true, 0, 0, &readContentType, &writeContentType },
3637   { "connectURI", VALTYPE_TEXT, false, 0, 0, &readConnectURI, NULL },
3638   { "connectHost", VALTYPE_TEXT, false, 0, 0, &readConnectHost, NULL },
3639   { "connectDoc", VALTYPE_TEXT, false, 0, 0, &readConnectDoc, NULL },
3640   { "timedout", VALTYPE_INT8, false, 0, 0, &readTimedOut, NULL },
3641   { "lastused", VALTYPE_TIME64, false, 0, 0, &readLastUsed, NULL },
3642   #ifdef SYSYNC_SERVER
3643   { "sendrespuri", VALTYPE_INT8, true, 0, 0, &readSendRespURI, &writeSendRespURI },
3644   #endif
3645   #ifdef SYSYNC_CLIENT
3646   { "sessionPassword", VALTYPE_TEXT, true, 0, 0, NULL, &writeSessionPassword },
3647   #ifdef ENGINE_LIBRARY
3648   { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL },
3649   #endif
3650   #endif
3651 };
3652
3653 // get table describing the fields in the struct
3654 const TStructFieldInfo *TAgentParamsKey::getFieldsTable(void)
3655 {
3656   return ServerParamFieldInfos;
3657 } // TAgentParamsKey::getFieldsTable
3658
3659 sInt32 TAgentParamsKey::numFields(void)
3660 {
3661   return sizeof(ServerParamFieldInfos)/sizeof(TStructFieldInfo);
3662 } // TAgentParamsKey::numFields
3663
3664 // get actual struct base address
3665 uInt8P TAgentParamsKey::getStructAddr(void)
3666 {
3667   // prepared for accessing fields in server session object
3668   return (uInt8P)fAgentP;
3669 } // TAgentParamsKey::getStructAddr
3670
3671
3672 // open subkey by name (not by path!)
3673 TSyError TAgentParamsKey::OpenSubKeyByName(
3674   TSettingsKeyImpl *&aSettingsKeyP,
3675   cAppCharP aName, stringSize aNameSize,
3676   uInt16 aMode
3677 ) {
3678   #ifdef DBAPI_TUNNEL_SUPPORT
3679   if (strucmp(aName,"tunnel",aNameSize)==0) {
3680     // get tunnel datastore pointer
3681     TLocalEngineDS *ds = fAgentP->getTunnelDS();
3682     if (!ds) return LOCERR_WRONGUSAGE;
3683     // opens current session's tunnel key
3684     aSettingsKeyP = ds->newTunnelKey(fEngineInterfaceP);
3685   }
3686   else
3687   #endif
3688     return inherited::OpenSubKeyByName(aSettingsKeyP,aName,aNameSize,aMode);
3689   // opened a key
3690   return LOCERR_OK;
3691 } // TAgentParamsKey::OpenSubKeyByName
3692
3693
3694 #endif // ENGINEINTERFACE_SUPPORT
3695
3696 } // namespace sysync
3697
3698 // eof